ozwillo / ozwillo-portal Goto Github PK
View Code? Open in Web Editor NEWOzwillo's user portal
Home Page: https://www.ozwillo.com
License: GNU Affero General Public License v3.0
Ozwillo's user portal
Home Page: https://www.ozwillo.com
License: GNU Affero General Public License v3.0
Automatic token renewal has been implemented in Citizen Kin. Reuse this implementation in the Portal (not the case presently).
Following up on #10 we have created instances and services. For now public services are visible in the app store and private services are automatically added to the manager's dashboard.
Implement a prototype of the App management view that lets IT Managers view all the instances their organizations have bought and manage them and their services (i.e. publish services on the store, subscribe org users to the app, …)
Right now the system allows for 64×64 PNG icons for dashboard, app store, my apps, etc. Ideally we should be able to handle variants, i.e. display a 32×32 icon in My Apps, use blown-up icons elsewhere, etc. That means we should provide means for the admin to enter / upload multiple icon variants (and the portal itself should be able to do downscaling at least).
Create a draft application store so it can be demoed and explained to project partners.
When connected as citizen (Alice) and going to the application store, Citizen Kin appears as "installed"
Create a simple back office to let administrators edit the content (and view missing translations) - ideally with a split-screen UI whereby content managers enter Markdown content and have a live rendition on a second pane.
The story:
Then the portal should display a specific error page ("Your application purchase could not be completed") rather than redirect to the generic error page ("Oops, there has been an error").
NB - obviously this doesn't catch errors after the app factory has returned and before it has acquitted the instance creation. That's another topic; at least this way we can handle errors from synchronous app factories (e.g. Citizen Kin and others where instantiation == add a row in a database).
Currently the appstore pages require authentication.
Related to #37 - after that is fixed, only the app store should be (optionally) unauthenticated.
When there are several icons, clicking "buy" on the last one doesn't reload the popup content.
In the fix for #39 we allow to set a distant URL for the service icon. We should also allow users to upload icons straight from the portal, which would then self-host the icons (in the service definition, you'd have Portal URLs for icons).
UI-wise we should make it explicit that we save either a "foreign" icon URL or a direct upload.
Technically it's not too hard, we only need a complementary method in IconService that allows to set an icon without downloading.
Icons are cached on first usage; but when the provided icon url is invalid, we shouldn't check every time, rather we should only check the remote service at most once every few minutes (parameterizable).
Following up on #10 - prototypes are well and good but an actual implementation is better.
See also pole-numerique/oasis-citizenkin#78
When we're out of the first sprint, take a few minutes to clean up the build:
With Citizen Kin as a first step, implement the draft protocol for creating live instances, assigning groups, and declaring services.
Also, use the kernel suggestion for setting the initial language, by having the kernel extend the login failure response.
@tbroyer quote :
Ideally, I think the Portal should initially do a login check with prompt=none so that if the user is already authenticated at the Kernel, it'll be automatically logged into the portal. We discussed it with @schambon and agreed it would be the best implementation, but it's not necessarily easy to implement so we were OK delaying it. The idea was to extend the error response with a locale parameter.
Currently it's only possible to enter one name, description, icon for a service, and the code does in fact push forcefully that one version into all language versions.
We must update the UI to allow multiple-language entering.
Currently the navbar doesn't behave correctly in lower window widths. Without necessarily going full-responsive there must be ways to degrade better down to 800 pixels wide or so.
Track and report server load, ideally in terms of peak session count / day; it seems that Spring Boot already provides peak threads which can be used as a proxy to start with.
With prompt=none
Catalogue entries provide icons:
Icons are provided in the form of URLs. The Portal should not make any assumption about these URLs beyond the fact that they point to a 64×64 PNG.
Create a service to fetch these icons, store them in a portal-local cache, and serve them to clients:
When you're logged to the portal and goes on the portal homepage, the sign out button says "sign out null"
Currently the service descriptions used to build the Dashboard are taken off the /m/search kernel endpoint.
That endpoint does not in fact return the service_uri - so use the relevant endpoints.
If there is a non-zero notification count for a Dashboard icon,
And the service associated with that icon has a non-null notification_uri
Then that icon's link should be set to the notification_uri.
it's possible to make a drag and drop of an icon from the principal dashboard to a another one, but it's not possible to change again
Work on issue-#18-userprofile branch
Tasks:
Locales (and time zones) are stored in the kernel profile, and returned by the userinfo endpoint (presumably under the "profile" scope).
Use that info to display text and date/times with the correct settings after login.
Work on issue-#20-network branch.
Currently the portal looks okay in IE8, with the exception of rounded borders (IE doesn't support border-radius).
A workaround can be to use CSS3 emulation libraries such as PIE (http://css3pie.com/documentation/getting-started/)
The TokenRefreshInterceptor works by throwing a specific RuntimeException that is caught by the OasisExceptionTranslationFilter - only PortalControl has a catch(Throwable t) (!) that causes the exception to be caught and the error page to be displayed, instead of a new token to be transparently negotiated.
Opening a session with john.doe I get stuck on
Dashboards can be created but cannot be either renamed or destroyed
Sign up button on homepage is disabled
When drag and dropping, make the drop zone be the interstitial zone between two icons.
Now that the home page / CMS is done with a separate Joomla and the portal is moved to portal.(domain), we must remove the home page and instead redirect to www.(domain).
Implement a first-level (request-scoped) cache at all data access points in the portal, so that multiple calls to the same data access service (either backed by Mongo, Kernel service, or Data core service) do not translate to as many calls to the backing repository.
This cache should be ephemeral (request-bound) and transparent to user code. Look into using Spring's @Cacheable annotations using a custom CacheManager that uses a request-scoped bean to provide the necessary functionality.
When I log to ozwillo-preprod portal and then let my session "sleep" for at least 1 hour (I have not measured exactly), when I come back to the portal homepage and click on "My Ozwillo" I've got an error:
HTTP Status 500 - Request processing failed; nested exception is java.lang.NullPointerException
type Exception report
message Request processing failed; nested exception is java.lang.NullPointerException
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
root cause
java.lang.NullPointerException
java.util.Objects.requireNonNull(Objects.java:203)
java.util.Arrays$ArrayList.<init>(Arrays.java:3807)
java.util.Arrays.asList(Arrays.java:3794)
org.oasis_eu.portal.core.dao.impl.SubscriptionStoreImpl.findByUserId(SubscriptionStoreImpl.java:39)
org.oasis_eu.portal.services.PortalDashboardService.getDashboardEntries(PortalDashboardService.java:113)
org.oasis_eu.portal.services.PortalDashboardService$$FastClassBySpringCGLIB$$39a53ebb.invoke(<generated>)
org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
org.oasis_eu.portal.services.PortalDashboardService$$EnhancerBySpringCGLIB$$b978d565.getDashboardEntries(<generated>)
org.oasis_eu.portal.front.my.MyOzwilloController.myOzwillo(MyOzwilloController.java:61)
sun.reflect.GeneratedMethodAccessor125.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:483)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.oasis_eu.spring.kernel.security.OasisExceptionTranslationFilter.doFilter(OasisExceptionTranslationFilter.java:41)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.oasis_eu.spring.kernel.security.OasisAuthenticationFilter.doFilter(OasisAuthenticationFilter.java:82)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.52 logs.
Apache Tomcat/7.0.52```
Add the Pôle Numérique-provided tracker JS to the main layout(s)
Currently embedding a content item in a portal page is very manual (one has to call the ContentRenderingService from the controller and pass the result back to Thymeleaf).
Create a "cms" Thymeleaf dialect that would take the call to ContentRenderingService out of Spring controllers. This way, to embed a content item in a page, one should only use cms:content="content_id" in a div.
For public pages, when there is no user logged in, use a URL path variable to decide on the language to display the page in.
E.g. : /{page_name}/{lang?}/{page_id}
so /press/en/aaa-bbb-ccc would display the page in English, while /presse/fr/aaa-bbb-ccc would display it in French (an additional advantage of that is to make it possible to have localized URLs: "press" vs "presse").
Add a status endpoint that pings the database before returning 200.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.