001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.apps;
029
030import org.opencms.db.CmsUserSettings;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsUser;
034import org.opencms.file.types.I_CmsResourceType;
035import org.opencms.json.JSONArray;
036import org.opencms.json.JSONException;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.OpenCms;
040import org.opencms.module.CmsModule;
041import org.opencms.module.CmsModuleManager;
042import org.opencms.ui.CmsUserIconHelper;
043import org.opencms.ui.I_CmsDialogContext;
044import org.opencms.ui.actions.CmsContextMenuActionItem;
045import org.opencms.ui.actions.I_CmsDefaultAction;
046import org.opencms.ui.apps.cacheadmin.CmsCacheAdminConfiguration;
047import org.opencms.ui.apps.cacheadmin.CmsCacheFolder;
048import org.opencms.ui.apps.cacheadmin.CmsCacheViewFlexConfiguration;
049import org.opencms.ui.apps.cacheadmin.CmsCacheViewImageConfiguration;
050import org.opencms.ui.apps.dbmanager.CmsDbExportConfiguration;
051import org.opencms.ui.apps.dbmanager.CmsDbImportHTTPConfiguration;
052import org.opencms.ui.apps.dbmanager.CmsDbImportServerConfiguration;
053import org.opencms.ui.apps.dbmanager.CmsDbManagerConfiguration;
054import org.opencms.ui.apps.dbmanager.CmsDbManagerFolder;
055import org.opencms.ui.apps.dbmanager.CmsDbPropertiesAppConfiguration;
056import org.opencms.ui.apps.dbmanager.CmsDbRemovePubLocksConfiguration;
057import org.opencms.ui.apps.dbmanager.CmsDbStaticExportConfiguration;
058import org.opencms.ui.apps.dbmanager.CmsDbSynchronizationConfiguration;
059import org.opencms.ui.apps.dbmanager.sqlconsole.CmsSqlConsoleAppConfiguration;
060import org.opencms.ui.apps.filehistory.CmsFileHistoryClearConfiguration;
061import org.opencms.ui.apps.filehistory.CmsFileHistoryConfiguration;
062import org.opencms.ui.apps.filehistory.CmsFileHistoryFolder;
063import org.opencms.ui.apps.git.CmsGitAppConfiguration;
064import org.opencms.ui.apps.linkvalidation.CmsLinkInFolderValidationConfiguration;
065import org.opencms.ui.apps.linkvalidation.CmsLinkValidationConfiguration;
066import org.opencms.ui.apps.linkvalidation.CmsLinkValidationExternalConfiguration;
067import org.opencms.ui.apps.linkvalidation.CmsLinkValidationFolder;
068import org.opencms.ui.apps.lists.CmsListManagerConfiguration;
069import org.opencms.ui.apps.logfile.CmsLogFileConfiguration;
070import org.opencms.ui.apps.modules.CmsModuleAppConfiguration;
071import org.opencms.ui.apps.projects.CmsProjectManagerConfiguration;
072import org.opencms.ui.apps.projects.CmsProjectOverviewConfiguration;
073import org.opencms.ui.apps.publishqueue.CmsPublishQueueConfiguration;
074import org.opencms.ui.apps.resourcetypes.CmsResourceTypeAppConfiguration;
075import org.opencms.ui.apps.scheduler.CmsScheduledJobsAppConfig;
076import org.opencms.ui.apps.search.CmsSourceSearchAppConfiguration;
077import org.opencms.ui.apps.searchindex.CmsSearchindexAppConfiguration;
078import org.opencms.ui.apps.sessions.CmsBroadCastConfigurtion;
079import org.opencms.ui.apps.shell.CmsShellAppConfiguration;
080import org.opencms.ui.apps.sitemanager.CmsSiteManagerConfiguration;
081import org.opencms.ui.apps.user.CmsAccountsAppConfiguration;
082import org.opencms.ui.contextmenu.CmsContextMenuItemProviderGroup;
083import org.opencms.ui.contextmenu.I_CmsContextMenuItem;
084import org.opencms.ui.contextmenu.I_CmsContextMenuItemProvider;
085import org.opencms.ui.editors.CmsAcaciaEditor;
086import org.opencms.ui.editors.CmsSourceEditor;
087import org.opencms.ui.editors.CmsXmlContentEditor;
088import org.opencms.ui.editors.CmsXmlPageEditor;
089import org.opencms.ui.editors.I_CmsEditor;
090import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditor;
091import org.opencms.util.CmsStringUtil;
092import org.opencms.workplace.tools.CmsTool;
093import org.opencms.workplace.tools.CmsToolManager;
094import org.opencms.workplace.tools.I_CmsToolHandler;
095
096import java.io.File;
097import java.util.ArrayList;
098import java.util.Arrays;
099import java.util.Collection;
100import java.util.Collections;
101import java.util.Comparator;
102import java.util.HashMap;
103import java.util.HashSet;
104import java.util.Iterator;
105import java.util.LinkedHashSet;
106import java.util.List;
107import java.util.Map;
108import java.util.ServiceLoader;
109import java.util.Set;
110
111import org.apache.commons.logging.Log;
112
113import com.google.common.collect.ComparisonChain;
114import com.google.common.collect.Lists;
115import com.google.common.collect.Maps;
116import com.google.common.collect.Sets;
117
118/**
119 * The workplace app manager.<p>
120 */
121public class CmsWorkplaceAppManager {
122
123    /**
124     * Comparator for configuration objects implementing I_CmsHasOrder.<p>
125     *
126     * @param <T> the type to compare
127     */
128    public static class ConfigurationComparator<T extends I_CmsHasOrder> implements Comparator<T> {
129
130        /**
131         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
132         */
133        public int compare(I_CmsHasOrder o1, I_CmsHasOrder o2) {
134
135            return ComparisonChain.start().compare(o1.getOrder(), o2.getOrder()).result();
136        }
137    }
138
139    /**
140     * Wrapper for the navigation state.<p>
141     */
142    public static class NavigationState {
143
144        /** The parameter separator. */
145        public static final String PARAM_SEPARATOR = "/";
146
147        /** The state parameters. */
148        private String m_params = "";
149
150        /** The view/app name. */
151        private String m_viewName = "";
152
153        /**
154         * Constructor.<p>
155         *
156         * @param stateString the state string to parse
157         */
158        public NavigationState(String stateString) {
159
160            if (stateString.startsWith("!")) {
161                stateString = stateString.substring(1);
162            }
163            int separatorPos = stateString.indexOf(PARAM_SEPARATOR);
164            if (separatorPos > 0) {
165                m_viewName = stateString.substring(0, separatorPos);
166                m_params = stateString.substring(separatorPos + 1);
167            } else {
168                m_viewName = stateString;
169            }
170            if (m_viewName.endsWith("/")) {
171                m_viewName = m_viewName.substring(0, m_viewName.length() - 1);
172            }
173        }
174
175        /**
176         * Returns the parameter part of the state.<p>
177         *
178         * @return the parameters
179         */
180        String getParams() {
181
182            return m_params;
183        }
184
185        /**
186         * Returns the view name.<p>
187         *
188         * @return the view name
189         */
190        String getViewName() {
191
192            return m_viewName;
193
194        }
195    }
196
197    /** The administration category id. */
198    public static final String ADMINISTRATION_CATEGORY_ID = "Administration";
199
200    /** The legacy category id. */
201    public static final String LEGACY_CATEGORY_ID = "Legacy";
202
203    /** The main category id. */
204    public static final String MAIN_CATEGORY_ID = "Main";
205
206    /** The toolbar.css resource name. */
207    public static final String TOOLBAR_CSS = "css/toolbar.css";
208
209    /** The workplace app settings additional info key. */
210    public static String WORKPLACE_APP_SETTINGS_KEY = "WORKPLACE_APP_SETTINGS";
211
212    /** The workplace CSS module parameter name. */
213    public static String WORKPLACE_CSS_PARAM = "workplace-css";
214
215    /** The logger for this class. */
216    protected static Log LOG = CmsLog.getLog(CmsWorkplaceAppManager.class.getName());
217
218    /** The default quick launch apps, these can be overridden by the user. */
219    private static final String[] DEFAULT_USER_APPS = new String[] {
220        CmsAccountsAppConfiguration.APP_ID,
221        CmsModuleAppConfiguration.APP_ID};
222
223    /** The available editors. */
224    private static final I_CmsEditor[] EDITORS = new I_CmsEditor[] {
225        new CmsAcaciaEditor(),
226        new CmsSourceEditor(),
227        new CmsXmlContentEditor(),
228        new CmsXmlPageEditor(),
229        new CmsMessageBundleEditor()};
230
231    /** Legacy apps explicitly hidden from new workplace. */
232    private static final Set<String> LEGACY_BLACKLIST = Sets.newConcurrentHashSet(
233        Arrays.asList(
234            "/accounts",
235            "/contenttools",
236            "/git",
237            "/scheduler",
238            "/galleryoverview",
239            "/projects",
240            "/project_overview",
241            "/history",
242            "/sites",
243            "/cache",
244            "/publishqueue",
245            "/database",
246            "/linkvalidation",
247            "/workplace",
248            "/modules",
249            "/searchindex"));
250
251    /** The additional info key for the user quick launch apps. */
252    private static final String QUICK_LAUCH_APPS_KEY = "quick_launch_apps";
253
254    /** The standard quick launch apps. */
255    private static final String[] STANDARD_APPS = new String[] {
256        CmsPageEditorConfiguration.APP_ID,
257        CmsSitemapEditorConfiguration.APP_ID,
258        CmsFileExplorerConfiguration.APP_ID,
259        CmsAppHierarchyConfiguration.APP_ID};
260
261    /** The additional style sheets. */
262    private Collection<String> m_additionalStyleSheets;
263
264    /** The admin cms context. */
265    private CmsObject m_adminCms;
266
267    /** The app categories. */
268    private Map<String, I_CmsAppCategory> m_appCategories;
269
270    /** The configured apps. */
271    private Map<String, I_CmsWorkplaceAppConfiguration> m_appsById = Maps.newHashMap();
272
273    /** The user icon helper. */
274    private CmsUserIconHelper m_iconHelper;
275
276    /** The standard quick launch apps. */
277    private List<I_CmsWorkplaceAppConfiguration> m_standardQuickLaunchApps;
278
279    /** The additional workplace CSS URIs. */
280    private Set<String> m_workplaceCssUris;
281
282    /** Menu item manager. */
283    private CmsContextMenuItemProviderGroup m_workplaceMenuItemProvider;
284
285    /**
286     * Constructor.<p>
287     *
288     * @param adminCms the admin cms context
289     *
290     * @throws CmsException in case initializing the cms object fails
291     */
292    public CmsWorkplaceAppManager(CmsObject adminCms)
293    throws CmsException {
294
295        m_adminCms = adminCms;
296        m_iconHelper = new CmsUserIconHelper(OpenCms.initCmsObject(m_adminCms));
297        m_workplaceMenuItemProvider = new CmsContextMenuItemProviderGroup();
298        m_workplaceMenuItemProvider.addProvider(CmsDefaultMenuItemProvider.class);
299        m_workplaceMenuItemProvider.initialize();
300    }
301
302    /**
303     * Constructor for testing only.<p>
304     */
305    protected CmsWorkplaceAppManager() {
306        // nothing to do
307    }
308
309    /**
310     * Returns the additional style sheets provided by I_CmsWorkplaceStylesheetProvider services.<p>
311     *
312     * @return the additional style sheets
313     */
314    public Collection<String> getAdditionalStyleSheets() {
315
316        if (m_additionalStyleSheets == null) {
317            Set<String> stylesheets = new LinkedHashSet<>();
318            for (I_CmsWorkplaceStylesheetProvider provider : ServiceLoader.load(
319                I_CmsWorkplaceStylesheetProvider.class)) {
320                stylesheets.addAll(provider.getStylesheets());
321            }
322            m_additionalStyleSheets = Collections.unmodifiableSet(stylesheets);
323        }
324        return m_additionalStyleSheets;
325    }
326
327    /**
328     * Returns the app configuration with the given id.<p>
329     *
330     * @param appId the app id
331     *
332     * @return the app configuration
333     */
334    public I_CmsWorkplaceAppConfiguration getAppConfiguration(String appId) {
335
336        return m_appsById.get(appId);
337    }
338
339    /**
340     * Returns the app configuration instances for the given ids.<p>
341     *
342     * @param appIds the app ids
343     *
344     * @return the app configurations
345     */
346    public List<I_CmsWorkplaceAppConfiguration> getAppConfigurations(String... appIds) {
347
348        List<I_CmsWorkplaceAppConfiguration> result = new ArrayList<I_CmsWorkplaceAppConfiguration>();
349        for (int i = 0; i < appIds.length; i++) {
350            I_CmsWorkplaceAppConfiguration config = getAppConfiguration(appIds[i]);
351            if (config != null) {
352                result.add(config);
353            }
354        }
355        return result;
356    }
357
358    /**
359     * Returns the user app setting of the given type.<p>
360     *
361     * @param cms the cms context
362     * @param type the app setting type
363     *
364     * @return the app setting
365     *
366     * @throws InstantiationException in case instantiating the settings type fails
367     * @throws IllegalAccessException in case the settings default constructor is not accessible
368     */
369    public <T extends I_CmsAppSettings> T getAppSettings(CmsObject cms, Class<T> type)
370    throws InstantiationException, IllegalAccessException {
371
372        CmsUser user = cms.getRequestContext().getCurrentUser();
373        CmsUserSettings settings = new CmsUserSettings(user);
374        String settingsString = settings.getAdditionalPreference(type.getName(), true);
375        T result = type.newInstance();
376
377        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(settingsString)) {
378
379            result.restoreSettings(settingsString);
380
381        }
382
383        return result;
384    }
385
386    /**
387     * Returns the configured categories.<p>
388     *
389     * @return the app categories
390     */
391    public Collection<I_CmsAppCategory> getCategories() {
392
393        return Collections.unmodifiableCollection(m_appCategories.values());
394    }
395
396    /**
397     * Returns the default action for the given context if available.<p>
398     *
399     * @param context the dialog context
400     *
401     * @return the default action
402     */
403    public I_CmsDefaultAction getDefaultAction(I_CmsDialogContext context) {
404
405        return getDefaultAction(context, getMenuItemProvider());
406    }
407
408    /**
409     * Returns the default action for the given context if available.<p>
410     *
411     * @param context the dialog context
412     * @param menuItemProvider the menu item provider
413     *
414     * @return the default action
415     */
416    public I_CmsDefaultAction getDefaultAction(
417        I_CmsDialogContext context,
418        I_CmsContextMenuItemProvider menuItemProvider) {
419
420        I_CmsDefaultAction result = null;
421        int resultRank = -1;
422        if (context.getResources().size() == 1) {
423            for (I_CmsContextMenuItem menuItem : menuItemProvider.getMenuItems()) {
424                if ((menuItem instanceof CmsContextMenuActionItem)
425                    && (((CmsContextMenuActionItem)menuItem).getWorkplaceAction() instanceof I_CmsDefaultAction)) {
426                    I_CmsDefaultAction action = (I_CmsDefaultAction)((CmsContextMenuActionItem)menuItem).getWorkplaceAction();
427                    if (action.getVisibility(context).isActive()) {
428                        if (result == null) {
429                            result = action;
430                            resultRank = action.getDefaultActionRank(context);
431                        } else {
432                            int rank = action.getDefaultActionRank(context);
433                            if (rank > resultRank) {
434                                result = action;
435                                resultRank = rank;
436                            }
437                        }
438                    }
439                }
440            }
441        }
442        return result;
443    }
444
445    /**
446     * Gets all configured quick launch apps, independent of the current user.<p>
447     *
448     * @return the quick launch apps
449     */
450    public List<I_CmsWorkplaceAppConfiguration> getDefaultQuickLaunchConfigurations() {
451
452        if (m_standardQuickLaunchApps == null) {
453
454            m_standardQuickLaunchApps = Collections.unmodifiableList(getAppConfigurations(STANDARD_APPS));
455        }
456        return m_standardQuickLaunchApps;
457    }
458
459    /**
460     * Returns the editor for the given resource.<p>
461     *
462     * @param resource the resource to edit
463     * @param plainText if plain text editing is required
464     *
465     * @return the editor
466     */
467    public I_CmsEditor getEditorForResource(CmsResource resource, boolean plainText) {
468
469        List<I_CmsEditor> editors = new ArrayList<I_CmsEditor>();
470        for (int i = 0; i < EDITORS.length; i++) {
471            if (EDITORS[i].matchesResource(resource, plainText)) {
472                editors.add(EDITORS[i]);
473            }
474        }
475        I_CmsEditor result = null;
476        if (editors.size() == 1) {
477            result = editors.get(0);
478        } else if (editors.size() > 1) {
479            Collections.sort(editors, new Comparator<I_CmsEditor>() {
480
481                public int compare(I_CmsEditor o1, I_CmsEditor o2) {
482
483                    return o1.getPriority() > o2.getPriority() ? -1 : 1;
484                }
485            });
486            result = editors.get(0);
487        }
488        return result;
489    }
490
491    /**
492     * Returns the editor for the given resource type.<p>
493     *
494     * @param type the resource type to edit
495     * @param plainText if plain text editing is required
496     *
497     * @return the editor
498     */
499    public I_CmsEditor getEditorForType(I_CmsResourceType type, boolean plainText) {
500
501        List<I_CmsEditor> editors = new ArrayList<I_CmsEditor>();
502        for (int i = 0; i < EDITORS.length; i++) {
503            if (EDITORS[i].matchesType(type, plainText)) {
504                editors.add(EDITORS[i]);
505            }
506        }
507        I_CmsEditor result = null;
508        if (editors.size() == 1) {
509            result = editors.get(0);
510        } else if (editors.size() > 1) {
511            Collections.sort(editors, new Comparator<I_CmsEditor>() {
512
513                public int compare(I_CmsEditor o1, I_CmsEditor o2) {
514
515                    return o1.getPriority() > o2.getPriority() ? -1 : 1;
516                }
517            });
518            result = editors.get(0);
519        }
520        return result;
521    }
522
523    /**
524     * Gets the menu item provider for the workplace.<p>
525     *
526     * @return the menu item provider
527     */
528    public I_CmsContextMenuItemProvider getMenuItemProvider() {
529
530        return m_workplaceMenuItemProvider;
531    }
532
533    /**
534     * Gets the configured quick launch apps which are visible for the current user.<p>
535     *
536     * @param cms the current CMS context
537     * @return the list of available quick launch apps
538     */
539    public List<I_CmsWorkplaceAppConfiguration> getQuickLaunchConfigurations(CmsObject cms) {
540
541        List<I_CmsWorkplaceAppConfiguration> result = new ArrayList<I_CmsWorkplaceAppConfiguration>();
542        result.addAll(getDefaultQuickLaunchConfigurations());
543        result.addAll(getUserQuickLauchConfigurations(cms));
544        Iterator<I_CmsWorkplaceAppConfiguration> it = result.iterator();
545        while (it.hasNext()) {
546            I_CmsWorkplaceAppConfiguration appConfig = it.next();
547            CmsAppVisibilityStatus visibility = appConfig.getVisibility(cms);
548            if (!visibility.isVisible()) {
549                it.remove();
550            }
551        }
552        return result;
553    }
554
555    /**
556     * Returns the user icon helper.<p>
557     *
558     * @return the user icon helper
559     */
560    public CmsUserIconHelper getUserIconHelper() {
561
562        return m_iconHelper;
563    }
564
565    /**
566     * Returns all available workplace apps.<p>
567     *
568     * @return the available workpllace apps
569     */
570    public Collection<I_CmsWorkplaceAppConfiguration> getWorkplaceApps() {
571
572        return m_appsById.values();
573    }
574
575    /**
576     * Returns the additional workplace CSS URIs.<p>
577     *
578     * @return the additional workplace CSS URIs
579     */
580    public Collection<String> getWorkplaceCssUris() {
581
582        return m_workplaceCssUris;
583    }
584
585    /**
586     * Initializes the additional workplace CSS URIs.<p>
587     * They will be taken from the module parameter 'workplace-css' if present in any module.<p>
588     *
589     * @param moduleManager the module manager instance
590     */
591    public void initWorkplaceCssUris(CmsModuleManager moduleManager) {
592
593        Set<String> cssUris = new HashSet<String>();
594        for (CmsModule module : moduleManager.getAllInstalledModules()) {
595            String param = module.getParameter(WORKPLACE_CSS_PARAM);
596            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(param)) {
597                cssUris.add(param);
598            }
599        }
600        File cssFile = new File(
601            OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebApplication(
602                CmsStringUtil.joinPaths("resources", TOOLBAR_CSS)));
603        if (cssFile.exists()) {
604            cssUris.add(TOOLBAR_CSS);
605        }
606        m_workplaceCssUris = Collections.unmodifiableSet(cssUris);
607    }
608
609    /**
610     * Loads the workplace apps.<p>
611     */
612    public void loadApps() {
613
614        m_appsById.clear();
615        m_appCategories = loadCategories();
616        addAppConfigurations(loadDefaultApps());
617        addAppConfigurations(loadAppsUsingServiceLoader());
618        addAppConfigurations(loadLegacyApps());
619    }
620
621    /**
622     * Stores the given app setting within the users additional info.<p>
623     *
624     * @param cms the cms context
625     * @param type the app setting type, used as the settings key
626     * @param appSettings the settings to store
627     */
628    public void storeAppSettings(CmsObject cms, Class<? extends I_CmsAppSettings> type, I_CmsAppSettings appSettings) {
629
630        CmsUser user = cms.getRequestContext().getCurrentUser();
631        CmsUserSettings settings = new CmsUserSettings(user);
632
633        String currentSetting = settings.getAdditionalPreference(type.getName(), true);
634        String state = appSettings.getSettingsString();
635        if (((state == null) && (currentSetting == null)) || ((state != null) && state.equals(currentSetting))) {
636            // nothing changed
637            return;
638        }
639
640        settings.setAdditionalPreference(type.getName(), state);
641        try {
642            settings.save(cms);
643        } catch (CmsException e) {
644            LOG.error("Failed to store workplace app settings for type " + type.getName(), e);
645        }
646    }
647
648    /**
649     * Returns the quick launch apps set for the current user.<p>
650     *
651     * @param cms the cms context
652     *
653     * @return the quick launch app configurations
654     */
655    protected List<I_CmsWorkplaceAppConfiguration> getUserQuickLauchConfigurations(CmsObject cms) {
656
657        String apps_info = (String)cms.getRequestContext().getCurrentUser().getAdditionalInfo(QUICK_LAUCH_APPS_KEY);
658        String[] appIds = null;
659        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(apps_info)) {
660            try {
661                JSONArray ids = new JSONArray(apps_info);
662                appIds = new String[ids.length()];
663                for (int i = 0; i < appIds.length; i++) {
664                    appIds[i] = ids.getString(i);
665                }
666            } catch (JSONException e) {
667                LOG.error("Error parsing user quick launch apps setting.", e);
668                appIds = null;
669            }
670        }
671        return getAppConfigurations(appIds != null ? appIds : DEFAULT_USER_APPS);
672    }
673
674    /**
675     * Writes the user quick launch apps setting to the user additional info.<p>
676     *
677     * @param cms the cms context
678     * @param apps the app ids
679     *
680     * @throws Exception in case writing the user fails
681     */
682    protected void setUserQuickLaunchApps(CmsObject cms, List<String> apps) throws Exception {
683
684        JSONArray appIds = new JSONArray(apps);
685        CmsUser user = cms.getRequestContext().getCurrentUser();
686        String infoValue = appIds.toString();
687        String previousApps = (String)user.getAdditionalInfo(QUICK_LAUCH_APPS_KEY);
688        // remove the additional info value to use default setting, in case the selected apps match the default apps
689        if (new JSONArray(DEFAULT_USER_APPS).toString().equals(infoValue)) {
690            infoValue = null;
691        }
692        // check if the additional info value needs to be changed
693        if ((infoValue == previousApps) || ((infoValue != null) && infoValue.equals(previousApps))) {
694
695            return;
696        }
697        if (infoValue == null) {
698            user.deleteAdditionalInfo(QUICK_LAUCH_APPS_KEY);
699        } else {
700            user.setAdditionalInfo(QUICK_LAUCH_APPS_KEY, infoValue);
701        }
702        cms.writeUser(user);
703    }
704
705    /**
706     * Adds the given app configuration.<p>
707     *
708     * @param appConfigs the app configuration
709     */
710    private void addAppConfigurations(Collection<I_CmsWorkplaceAppConfiguration> appConfigs) {
711
712        for (I_CmsWorkplaceAppConfiguration appConfig : appConfigs) {
713            I_CmsWorkplaceAppConfiguration old = m_appsById.get(appConfig.getId());
714            if ((old == null) || (old.getPriority() < appConfig.getPriority())) {
715                m_appsById.put(appConfig.getId(), appConfig);
716            }
717        }
718    }
719
720    /**
721     * Loads the App Folder.<p>
722     *
723     * @return list of all app folder
724     */
725    private List<I_CmsFolderAppCategory> loadAppFolder() {
726
727        List<I_CmsFolderAppCategory> result = new ArrayList<I_CmsFolderAppCategory>();
728        result.addAll(
729            Arrays.<I_CmsFolderAppCategory> asList(
730                new CmsLinkValidationFolder(),
731                new CmsFileHistoryFolder(),
732                new CmsDbManagerFolder(),
733                new CmsCacheFolder()));
734        return result;
735    }
736
737    /**
738     * Returns the configured apps using the service loader.<p>
739     *
740     * @return tthe configured apps
741     */
742    private List<I_CmsWorkplaceAppConfiguration> loadAppsUsingServiceLoader() {
743
744        List<I_CmsWorkplaceAppConfiguration> appConfigurations = new ArrayList<I_CmsWorkplaceAppConfiguration>();
745        Iterator<I_CmsWorkplaceAppConfiguration> configs = ServiceLoader.load(
746            I_CmsWorkplaceAppConfiguration.class).iterator();
747        while (configs.hasNext()) {
748            try {
749                I_CmsWorkplaceAppConfiguration config = configs.next();
750                appConfigurations.add(config);
751            } catch (Throwable t) {
752                LOG.error("Error loading workplace app configuration from classpath.", t);
753            }
754        }
755        return appConfigurations;
756    }
757
758    /**
759     * Loads the app categories.<p>
760     *
761     * @return the app categories
762     */
763    private Map<String, I_CmsAppCategory> loadCategories() {
764
765        Map<String, I_CmsAppCategory> appCategories = new HashMap<String, I_CmsAppCategory>();
766        CmsAppCategory main = new CmsAppCategory(MAIN_CATEGORY_ID, null, 0, 0);
767        appCategories.put(main.getId(), main);
768        CmsAppCategory admin = new CmsAppCategory(ADMINISTRATION_CATEGORY_ID, null, 5, 0);
769        appCategories.put(admin.getId(), admin);
770        CmsAppCategory legacy = new CmsAppCategory(LEGACY_CATEGORY_ID, null, 10, 0);
771        appCategories.put(legacy.getId(), legacy);
772        List<I_CmsFolderAppCategory> folder = loadAppFolder();
773        for (I_CmsFolderAppCategory appFolder : folder) {
774            appCategories.put(appFolder.getId(), appFolder);
775        }
776        Iterator<I_CmsAppCategory> categoryIt = ServiceLoader.load(I_CmsAppCategory.class).iterator();
777        while (categoryIt.hasNext()) {
778            try {
779                I_CmsAppCategory cat = categoryIt.next();
780                if (!appCategories.containsKey(cat.getId())
781                    || (appCategories.get(cat.getId()).getPriority() < cat.getPriority())) {
782                    appCategories.put(cat.getId(), cat);
783                }
784            } catch (Throwable t) {
785                LOG.error("Error loading workplace app category from classpath.", t);
786            }
787        }
788        return appCategories;
789    }
790
791    /**
792     * Loads the default apps.<p>
793     *
794     * @return the default apps
795     */
796    private Collection<I_CmsWorkplaceAppConfiguration> loadDefaultApps() {
797
798        List<I_CmsWorkplaceAppConfiguration> result = Lists.newArrayList();
799        result.addAll(
800            Arrays.<I_CmsWorkplaceAppConfiguration> asList(
801                new CmsSitemapEditorConfiguration(),
802                new CmsPageEditorConfiguration(),
803                new CmsFileExplorerConfiguration(),
804                new CmsScheduledJobsAppConfig(),
805                new CmsAppHierarchyConfiguration(),
806                new CmsEditorConfiguration(),
807                new CmsQuickLaunchEditorConfiguration(),
808                new CmsProjectManagerConfiguration(),
809                new CmsProjectOverviewConfiguration(),
810                new CmsCacheAdminConfiguration(),
811                new CmsCacheViewFlexConfiguration(),
812                new CmsCacheViewImageConfiguration(),
813                new CmsFileHistoryConfiguration(),
814                new CmsFileHistoryClearConfiguration(),
815                new CmsLinkValidationConfiguration(),
816                new CmsLinkValidationExternalConfiguration(),
817                new CmsLinkInFolderValidationConfiguration(),
818                new CmsDbManagerConfiguration(),
819                new CmsDbImportHTTPConfiguration(),
820                new CmsDbImportServerConfiguration(),
821                new CmsDbExportConfiguration(),
822                new CmsDbStaticExportConfiguration(),
823                new CmsSqlConsoleAppConfiguration(),
824                new CmsDbRemovePubLocksConfiguration(),
825                new CmsDbSynchronizationConfiguration(),
826                new CmsDbPropertiesAppConfiguration(),
827                new CmsSearchindexAppConfiguration(),
828                new CmsLogFileConfiguration(),
829                new CmsSourceSearchAppConfiguration(),
830                new CmsListManagerConfiguration(),
831                new CmsSiteManagerConfiguration(),
832                new CmsPublishQueueConfiguration(),
833                new CmsGitAppConfiguration(),
834                new CmsBroadCastConfigurtion(),
835                new CmsModuleAppConfiguration(),
836                new CmsAccountsAppConfiguration(),
837                new CmsShellAppConfiguration(),
838                new CmsResourceTypeAppConfiguration()));
839
840        return result;
841    }
842
843    /**
844     * Loads the legacy apps.<p>
845     *
846     * @return the legacy apps
847     */
848    private Collection<I_CmsWorkplaceAppConfiguration> loadLegacyApps() {
849
850        List<I_CmsWorkplaceAppConfiguration> configs = new ArrayList<I_CmsWorkplaceAppConfiguration>();
851        // avoid accessing the workplace manager during test case
852        if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_2_INITIALIZING) {
853            List<CmsTool> tools = OpenCms.getWorkplaceManager().getToolManager().getToolHandlers();
854            for (CmsTool tool : tools) {
855
856                I_CmsToolHandler handler = tool.getHandler();
857                String path = handler.getPath();
858
859                // only collecting first path level tools
860                if ((path.length() > 1) && (path.indexOf(CmsToolManager.TOOLPATH_SEPARATOR, 1) < 0)) {
861                    if (!LEGACY_BLACKLIST.contains(path)) {
862                        configs.add(new CmsLegacyAppConfiguration(handler));
863                    }
864                }
865
866            }
867        }
868        return configs;
869    }
870
871}