ServiceNavigation.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.server.services;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import org.apache.commons.lang3.EnumUtils;
import org.silverpeas.components.gallery.model.Media;
import org.silverpeas.components.gallery.model.MediaPK;
import org.silverpeas.components.gallery.service.MediaServiceProvider;
import org.silverpeas.components.quickinfo.model.News;
import org.silverpeas.core.admin.component.model.ComponentInst;
import org.silverpeas.core.admin.component.model.ComponentInstLight;
import org.silverpeas.core.admin.component.model.SilverpeasComponentInstance;
import org.silverpeas.core.admin.service.AdminException;
import org.silverpeas.core.admin.service.Administration;
import org.silverpeas.core.admin.service.OrganizationController;
import org.silverpeas.core.admin.space.SpaceInst;
import org.silverpeas.core.admin.user.model.UserDetail;
import org.silverpeas.core.annotation.WebService;
import org.silverpeas.core.contribution.attachment.AttachmentServiceProvider;
import org.silverpeas.core.contribution.attachment.model.SimpleDocument;
import org.silverpeas.core.contribution.attachment.model.SimpleDocumentPK;
import org.silverpeas.core.contribution.content.form.DataRecord;
import org.silverpeas.core.contribution.content.form.RecordSet;
import org.silverpeas.core.contribution.content.wysiwyg.service.WysiwygController;
import org.silverpeas.core.contribution.model.ContributionIdentifier;
import org.silverpeas.core.contribution.publication.model.PublicationDetail;
import org.silverpeas.core.contribution.publication.model.PublicationPK;
import org.silverpeas.core.contribution.publication.service.PublicationService;
import org.silverpeas.core.contribution.template.publication.PublicationTemplate;
import org.silverpeas.core.mylinks.model.LinkDetail;
import org.silverpeas.core.security.token.synchronizer.SynchronizerToken;
import org.silverpeas.core.util.URLUtil;
import org.silverpeas.core.util.file.FileFolderManager;
import org.silverpeas.core.util.file.FileRepositoryManager;
import org.silverpeas.core.web.look.PublicationHelper;
import org.silverpeas.core.web.rs.UserPrivilegeValidation;
import org.silverpeas.core.web.rs.annotation.Authorized;
import org.silverpeas.core.web.util.viewgenerator.html.GraphicElementFactory;
import org.silverpeas.kernel.bundle.SettingBundle;
import org.silverpeas.kernel.logging.SilverLogger;
import org.silverpeas.kernel.util.StringUtil;
import org.silverpeas.mobile.server.helpers.DataURLHelper;
import org.silverpeas.mobile.server.helpers.FormXMLHelper;
import org.silverpeas.mobile.server.services.helpers.FavoritesHelper;
import org.silverpeas.mobile.server.services.helpers.AllowedSpaces;
import org.silverpeas.mobile.server.services.helpers.NewsHelper;
import org.silverpeas.mobile.server.services.helpers.NotificationsPushHelper;
import org.silverpeas.mobile.server.services.helpers.UserHelper;
import org.silverpeas.mobile.server.services.helpers.events.EventsHelper;
import org.silverpeas.mobile.server.services.helpers.events.NextEvents;
import org.silverpeas.mobile.shared.dto.*;
import org.silverpeas.mobile.shared.dto.almanach.CalendarEventDTO;
import org.silverpeas.mobile.shared.dto.documents.PublicationDTO;
import org.silverpeas.mobile.shared.dto.navigation.*;
import org.silverpeas.mobile.shared.dto.navigation.aurora.AuroraSpaceHomePageConfig;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Predicate;

/**
 * Service de gestion de la navigation dans les espaces et apps.
 *
 * @author svuillet
 */
@WebService
@Authorized
@Path(ServiceNavigation.PATH)
public class ServiceNavigation extends AbstractRestWebService {

    @Context
    HttpServletRequest request;

    static final String PATH = "mobile/navigation";


    private final OrganizationController organizationController = OrganizationController.get();

    private boolean isUserGUIMobileForTablets() {
        return getSettings().getBoolean("guiMobileForTablets", true);
    }

    @PUT
    @Path("storeTokenMessaging/{token}/")
    public void storeTokenMessaging(@PathParam("token") String token) {
        NotificationsPushHelper.getInstance().storeToken(getUser().getId(), token);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("user/{login}/{domainId}/")
    public DetailUserDTO getUser(@PathParam("login") String login,
                                 @PathParam("domainId") String domainId) {

        String id = null;
        try {
            id = Administration.get().getUserIdByLoginAndDomain(login, domainId);
            UserDetail user = Administration.get().getUserDetail(id);

            DetailUserDTO userDTO = UserHelper.getInstance().populate(user);
            userDTO = initSession(userDTO);

            String avatar = DataURLHelper.convertAvatarToUrlData(user.getAvatarFileName(),
                    getSettings().getString("big.avatar.size", "40x"));
            userDTO.setAvatar(avatar);
            return userDTO;

        } catch (Exception e) {
            SilverLogger.getLogger(this).error(e);
            throw new WebApplicationException(e);
        }
    }

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Path("setTabletMode")
    public Boolean setTabletMode() {
        if (!isUserGUIMobileForTablets()) {
            request.getSession().setAttribute("tablet", Boolean.valueOf(true));
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    @GET
    @Path("clearAppCache")
    public void clearAppCache() {
        // clear cache
        getHttpServletResponse().setHeader("Clear-Site-Data", "\"cache\", \"cookies\", \"storage\"");
    }

    private static String getBaseUrl(HttpServletRequest request) {
        String scheme = request.getScheme() + "://";
        String serverName = request.getServerName();
        String serverPort = (request.getServerPort() == 80) ? "" : ":" + request.getServerPort();
        String contextPath = request.getContextPath();
        return scheme + serverName + serverPort + contextPath;
    }

    private DetailUserDTO initSession(DetailUserDTO user) {
        SynchronizerToken token = (SynchronizerToken) request.getSession().getAttribute("X-STKN");
        if (user != null) {
            UserDetail usr = organizationController.getUserDetail(user.getId());
            setUserInSession(usr);
            DetailUserDTO dto = UserHelper.getInstance().populate(usr);

            if (token == null) {
                // web security turn off
                try {
                    dto.setSessionKey(Administration.get().getUserFull(usr.getId()).getToken());
                } catch (Exception e) {
                    SilverLogger.getLogger(this).error(e);
                }
            } else {
                dto.setSessionKey(token.getValue());
            }
            return dto;
        } else {
            return null;
        }
    }

    protected void setUserInSession(UserDetail user) {
        request.getSession().setAttribute(AbstractRestWebService.USER_ATTRIBUT_NAME, user);
    }

    protected UserDetail getUserInSession() {
        return (UserDetail) request.getSession().getAttribute(AbstractRestWebService.USER_ATTRIBUT_NAME);
    }

    private AuroraSpaceHomePageConfig getAuroraSpaceHomePageConfig(String spaceId) {
        try {
            if (spaceId != null && !spaceId.isEmpty()) {
                SpaceInst space = Administration.get().getSpaceInstById(spaceId);
                if (space.getFirstPageType() == 0) {
                    for (SilverpeasComponentInstance instance : space.getAllComponentInstances()) {
                        if (instance.getName().equalsIgnoreCase("webPages")) {
                            if (instance.getParameterValue("xmlTemplate").equalsIgnoreCase("auroraspacehomepage.xml")) {
                                PublicationTemplate pubTemplate = FormXMLHelper.getXMLTemplate(request, instance.getId());
                                RecordSet recordSet = pubTemplate.getRecordSet();
                                DataRecord data = recordSet.getRecord("0");
                                AuroraSpaceHomePageConfig config = new AuroraSpaceHomePageConfig();
                                config.setIntroduction(getWysiwygFieldContent(data.getField("intro").getValue(), instance.getId()));
                                config.setPicture(getImageFieldURL(data.getField("picture").getValue(), instance.getId()));
                                List<ShortCutLinkDTO> shortcuts = new ArrayList<>();
                                for (int i = 1; i <= 6; i++) {
                                    String url = data.getField("shortcutURL"+i).getValue();
                                    String label = data.getField("shortcutLabel"+i).getValue();
                                    String icon = data.getField("shortcutIcon"+i).getValue();
                                    if (!url.isEmpty()) {
                                        ShortCutLinkDTO shortcut = new ShortCutLinkDTO();
                                        shortcut.setUrl(url);
                                        shortcut.setText(label);

                                        if (icon == null) {
                                            shortcut.setIcon("");
                                        } else {
                                            String urlIcon = "/silverpeas/attached_file/componentId/" + instance.getId() + "/attachmentId/";
                                            urlIcon += data.getField("shortcutIcon" + i).getValue();
                                            urlIcon += "/lang/fr/name/logo-silverpeas.png";
                                            shortcut.setIcon(urlIcon);
                                        }
                                        shortcuts.add(shortcut);
                                    }
                                }
                                config.setShortcuts(shortcuts);
                                config.setLatestPublications(data.getField("latestPublications").getValue());

                                if (config.getLatestPublications() != null && !config.getLatestPublications().trim().isEmpty()) {
                                    int max = Integer.valueOf(config.getLatestPublications());
                                    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM");
                                    ArrayList<PublicationDTO> lastPubs = new ArrayList<PublicationDTO>();
                                    List<PublicationDetail> pubs = getPublicationHelper().getPublications(spaceId, max);
                                    for (PublicationDetail pub : pubs) {
                                        if (pub.canBeAccessedBy(getUser())) {
                                            PublicationDTO dto = new PublicationDTO();
                                            dto.setId(pub.getId());
                                            dto.setName(pub.getName());
                                            dto.setUpdateDate(sdf.format(pub.getLastUpdateDate()));
                                            dto.setInstanceId(pub.getInstanceId());
                                            lastPubs.add(dto);
                                        }
                                    }
                                    config.setLastPublications(lastPubs);
                                }

                                //TODO : complete aurora fields
                                return config;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    private String getImageFieldURL(String fieldValue, String componentId) {
        SimpleDocumentPK attachmentPk;
        if (StringUtil.isLong(fieldValue)) {
            attachmentPk = new SimpleDocumentPK(null, componentId);
            attachmentPk.setOldSilverpeasId(Long.parseLong(fieldValue));
        } else {
            attachmentPk = new SimpleDocumentPK(fieldValue, componentId);
        }
        SimpleDocument attachment = AttachmentServiceProvider
                .getAttachmentService().searchDocumentById(
                        attachmentPk, getUser().getUserPreferences().getLanguage());
        if (attachment != null) {
            return URLUtil.getApplicationURL() + attachment.getAttachmentURL();
        }
        return "";
    }

    private String getWysiwygFieldContent(String fieldValue, String componentId) {
        final String DB_KEY = "xmlWysiwygField_";
        final String DIRECTORY_NAME = "xmlWysiwyg";
        String[] dirs = {DIRECTORY_NAME};
        String fileName = fieldValue.substring(DB_KEY.length());
        String path = FileRepositoryManager.getAbsolutePath(componentId, dirs);
        Optional<String> content = FileFolderManager.getFileContent(path, fileName);
        if (content.isEmpty()) return "";
        return content.get();
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("homepage/{spaceId}/{zoom}/")
    public HomePageDTO getHomePageData(@PathParam("spaceId") String spaceId, @PathParam("zoom") String zoom) {
        HomePageDTO data = new HomePageDTO();
        try {
            if (spaceId.equals("null")) spaceId = null;

            //initSilverpeasSession(request);
            request.getSession().setAttribute("Silverpeas_Portlet_SpaceId", spaceId);

            String look = "";
            try {
                look = getMainSessionController().getFavoriteLook();
            } catch (Exception e) {
                look = "Initial";
                SilverLogger.getLogger(this).error(e);
            }

            SettingBundle settings = GraphicElementFactory.getLookSettings(look);

            // Navigation (spaces & apps)
            if (spaceId == null) {
                data.setId("root");
            } else {
                data.setId(spaceId);
            }

            if (spaceId != null) {
                SpaceInst space = Administration.get().getSpaceInstById(spaceId);
                List<ComponentInst> apps = space.getAllComponentsInst();
                int hiddenApps = 0;
                for (ComponentInst app : apps) {
                    if (app.isHidden()) {
                        hiddenApps++;
                    }
                }
                data.setSpaceName(space.getName(getUser().getUserPreferences().getLanguage()));
            }
            data.setSpacesAndApps(getSpacesAndApps(spaceId));

            AuroraSpaceHomePageConfig pageConfig = getAuroraSpaceHomePageConfig(spaceId);
            if (pageConfig != null) {
                data.setAuroraConfig(pageConfig);
            }

            if (pageConfig == null) {

                data.setNewsDisplayer(
                        settings.getString("home.news.displayer", HomePageDTO.NEWS_DISPLAYER_CARROUSEL));
                int maxNews;
                if (spaceId == null) {
                    maxNews = settings.getInteger("home.news.size", 3);
                } else {
                    maxNews = settings.getInteger("space.homepage.news.nb", 3);
                }

                if ((spaceId == null && getSettings().getBoolean("homepage.lastnews", true)) ||
                        (spaceId != null && getSettings().getBoolean("spacehomepage.lastnews", true))) {
                    List<News> lastNews = NewsHelper.getInstance().getLastNews(getUser().getId(), spaceId, maxNews);
                    data.setNews(NewsHelper.getInstance().populate(lastNews, false));
                }

                if (spaceId == null || spaceId.isEmpty()) {
                    List<LinkDetail> links =
                            FavoritesHelper.getInstance().getBookmarkPersoVisible(getUser().getId());
                    data.setFavorites(FavoritesHelper.getInstance().populate(links));
                }


                // last publications
                if ((spaceId == null && getSettings().getBoolean("homepage.lastpublications", true)) ||
                        (spaceId != null && getSettings().getBoolean("spacehomepage.lastpublications", true))) {
                    try {
                        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM");
                        ArrayList<PublicationDTO> lastPubs = new ArrayList<PublicationDTO>();
                        int max;
                        if (spaceId == null) {
                            max = settings.getInteger("home.publications.nb", 3);
                        } else {
                            max = settings.getInteger("space.homepage.latestpublications.nb", 3);
                        }
                        List<PublicationDetail> pubs = getPublicationHelper().getPublications(spaceId, max);
                        for (PublicationDetail pub : pubs) {
                            if (pub.canBeAccessedBy(getUser())) {
                                PublicationDTO dto = new PublicationDTO();
                                dto.setId(pub.getId());
                                dto.setName(pub.getName());
                                dto.setUpdateDate(sdf.format(pub.getLastUpdateDate()));
                                dto.setInstanceId(pub.getInstanceId());
                                lastPubs.add(dto);
                            }
                        }
                        data.setLastPublications(lastPubs);

                    } catch (Exception e) {
                        SilverLogger.getLogger(this).error(e);
                    }
                }

                // aurora shortcuts
                List<ShortCutLinkDTO> shortCutLinkDTOList = new ArrayList<>();
                if (spaceId == null || spaceId.isEmpty()) {
                    int i = 1;
                    while (i > 0) {
                        String url = settings.getString("Shortcut.home." + i + ".Url", "");
                        if (!url.isEmpty()) {
                            String text = settings.getString("Shortcut.home." + i + ".AltText", "");
                            String icon = settings.getString("Shortcut.home." + i + ".IconUrl", "");
                            ShortCutLinkDTO shortCutLinkDTO = new ShortCutLinkDTO();
                            shortCutLinkDTO.setUrl(url);
                            shortCutLinkDTO.setText(text);
                            shortCutLinkDTO.setIcon(icon);
                            shortCutLinkDTOList.add(shortCutLinkDTO);
                        } else {
                            i = 0;
                            break;
                        }
                        i++;
                    }
                }
                data.setShortCuts(shortCutLinkDTOList);

                // aurora tools
                shortCutLinkDTOList = new ArrayList<>();
                if (spaceId == null || spaceId.isEmpty()) {
                    int i = 1;
                    while (i > 0) {
                        String url = settings.getString("Shortcut.tool." + i + ".Url", "");
                        if (!url.isEmpty()) {
                            String text = settings.getString("Shortcut.tool." + i + ".AltText", "");
                            String icon = settings.getString("Shortcut.tool." + i + ".IconUrl", "");
                            ShortCutLinkDTO shortCutLinkDTO = new ShortCutLinkDTO();
                            shortCutLinkDTO.setUrl(url);
                            shortCutLinkDTO.setText(text);
                            shortCutLinkDTO.setIcon(icon);
                            shortCutLinkDTOList.add(shortCutLinkDTO);
                        } else {
                            i = 0;
                            break;
                        }
                        i++;
                    }
                }
                data.setTools(shortCutLinkDTOList);


                // upcomming events
                NextEvents events = null;
                List<CalendarEventDTO> eventsToDisplay = null;
                String lang = getUser().getUserPreferences().getLanguage();
                if ((spaceId == null && getSettings().getBoolean("homepage.lastevents", true))) {
                    boolean includeToday = settings.getBoolean("home.events.today.include", true);
                    List<String> allowedComponentIds =
                            Arrays.asList(getAllowedComponentIds(settings, "home.events.appId", "almanach"));
                    int nbDays = settings.getInteger("home.events.maxDays", 3);
                    boolean onlyImportant = settings.getBoolean("home.events.importantOnly", false);
                    events = EventsHelper.getInstance()
                            .getNextEvents(allowedComponentIds, includeToday, nbDays, onlyImportant);
                } else if (spaceId != null && getSettings().getBoolean("spacehomepage.lastevents", true)) {
                    List<String> allowedAppIds = new ArrayList<>();
                    List<ComponentInstLight> components = getAllowedComponents(false, "almanach", spaceId);
                    for (ComponentInstLight component : components) {
                        allowedAppIds.add(component.getId());
                    }
                    events = EventsHelper.getInstance().getNextEvents(allowedAppIds, true, 5, false);
                }
                eventsToDisplay = EventsHelper.getInstance().populate(events, lang);
                data.setLastEvents(eventsToDisplay);

                // freezone

                if ((spaceId == null && getSettings().getBoolean("homepage.freezone", true))) {
                    String pageWebAppId = settings.getString("home.freezone.appId", "");
                    if (pageWebAppId != null && !pageWebAppId.isEmpty() && isComponentAvailable(pageWebAppId)) {
                        String html = WysiwygController.loadForReadOnly(pageWebAppId, pageWebAppId, lang);
                        html = "<div style='zoom:" + zoom + "'>" + html + "</div>";
                        data.setHtmlFreeZone(html);
                    }
                } else if (spaceId != null) {
                    SpaceInst space = Administration.get().getSpaceInstById(spaceId);
                    if (space.getFirstPageType() == HomePages.URL.getValue() &&
                            getSettings().getBoolean("spacehomepage.displayUrlType")) {
                        String url = space.getFirstPageExtraParam();
                        if (url.startsWith("/") && !url.startsWith("/silverpeas") && !url.startsWith("$"))
                            url = "/silverpeas" + url;
                        String html = getIframe(url, zoom);
                        data.setHtmlFreeZone(html);
                    } else if (space.getFirstPageType() == HomePages.APP.getValue() && space.getFirstPageExtraParam().startsWith("webPage")) {
                        String appId = space.getFirstPageExtraParam();
                        String url = "/silverpeas/services/spmobile/PublicationContent" + "?id=" + appId + "&componentId=" + appId;
                        String html = getIframe(url, zoom);
                        data.setHtmlFreeZone(html);
                    }
                }

                // freezone thin
                if ((spaceId == null && getSettings().getBoolean("homepage.freezonethin", true))) {
                    String pageWebAppId = settings.getString("home.freezone.thin.appId", "");
                    if (pageWebAppId != null && !pageWebAppId.isEmpty() && isComponentAvailable(pageWebAppId)) {
                        String html = WysiwygController.loadForReadOnly(pageWebAppId, pageWebAppId, lang);
                        data.setHtmlFreeZoneThin(html);
                    }
                }
            }

        } catch (Exception e) {
            SilverLogger.getLogger(this).error(e);
            throw new WebApplicationException(e);
        }

        return data;
    }

    private String getIframe(String url, String zoom) {
        String style = "border-style: none; width: 100%; overflow: hidden;";

        String script = "o.contentWindow.document.body.style='zoom:" + zoom + "'; ";
        script += "var h = o.contentWindow.document.body.scrollHeight;";
        script += "o.style.height=h+'px'; ";

        String html = "<iframe style='" + style + "' src='" + url + "' onload=\"" + "javascript:(function(o){" + script + "}(this));" + "\" scrolling='no'>";
        html += "</iframe>";
        return html;
    }

    private List<ComponentInstLight> getAllowedComponents(boolean visibleOnly, String name,
                                                          String spaceId) {
        OrganizationController oc = OrganizationController.get();
        String[] appIds = oc.getAvailCompoIdsAtRoot(spaceId, getUser().getId());
        List<ComponentInstLight> components = new ArrayList<>();
        Predicate<ComponentInstLight> canBeGet = c -> !visibleOnly || !c.isHidden();
        Predicate<ComponentInstLight> matchesName =
                c -> StringUtil.isNotDefined(name) || c.getName().equals(name);
        for (String appId : appIds) {
            ComponentInstLight component = oc.getComponentInstLight(appId);
            if (canBeGet.test(component) && matchesName.test(component)) {
                components.add(component);
            }
        }
        return components;
    }

    private String[] getAllowedComponentIds(SettingBundle settings, String param, String appName)
            throws AdminException {
        String paramValue = settings.getString(param, "");
        String[] appIds;
        if (paramValue.trim().equals("*")) {
            appIds = organizationController.getComponentIdsForUser(getUser().getId(), appName);
        } else {
            appIds = StringUtil.split(paramValue, ' ');
        }
        return getAllowedComponents(appIds).toArray(new String[0]);
    }

    private List<String> getAllowedComponents(String... componentIds) throws AdminException {
        List<String> allowedComponentIds = new ArrayList<>();
        for (String componentId : componentIds) {
            if (isComponentAvailable(componentId)) {
                allowedComponentIds.add(componentId);
            }
        }
        return allowedComponentIds;
    }

    private boolean isComponentAvailable(String componentId) throws AdminException {
        return Administration.get().isComponentAvailableToUser(componentId, getUser().getId());
    }

    private boolean isSupportedApp(ComponentInstLight app) {
        String[] appsExcluded = getSettings().getList("apps.exclude.intances", ",");
        if (EnumUtils.isValidEnum(Apps.class, app.getName())) {
            String[] supportedApps = getSettings().getList("apps.supported", ",");
            if (Arrays.asList(appsExcluded).contains(app.getId())) {
                return false;
            }

            if (Arrays.asList(supportedApps).contains(app.getName())) {
                return true;
            } else {
                return false;
            }
        } else if (isWorkflowApp(app)) {
            if (Arrays.asList(appsExcluded).contains(app.getId())) {
                return false;
            }
            return true;
        }
        return false;
    }

    private boolean isWorkflowApp(ComponentInstLight app) {
        try {
            return app.isWorkflow();
        } catch (Throwable t) {
            return false;
        }
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("isWorkflowApp/{instanceId}/")
    public Boolean isWorkflowApp(@PathParam("instanceId") String intanceId) {
        try {
            ComponentInstLight app = Administration.get().getComponentInstLight(intanceId);
            return app.isWorkflow();
        } catch (Throwable t) {
            throw new WebApplicationException(t);
        }
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("personalSpace/{userId}/")
    public List<ApplicationInstanceDTO> getPersonnalSpaceContent(@PathParam("userId") String userId) {
        try {
            SpaceInst space = Administration.get().getPersonalSpace(userId);
            List<ApplicationInstanceDTO> apps = new ArrayList<>();
            // A user may not have a personal space yet.
            if (space == null) {
                return apps;
            }
            for (ComponentInst app : space.getAllComponentsInst()) {
                ApplicationInstanceDTO dto = populate(app, true);
                apps.add(dto);
            }
            return apps;
        } catch (Exception e) {
            SilverLogger.getLogger(this).error(e);
            throw new WebApplicationException(e);
        }
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("space/{spaceId}/")
    public SpaceDTO getSpace(@PathParam("spaceId") String spaceId) {
        try {
            SpaceInst space = Administration.get().getSpaceInstById(spaceId);
            SpaceDTO dto = new SpaceDTO();
            dto.setHomePageType(space.getFirstPageType());
            dto.setHomePageParameter(space.getFirstPageExtraParam());
            return dto;
        } catch (AdminException e) {
            SilverLogger.getLogger(this).error(e);
            throw new WebApplicationException(e);
        }
    }

    //TODO : remove appType
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("spacesAndApps/{rootSpaceId}/")
    public List<SilverpeasObjectDTO> getSpacesAndApps(@PathParam("rootSpaceId") String rootSpaceId) {
        ArrayList<SilverpeasObjectDTO> results = new ArrayList<>();
        if (rootSpaceId != null && rootSpaceId.equals("null")) rootSpaceId = null;
        try {
            AllowedSpaces allowedSpaces = new AllowedSpaces(
                getSettings().getString("spaces.allowed", ""),
                getSettings().getString("spaces.excluded", ""));
            Map<String, SpaceInst> spaces = new HashMap<>();
            AllowedSpaces.ParentResolver parents = id -> {
                SpaceInst parent = getNavigationSpace(id, spaces);
                return parent != null ? parent.getDomainFatherId() : null;
            };
            if (rootSpaceId != null && !allowedSpaces.allows(rootSpaceId, parents)) {
                return results;
            }
            if (rootSpaceId == null) {
                List<String> spaceIds = !allowedSpaces.hasAllowedRoots()
                    ? Arrays.asList(Administration.get().getAllRootSpaceIds(getUser().getId()))
                    : allowedSpaces.navigationRoots(
                        Arrays.asList(Administration.get().getAllSpaceIds(getUser().getId())), parents);
                for (String spaceId : spaceIds) {
                    SpaceInst space = getNavigationSpace(spaceId, spaces);
                    if (space != null && !space.isRemoved() && allowedSpaces.allows(space.getId(), parents)) {
                        if (allowedSpaces.hasAllowedRoots() || space.getLevel() == 0) {
                            if (containApp(space)) {
                                results.add(populate(space));
                            }
                        }
                    }
                }
                Collections.sort(results);
            } else {
                String[] spaceIds =
                        Administration.get().getAllowedSubSpaceIds(getUser().getId(), rootSpaceId);
                for (String spaceId : spaceIds) {
                    SpaceInst space = getNavigationSpace(spaceId, spaces);
                    if (space != null && !space.isRemoved() && allowedSpaces.allows(space.getId(), parents)) {
                        if (("WA" + space.getDomainFatherId()).equals(rootSpaceId) ||
                                space.getDomainFatherId().equals(rootSpaceId)) {
                            if (containApp(space)) {
                                results.add(populate(space));
                            }
                        }
                    }
                }
                Collections.sort(results);
                ArrayList<SilverpeasObjectDTO> partialResults = new ArrayList<>();
                String[] appsIds = Administration.get().getAvailCompoIds(rootSpaceId, getUser().getId());
                for (String appId : appsIds) {
                    ComponentInstLight app = Administration.get().getComponentInstLight(appId);
                    if (isSupportedApp(app) && app.getDomainFatherId().equals(rootSpaceId)) {
                        if (!app.isHidden()) {
                            SpaceInst space = Administration.get().getSpaceInstById(app.getDomainFatherId());
                            partialResults.add(populate(app, space.isPersonalSpace()));
                        }
                    }
                }
                Collections.sort(partialResults);
                results.addAll(partialResults);
            }

        } catch (Exception e) {
            SilverLogger.getLogger(this).error(e);
        }
        return results;
    }

    private SpaceInst getNavigationSpace(String spaceId, Map<String, SpaceInst> spaces)
        throws AdminException {
        String id = AllowedSpaces.normalizeId(spaceId);
        if (id.isEmpty() || "0".equals(id)) return null;
        if (!spaces.containsKey(id)) {
            spaces.put(id, Administration.get().getSpaceInstById("WA" + id));
        }
        return spaces.get(id);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("app/{instanceId}/{contentId}/{contentType}/")
    public ApplicationInstanceDTO getApp(@PathParam("instanceId") String instanceId,
                                         @PathParam("contentId") String contentId, @PathParam("contentType") String contentType) {
        if (instanceId.equals("null")) instanceId = null;
        String localId = "";
        if (instanceId == null) {
            if (contentType.equals(ContentsTypes.Publication.name())) {
                PublicationDetail pub = PublicationService.get().getDetail(new PublicationPK(contentId));
                if (pub == null) throw new NotFoundException();
                instanceId = pub.getInstanceId();
            } else if (contentType.equals(ContentsTypes.Media.name())) {
                Media media = MediaServiceProvider.getMediaService().getMedia(new MediaPK(contentId));
                if (media == null) throw new NotFoundException();
                instanceId = media.getInstanceId();
            } else if (contentType.equals(ContentsTypes.Event.name())) {
                ContributionIdentifier contributionId =
                        ContributionIdentifier.decode(new String(StringUtil.fromBase64(contentId)));
                localId = contributionId.getLocalId();
                instanceId = contributionId.getComponentInstanceId();
                if (instanceId.equals("?")) throw new NotFoundException();
            }
        }
        ApplicationInstanceDTO dto = getApplicationInstanceDTO(instanceId);
        if (dto == null) throw new NotFoundException();
        dto.setExtraId(localId);
        populateParameters(dto.getType(), dto.getId(), dto);
        return dto;
    }

    private ApplicationInstanceDTO getApplicationInstanceDTO(final String instanceId) {
        ApplicationInstanceDTO dto = null;
        try {
            ComponentInstLight app = Administration.get().getComponentInstLight(instanceId);
            SpaceInst space = Administration.get().getSpaceInstById(app.getDomainFatherId());
            dto = populate(app, space.isPersonalSpace());
        } catch (Exception e) {
            SilverLogger.getLogger(this).error(e);
        }
        return dto;
    }

    private boolean containApp(SpaceInst space) throws Exception {
        String[] appsIds = Administration.get().getAvailCompoIds(space.getId(), getUser().getId());
        for (String appId : appsIds) {
            ComponentInstLight app = Administration.get().getComponentInstLight(appId);
            if (isSupportedApp(app)) {
                return true;
            }
        }
        return false;
    }

    private String[] getUserRoles(String componentId, String userId) {
        return organizationController.getUserProfiles(userId, componentId);
    }

    private SpaceDTO populate(SpaceInst space) {
        SpaceDTO dto = new SpaceDTO();
        dto.setId(space.getId());
        dto.setLabel(space.getName());
        dto.setPersonal(space.isPersonalSpace());
        dto.setOrderNum(space.getOrderNum());

        if (space.getFirstPageType() == HomePages.APP.getValue()) {
            List<ComponentInst> apps = space.getAllComponentsInst();
            int nbHiddenApps = 0;
            String appNotHidden = "";
            if (apps.size() == 1) {
                dto.setHomePageType(HomePages.APP.getValue());
            } else {
                for (ComponentInst app : apps) {
                    if (app.isHidden()) {
                        nbHiddenApps++;
                    } else {
                        appNotHidden = app.getId();
                    }
                }
                if (apps.size() == nbHiddenApps || (apps.size() - 1 == nbHiddenApps &&
                        appNotHidden.equals(space.getFirstPageExtraParam()))) {
                    dto.setHomePageType(HomePages.APP.getValue());
                } else {
                    dto.setHomePageType(HomePages.SILVERPEAS.getValue());
                }
            }
        } else {
            dto.setHomePageType(space.getFirstPageType());
        }
        dto.setHomePageParameter(space.getFirstPageExtraParam());

        return dto;
    }

    private ApplicationInstanceDTO populate(ComponentInst app, boolean personal) {
        ApplicationInstanceDTO dto = new ApplicationInstanceDTO();
        dto.setId(app.getId());
        dto.setLabel(app.getLabel());
        dto.setType(app.getName());
        dto.setOrderNum(app.getOrderNum());
        dto.setPersonnal(personal);

        populateParameters(app.getName(), app.getId(), dto);

        return dto;
    }

    private void populateParameters(String name, String id, ApplicationInstanceDTO dto) {
        Map<String, String> params = new HashMap<String, String>();
        if (name.equals("quickinfo")) {
            String value = "";
            value = getMainSessionController().getComponentParameterValue(id, "thumbnailMandatory");
            params.put("thumbnailMandatory", value);
            value = getMainSessionController().getComponentParameterValue(id, "thumbnailWidthSize");
            params.put("thumbnailWidthSize", value);
            value = getMainSessionController().getComponentParameterValue(id, "thumbnailHeightSize");
            params.put("thumbnailHeightSize", value);
        }
        dto.setParameters(params);
    }

    private ApplicationInstanceDTO populate(ComponentInstLight app, boolean personal) {
        ApplicationInstanceDTO dto = new ApplicationInstanceDTO();
        dto.setId(app.getId());
        dto.setLabel(app.getLabel());
        dto.setType(app.getName());
        dto.setOrderNum(app.getOrderNum());
        dto.setWorkflow(isWorkflowApp(app));
        dto.setPersonnal(personal);
        dto.setIntroduction(loadIntroduction(app));

        RightDTO rights = new RightDTO();
        String[] roles = getUserRoles(app.getId(), getUser().getId());
        for (int i = 0; i < roles.length; i++) {
            if (roles[i].equals("admin")) {
                rights.setManager(true);
            }
            if (roles[i].equals("publisher")) {
                rights.setPublisher(true);
            }
            if (roles[i].equals("writer")) {
                rights.setWriter(true);
            }
            if (roles[i].equals("user")) {
                rights.setReader(true);
            }
        }
        dto.setRights(rights);

        try {
            String value = "";
            try {
                value = getMainSessionController().getComponentParameterValue(app.getId(), "notifications");
                dto.setNotifiable(value.equals("yes"));
            } catch (Exception e) {
                dto.setNotifiable(false);
            }
            if (app.getName().equals("kmelia")) {
                value = getMainSessionController().getComponentParameterValue(app.getId(), "tabComments");
            } else if (app.getName().equals("gallery") || app.getName().equals("quickinfo")) {
                value = getMainSessionController().getComponentParameterValue(app.getId(), "comments");
            }

            if (app.getName().equals("kmelia")) {
                dto.setCommentable(value.equals("yes"));
            } else if (app.getName().equals("gallery")) {
                dto.setCommentable(true);
                dto.setNotifiable(true);
            } else if (app.getName().equals("blog")) {
                dto.setCommentable(true);
                dto.setNotifiable(true);
            } else if (app.getName().equals("quickinfo")) {
                dto.setCommentable(value.equals("yes"));
                dto.setNotifiable(true);
            }
        } catch (Exception e) {
            dto.setCommentable(false);
        }

        try {
            String value = "";
            if (app.getName().equals("kmelia")) {
                value = getMainSessionController().getComponentParameterValue(app.getId(), "tabContent");
            }
            dto.setAbleToStoreContent(value.equals("yes"));
        } catch (Exception e) {
            dto.setAbleToStoreContent(false);
        }

        if (app.getName().equals("kmelia")) {
            try {
                String value = "";
                value = getMainSessionController().getComponentParameterValue(app.getId(), "useFolderSharing");
                dto.setFolderSharing(Integer.parseInt(value));
                value = getMainSessionController().getComponentParameterValue(app.getId(), "usePublicationSharing");
                dto.setPublicationSharing(Integer.parseInt(value));
                value = getMainSessionController().getComponentParameterValue(app.getId(), "useFileSharing");
                dto.setFileSharing(Integer.parseInt(value));
            } catch (Exception e) {
                dto.setFolderSharing(0);
                dto.setPublicationSharing(0);
                dto.setFileSharing(0);
            }
        } else populateParameters(app.getName(), app.getId(), dto);

        return dto;
    }
    private String loadIntroduction(ComponentInstLight app) {
        String intro = WysiwygController.load(app.getId(), "Intro", getUser().getUserPreferences().getLanguage());
        if (app.getId().startsWith("classifieds") || app.getId().startsWith("kmelia")) {
            intro = WysiwygController.load(app.getId(), "Node_0", getUser().getUserPreferences().getLanguage());
        } else {
            ComponentInstLight component = OrganizationController.get().getComponentInstLight(app.getId());
            if (component.isWorkflow()) {
                intro = WysiwygController.load(app.getId(), app.getId(), getUser().getUserPreferences().getLanguage());
            }
        }
        if (StringUtil.isNotDefined(intro)) {
            intro = "";
        }
        return intro;
    }
    private PublicationHelper getPublicationHelper() throws Exception {
        SettingBundle settings =
                GraphicElementFactory.getLookSettings(GraphicElementFactory.DEFAULT_LOOK_NAME);
        String helperClassName = settings.getString("publicationHelper",
                "org.silverpeas.components.kmelia.KmeliaTransversal");
        Class<?> helperClass = Class.forName(helperClassName);
        PublicationHelper kmeliaTransversal =
                (PublicationHelper) helperClass.getDeclaredConstructor().newInstance();
        kmeliaTransversal.setMainSessionController(getMainSessionController());

        return kmeliaTransversal;
    }

    @Override
    protected String getResourceBasePath() {
        return PATH;
    }

    @Override
    public String getComponentId() {
        return null;
    }

    @Override
    public void validateUserAuthorization(final UserPrivilegeValidation validation) {
    }

}