SpMobil.java
/*
* Copyright (C) 2000 - 2025 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "https://www.silverpeas.org/legal/floss_exception.html"
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.silverpeas.mobile.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.*;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.autobean.shared.AutoBean;
import org.silverpeas.mobile.client.apps.agenda.AgendaApp;
import org.silverpeas.mobile.client.apps.blog.BlogApp;
import org.silverpeas.mobile.client.apps.classifieds.ClassifiedsApp;
import org.silverpeas.mobile.client.apps.contacts.ContactsApp;
import org.silverpeas.mobile.client.apps.documents.DocumentsApp;
import org.silverpeas.mobile.client.apps.faq.FaqApp;
import org.silverpeas.mobile.client.apps.favorites.FavoritesApp;
import org.silverpeas.mobile.client.apps.formsonline.FormsOnlineApp;
import org.silverpeas.mobile.client.apps.hyperlink.HyperLinkApp;
import org.silverpeas.mobile.client.apps.media.MediaApp;
import org.silverpeas.mobile.client.apps.navigation.NavigationApp;
import org.silverpeas.mobile.client.apps.navigation.events.pages.HomePageLoadedEvent;
import org.silverpeas.mobile.client.apps.news.NewsApp;
import org.silverpeas.mobile.client.apps.notificationsbox.NotificationsBoxApp;
import org.silverpeas.mobile.client.apps.orgchartgroup.OrgChartGroupApp;
import org.silverpeas.mobile.client.apps.resourcesManager.ResourcesManagerApp;
import org.silverpeas.mobile.client.apps.sharesbox.SharesBoxApp;
import org.silverpeas.mobile.client.apps.survey.SurveyApp;
import org.silverpeas.mobile.client.apps.tasks.TasksApp;
import org.silverpeas.mobile.client.apps.webpage.WebPageApp;
import org.silverpeas.mobile.client.apps.workflow.WorkflowApp;
import org.silverpeas.mobile.client.common.*;
import org.silverpeas.mobile.client.common.app.App;
import org.silverpeas.mobile.client.common.auth.BiometricAuthHelper;
import org.silverpeas.mobile.client.common.auth.resources.BiometricMessages;
import org.silverpeas.mobile.client.common.event.ErrorEvent;
import org.silverpeas.mobile.client.common.event.ExceptionEvent;
import org.silverpeas.mobile.client.common.event.authentication.AbstractAuthenticationErrorEvent;
import org.silverpeas.mobile.client.common.event.authentication.AuthenticationEventHandler;
import org.silverpeas.mobile.client.common.mobil.MobilUtils;
import org.silverpeas.mobile.client.common.mobil.Orientation;
import org.silverpeas.mobile.client.common.navigation.LinksManager;
import org.silverpeas.mobile.client.common.navigation.PageHistory;
import org.silverpeas.mobile.client.common.network.NetworkHelper;
import org.silverpeas.mobile.client.common.network.rest.RestMethod;
import org.silverpeas.mobile.client.common.network.rest.RestMethodCallbackOnlineOnly;
import org.silverpeas.mobile.client.common.resources.ResourcesManager;
import org.silverpeas.mobile.client.common.storage.CacheStorageHelper;
import org.silverpeas.mobile.client.common.storage.LocalStorageHelper;
import org.silverpeas.mobile.client.components.Snackbar;
import org.silverpeas.mobile.client.components.base.Page;
import org.silverpeas.mobile.client.components.base.events.window.OrientationChangeEvent;
import org.silverpeas.mobile.client.pages.connexion.ConnexionPage;
import org.silverpeas.mobile.client.pages.cookies.CookiesPage;
import org.silverpeas.mobile.client.pages.main.HomePage;
import org.silverpeas.mobile.client.pages.search.SearchResultPage;
import org.silverpeas.mobile.client.pages.termsofservice.TermsOfServicePage;
import org.silverpeas.mobile.client.resources.ApplicationMessages;
import org.silverpeas.mobile.shared.dto.DetailUserDTO;
import org.silverpeas.mobile.shared.dto.FullUserDTO;
import org.silverpeas.mobile.shared.dto.HomePageDTO;
import org.silverpeas.mobile.shared.dto.ShortCutLinkDTO;
import org.silverpeas.mobile.shared.dto.authentication.IUserProfile;
import org.silverpeas.mobile.shared.dto.authentication.UserProfileDTO;
import org.silverpeas.mobile.shared.dto.configuration.Config;
import org.silverpeas.mobile.shared.dto.configuration.IConfig;
import org.silverpeas.mobile.shared.dto.navigation.ApplicationInstanceDTO;
import org.silverpeas.mobile.shared.dto.search.ResultDTO;
import org.silverpeas.mobile.shared.exceptions.AuthenticationException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class SpMobil implements EntryPoint, AuthenticationEventHandler {
private static Page mainPage = null;
private static DetailUserDTO user;
private static UserProfileDTO userProfile;
private static int nbRetryLogin = 0;
private static String viewport, bodyClass, bodyId;
private static ApplicationMessages msg;
private static String shortcutAppId;
private static String shortcutContentType;
private static String shortcutContentId;
private static String shortcutContributionId;
private static String shortcutRole;
private static String token;
private static String XSession;
private static SpMobil instance = null;
private static boolean SSO = false;
private static Orientation orientation = null;
private static List<App> apps = new ArrayList<App>();
public static DetailUserDTO getUser() {
return user;
}
public static void setUserProfile(final UserProfileDTO userProfile) {
SpMobil.userProfile = userProfile;
}
public static UserProfileDTO getUserProfile() {
return userProfile;
}
public static void setUser(final DetailUserDTO user, boolean displayPersonnalApps) {
SpMobil.user = user;
if(displayPersonnalApps) displayPersonnalApps();
}
/**
* Init. spmobile.
*/
public void onModuleLoad() {
msg = GWT.create(ApplicationMessages.class);
exportNativeFunctions();
checkVersion();
checkInstallation();
checkBiometrics();
// init connexion supervision
NetworkHelper.getInstance();
setFontSize(getConfiguration().getFontSize());
setFilter(getConfiguration());
instance = this;
shortcutAppId = Window.Location.getParameter("shortcutAppId");
shortcutContentType = Window.Location.getParameter("shortcutContentType");
shortcutContentId = Window.Location.getParameter("shortcutContentId");
shortcutContributionId = Window.Location.getParameter("shortcutContributionId");
shortcutRole = Window.Location.getParameter("shortcutRole");
NodeList<Element> node = Document.get().getElementsByTagName("meta");
for (int i = 0; i < node.getLength(); i++) {
if (node.getItem(i).getAttribute("name").equalsIgnoreCase("sp_token")) {
token = node.getItem(i).getAttribute("content");
} else if (node.getItem(i).getAttribute("name").equalsIgnoreCase("sp_session")) {
XSession = node.getItem(i).getAttribute("content");
}
}
if (token != null && !token.isEmpty()) {
AuthentificationManager.getInstance().addHeader("X-STKN", token);
}
if (XSession != null && !XSession.isEmpty()) {
AuthentificationManager.getInstance().addHeader("X-Silverpeas-Session", XSession);
}
if (token == null) {
token = AuthentificationManager.getInstance().getHeader("X-STKN");
}
EventBus.getInstance().addHandler(ExceptionEvent.TYPE, new ErrorManager());
EventBus.getInstance().addHandler(AbstractAuthenticationErrorEvent.TYPE, this);
SSO = !ResourcesManager.getSSOPath().isEmpty();
if (SSO) GWT.log("SSO is detected");
displayFirstPage();
NodeList<Element> tags = Document.get().getElementsByTagName("meta");
for (int i = 0; i < tags.getLength(); i++) {
MetaElement metaTag = ((MetaElement) tags.getItem(i));
if (metaTag.getName().equals("viewport")) {
viewport = metaTag.getContent();
}
}
orientation = MobilUtils.getOrientation();
Window.addResizeHandler(new ResizeHandler() {
@Override
public void onResize(final ResizeEvent resizeEvent) {
if (!MobilUtils.getOrientation().equals(orientation)) {
orientation = MobilUtils.getOrientation();
EventBus.getInstance().fireEvent(new OrientationChangeEvent(orientation));
}
}
});
// Instanciate apps
apps.add(new DocumentsApp());
apps.add(new MediaApp());
apps.add(new NewsApp());
apps.add(new NavigationApp());
apps.add(new TasksApp());
apps.add(new FavoritesApp());
apps.add(new WebPageApp());
apps.add(new BlogApp());
apps.add(new WorkflowApp());
apps.add(new HyperLinkApp());
apps.add(new AgendaApp());
apps.add(new FormsOnlineApp());
apps.add(new ContactsApp());
apps.add(new ClassifiedsApp());
apps.add(new SurveyApp());
apps.add(new NotificationsBoxApp());
apps.add(new SharesBoxApp());
apps.add(new FaqApp());
apps.add(new ResourcesManagerApp());
apps.add(new OrgChartGroupApp());
}
private static void checkBiometrics() {
boolean biometricsAuthEnable = Boolean.parseBoolean(ResourcesManager.getParam("authentification.biometrics", "false"));
if (biometricsAuthEnable) {
if (!BiometricAuthHelper.isSupported()) {
// Keep an existing local biometric lock; do not bypass it on an unsupported browser.
setBiometricLocked(BiometricAuthHelper.isRegistered());
BiometricMessages biometricMessages = GWT.create(BiometricMessages.class);
Window.alert(biometricMessages.unavailable());
return;
}
if (BiometricAuthHelper.isRegistered()) {
setBiometricLocked(true);
BiometricAuthHelper.loginLocal(success -> {
if (!success) {
BiometricMessages biometricMessages = GWT.create(BiometricMessages.class);
Window.alert(biometricMessages.authFailed());
return;
}
setBiometricLocked(false);
});
} else {
setBiometricLocked(false);
BiometricAuthHelper.register();
}
} else {
setBiometricLocked(false);
}
}
private static void setBiometricLocked(boolean locked) {
Element html = Document.get().getDocumentElement();
Element body = Document.get().getBody();
if (locked) {
html.setAttribute("data-biometric-locked", "");
// Inert blocks focus and excludes the protected content from assistive technology.
body.setAttribute("inert", "");
} else {
html.removeAttribute("data-biometric-locked");
body.removeAttribute("inert");
}
}
private static boolean installationOffered = false;
private static void checkInstallation() {
PWAHelper.addStateListener(() -> Scheduler.get().scheduleDeferred(() -> {
String cookie = Cookies.getCookie("ask_install");
if (!installationOffered && PWAHelper.canInstall() && !PWAHelper.isInstalledApp()
&& (cookie == null || cookie.isEmpty())) {
installationOffered = true;
Command decline = () -> {
long days = Long.parseLong(ResourcesManager.getParam("displayInstallQuestionFrequency", "30"));
Date expires = new Date(System.currentTimeMillis() + 1000L * 60 * 60 * 24 * days);
Cookies.setCookie("ask_install", "no", expires);
};
Snackbar.showConfirmation(msg.installation(), () -> PWAHelper.installApp(msg), decline, false);
}
}));
}
private static void checkVersion() {
String buildDate = ResourcesManager.getParam("build.date");
if (buildDate == null || buildDate.isEmpty()) {
return;
}
String lastBuild = LocalStorageHelper.getInstance().load("build.date");
if (!buildDate.equals(lastBuild)) {
if (lastBuild != null && !lastBuild.isEmpty()) {
AuthentificationManager.getInstance().clearCache();
}
// Record the new version so subsequent page changes do not clear the cache again.
LocalStorageHelper.getInstance().storeBuildDate();
}
}
public void displayFirstPage() {
checkVersion();
boolean displayCookiesInformation = Boolean.parseBoolean(ResourcesManager.getParam("displayCookiesInformation"));
String cookie = Cookies.getCookie("accept_cookies");
if (!SSO & displayCookiesInformation && (cookie == null || cookie.isEmpty())) {
displayCookiesPage();
} else {
loadIds(null);
}
}
public static Page getMainPage() {
if (mainPage == null) {
mainPage = new Page();
}
return mainPage;
}
public static String getUserToken() {
if (getUser() != null) {
return getUser().getToken();
} else if (userProfile != null) {
return userProfile.getApiToken();
} else {
return null;
}
}
public static SpMobil getInstance() {
return instance;
}
/**
* Auto login.
*
* @param user
* @param password
*/
private static void login(final FullUserDTO user, final String password, Command attempt) {
Notification.activityStart();
AuthentificationManager.getInstance()
.authenticateOnSilverpeas(user.getLogin(), password, user.getDomainId(), attempt);
}
public static void displayMainPage() {
SpMobil.getMainPage().showFooter();
if (!Window.Location.getHref().contains("?locale=") &&
!user.getLanguage().equalsIgnoreCase("fr")) {
Window.Location.replace(Window.Location.getHref() + "?locale=" + user.getLanguage());
}
getMainPage().setUser(user);
RootPanel.get().clear();
RootPanel.get().add(getMainPage());
PageHistory.getInstance().clear();
PageHistory.getInstance().goTo(new HomePage());
if ((shortcutContentType != null && shortcutContentId != null) ||
(shortcutAppId != null && shortcutContentType != null && shortcutContentId != null) ||
shortcutContributionId != null ||
(shortcutContentType != null && (shortcutContentType.equals("Component") ||
shortcutContentType.equals("Space")) && shortcutAppId != null)) {
ShortCutRouter.route(user, shortcutAppId, shortcutContentType, shortcutContentId,
shortcutContributionId, shortcutRole);
} else if (shortcutContentType != null && shortcutContentType.equalsIgnoreCase("Url") && shortcutAppId != null) {
LinksManager.openIframePage(shortcutAppId);
} else {
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<HomePageDTO>() {
@Override
public void attempt() {
ServicesLocator.getServiceNavigation().getHomePageData(null, SpMobil.getContentZoomLevel(), this);
}
@Override
public void onSuccess(final RestMethod method, final HomePageDTO result) {
super.onSuccess(method, result);
// send event to main home page
EventBus.getInstance().fireEvent(new HomePageLoadedEvent(result));
// caching for offline mode
for (ShortCutLinkDTO shortCut : result.getShortCuts()) {
CacheStorageHelper.store(shortCut.getIcon());
}
}
};
action.attempt();
}
}
private static void displayPersonnalApps () {
if (getUser() != null && !getMainPage().isPersonalAppsInitialized()) {
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<List<ApplicationInstanceDTO>>() {
@Override
public void attempt() {
ServicesLocator.getServiceNavigation().getPersonnalSpaceContent(getUser().getId(), this);
}
@Override
public void onSuccess(RestMethod method, List<ApplicationInstanceDTO> applicationInstanceDTOS) {
getMainPage().setPersonalApps(applicationInstanceDTOS);
}
};
action.attempt();
}
}
public static void displayTermsOfServicePage() {
RootPanel.get().clear();
RootPanel.get().add(getMainPage());
SpMobil.getMainPage().setContent(new TermsOfServicePage(), true);
}
public static void displayCookiesPage() {
RootPanel.get().clear();
RootPanel.get().add(getMainPage());
SpMobil.getMainPage().setContent(new CookiesPage(), true);
}
/**
* Load ids in SQL Web Storage.
*/
public void loadIds(Command attempt) {
FullUserDTO u = AuthentificationManager.getInstance().loadUser();
if (u != null) {
tryToRelogin(null);
} else {
String login = DOMUtils.getInputValueById("svpLogin");
String domainId = DOMUtils.getInputValueById("defaultDomain");
if (login != null && domainId != null && !login.isEmpty() && !domainId.isEmpty()) {
//SSO
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<DetailUserDTO>() {
@Override
public void attempt() {
super.attempt();
ServicesLocator.getServiceNavigation().getUser(login, domainId, this);
}
@Override
public void onFailure(RestMethod method, Throwable t) {
tabletGesture(false);
displayLoginPage(null);
}
@Override
public void onSuccess(RestMethod method, DetailUserDTO detailUserDTO) {
super.onSuccess(method, detailUserDTO);
setUser(detailUserDTO, true);
setUserProfile(UserProfileDTO.getBean(
LocalStorageHelper.getInstance().load(AuthentificationManager.USER_PROFIL, IUserProfile.class)));
if (getUserProfile() == null) {
UserProfileDTO p = new UserProfileDTO();
p.setFullName(detailUserDTO.getFirstName() + " " + detailUserDTO.getLastName());
p.setAvatar(detailUserDTO.getAvatar());
p.setFirstName(detailUserDTO.getFirstName());
p.setLastName(detailUserDTO.getLastName());
p.setLanguage(detailUserDTO.getLanguage());
p.setId(detailUserDTO.getId());
p.setEmailAddress(detailUserDTO.geteMail());
setUserProfile(p);
}
ServicesLocator.getServiceTermsOfService().show(new RestMethodCallbackOnlineOnly<String>() {
@Override
public void onFailure(final RestMethod method, final Throwable throwable) {
Notification.activityStop();
EventBus.getInstance().fireEvent(new ErrorEvent(throwable));
}
@Override
public void onSuccess(final RestMethod method, final String showTerms) {
if (Boolean.parseBoolean(showTerms)) {
SpMobil.displayTermsOfServicePage();
} else {
SpMobil.displayMainPage();
}
}
});
}
};
action.attempt();
} else {
//Login
tabletGesture(false);
displayLoginPage(null);
}
}
}
public static boolean isSSO() {
return SSO;
}
private void tryToRelogin(final Command attempt) {
checkVersion();
FullUserDTO user = AuthentificationManager.getInstance().loadUser();
if (user != null) {
String password = AuthentificationManager.getInstance().decryptPassword(user.getPassword());
if (password != null) {
login(user, password, attempt);
}
} else {
//Login
tabletGesture(false);
displayLoginPage(null);
}
}
public static void tabletGesture(boolean connected) {
if (MobilUtils.isTablet()) {
if (connected) {
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<Boolean>() {
@Override
public void attempt() {
super.attempt();
ServicesLocator.getServiceNavigation().setTabletMode(this);
}
@Override
public void onSuccess(final RestMethod method, final Boolean desktopMode) {
super.onSuccess(method, desktopMode);
if (desktopMode) {
String url = Window.Location.getHref();
url = url.substring(0, url.indexOf("silverpeas") + "silverpeas".length());
Window.Location.replace(url);
}
}
};
action.attempt();
} else {
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<Boolean>() {
@Override
public void attempt() {
super.attempt();
ServicesLocator.getServiceConnection().setTabletMode(this);
}
@Override
public void onSuccess(final RestMethod method, final Boolean desktopMode) {
super.onSuccess(method, desktopMode);
if (desktopMode) {
String url = Window.Location.getHref();
url = url.substring(0, url.indexOf("silverpeas") + "silverpeas".length());
Window.Location.replace(url);
}
}
};
action.attempt();
}
}
}
public static void displayLoginPage(AuthenticationException error) {
checkVersion();
ConnexionPage connexionPage = new ConnexionPage();
connexionPage.setAuthenticateError(error);
RootPanel.get().clear();
PageHistory.getInstance().clear();
RootPanel.get().add(connexionPage);
}
public static void search(final String query) {
Notification.activityStart();
RestMethodCallbackOnlineOnly action = new RestMethodCallbackOnlineOnly<List<ResultDTO>>() {
@Override
public void attempt() {
super.attempt();
ServicesLocator.getServiceSearch().search(query, this);
}
@Override
public void onSuccess(final RestMethod method, final List<ResultDTO> results) {
super.onSuccess(method, results);
getMainPage().resetSearchField();
getMainPage().closeMenu();
SearchResultPage page = new SearchResultPage();
page.setPageTitle(msg.results());
page.setResults(results);
page.show();
Notification.activityStop();
}
};
action.attempt();
}
public static void showFullScreen(final Widget content, final boolean zoomable, String bodyClass,
String bodyId) {
PageHistory.getInstance().gotoToFullScreen("viewer");
RootPanel.get().clear();
RootPanel.get().add(content);
if (zoomable) {
NodeList<Element> tags = Document.get().getElementsByTagName("meta");
for (int i = 0; i < tags.getLength(); i++) {
MetaElement metaTag = ((MetaElement) tags.getItem(i));
if (metaTag.getName().equals("viewport")) {
metaTag.setContent("");
}
}
}
SpMobil.bodyClass = Document.get().getBody().getClassName();
SpMobil.bodyId = Document.get().getBody().getId();
Document.get().getBody().setClassName(bodyClass);
Document.get().getBody().setId(bodyId);
Document.get().getBody().getStyle().setPaddingTop(0, Style.Unit.PX);
}
public static void restoreMainPage() {
RootPanel.get().clear();
RootPanel.get().add(SpMobil.getMainPage());
Document.get().getBody().setId(bodyId);
Document.get().getBody().setClassName(bodyClass);
Document.get().getBody().getStyle().clearPaddingTop();
NodeList<Element> tags = Document.get().getElementsByTagName("meta");
for (int i = 0; i < tags.getLength(); i++) {
MetaElement metaTag = ((MetaElement) tags.getItem(i));
if (metaTag.getName().equals("viewport")) {
metaTag.setContent(viewport);
}
}
}
public static void destroyMainPage() {
mainPage = null;
}
public static Config getConfiguration() {
AutoBean<IConfig> conf = LocalStorageHelper.getInstance().load("config", IConfig.class);
Config config = null;
if (conf == null) {
config = Config.getDefaultConfig();
} else {
config = Config.getBean(conf);
}
return config;
}
public static String getContentZoomLevel() {
double z = getConfiguration().getFontSize() / 10d;
return String.valueOf(z);
}
@Override
public void onAuthenticationError(final AbstractAuthenticationErrorEvent event) {
displayLoginPage(((AuthenticationException) event.getException()));
}
public static native void exportNativeFunctions()/*-{
$wnd.navigateTo = $entry(@org.silverpeas.mobile.client.common.navigation.LinksManager::navigateToPermalink(*));
}-*/;
public static void setFontSize(int value) {
Document.get().getBody().setAttribute("style", "font-size:"+value+"pt;");
}
public static void setFilter(Config configuration) {
if (configuration.isStandard()) {
Document.get().getElementsByTagName("html").getItem(0).setAttribute("style","");
} else if (configuration.isGrayscale()) {
Document.get().getElementsByTagName("html").getItem(0).setAttribute("style","filter:grayscale(1);");
} else if (configuration.isSepia()) {
Document.get().getElementsByTagName("html").getItem(0).setAttribute("style","filter:sepia(1);");
} else if (configuration.isInverse()) {
Document.get().getElementsByTagName("html").getItem(0).setAttribute("style","filter:invert(1);");
}
}
}