001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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.gwt;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsGalleryDisabledTypesMode;
032import org.opencms.db.CmsResourceState;
033import org.opencms.db.CmsUserSettings;
034import org.opencms.file.CmsObject;
035import org.opencms.file.CmsProject;
036import org.opencms.file.CmsProperty;
037import org.opencms.file.CmsPropertyDefinition;
038import org.opencms.file.CmsResource;
039import org.opencms.file.CmsResourceFilter;
040import org.opencms.file.CmsUser;
041import org.opencms.file.CmsVfsResourceNotFoundException;
042import org.opencms.flex.CmsFlexController;
043import org.opencms.gwt.shared.CmsBroadcastMessage;
044import org.opencms.gwt.shared.CmsCategoryTreeEntry;
045import org.opencms.gwt.shared.CmsContextMenuEntryBean;
046import org.opencms.gwt.shared.CmsCoreData;
047import org.opencms.gwt.shared.CmsCoreData.AdeContext;
048import org.opencms.gwt.shared.CmsCoreData.UserInfo;
049import org.opencms.gwt.shared.CmsLockInfo;
050import org.opencms.gwt.shared.CmsResourceCategoryInfo;
051import org.opencms.gwt.shared.CmsReturnLinkInfo;
052import org.opencms.gwt.shared.CmsTinyMCEData;
053import org.opencms.gwt.shared.CmsUploadRestrictionInfo;
054import org.opencms.gwt.shared.CmsUserSettingsBean;
055import org.opencms.gwt.shared.CmsValidationQuery;
056import org.opencms.gwt.shared.CmsValidationResult;
057import org.opencms.gwt.shared.rpc.I_CmsCoreService;
058import org.opencms.i18n.CmsMessages;
059import org.opencms.lock.CmsLock;
060import org.opencms.main.CmsBroadcast;
061import org.opencms.main.CmsException;
062import org.opencms.main.CmsIllegalArgumentException;
063import org.opencms.main.CmsLog;
064import org.opencms.main.CmsSessionInfo;
065import org.opencms.main.OpenCms;
066import org.opencms.module.CmsModule;
067import org.opencms.relations.CmsCategory;
068import org.opencms.relations.CmsCategoryService;
069import org.opencms.security.CmsPasswordInfo;
070import org.opencms.security.CmsRole;
071import org.opencms.security.CmsRoleManager;
072import org.opencms.security.CmsSecurityException;
073import org.opencms.site.CmsSite;
074import org.opencms.ui.CmsUserIconHelper;
075import org.opencms.ui.CmsVaadinUtils;
076import org.opencms.ui.I_CmsDialogContext;
077import org.opencms.ui.I_CmsDialogContextWithAdeContext;
078import org.opencms.ui.actions.I_CmsADEAction;
079import org.opencms.ui.apps.A_CmsWorkplaceApp;
080import org.opencms.ui.apps.CmsFileExplorerConfiguration;
081import org.opencms.ui.components.CmsBasicDialog.DialogWidth;
082import org.opencms.ui.contextmenu.CmsContextMenuTreeBuilder;
083import org.opencms.ui.contextmenu.CmsMenuItemVisibilityMode;
084import org.opencms.ui.contextmenu.I_CmsContextMenuItem;
085import org.opencms.ui.dialogs.CmsEmbeddedDialogsUI;
086import org.opencms.util.CmsFileUtil;
087import org.opencms.util.CmsStringUtil;
088import org.opencms.util.CmsTreeNode;
089import org.opencms.util.CmsUUID;
090import org.opencms.workplace.CmsWorkplace;
091import org.opencms.workplace.CmsWorkplaceLoginHandler;
092import org.opencms.workplace.CmsWorkplaceSettings;
093import org.opencms.xml.containerpage.CmsADESessionCache;
094
095import java.util.ArrayList;
096import java.util.Collection;
097import java.util.Collections;
098import java.util.HashMap;
099import java.util.HashSet;
100import java.util.Iterator;
101import java.util.LinkedHashMap;
102import java.util.List;
103import java.util.Locale;
104import java.util.Map;
105import java.util.Map.Entry;
106import java.util.Set;
107import java.util.function.Predicate;
108
109import javax.servlet.http.HttpServletRequest;
110
111import org.apache.commons.collections.Buffer;
112import org.apache.commons.logging.Log;
113
114import com.vaadin.ui.Component;
115import com.vaadin.ui.Window;
116
117/**
118 * Provides general core services.<p>
119 *
120 * @since 8.0.0
121 *
122 * @see org.opencms.gwt.CmsCoreService
123 * @see org.opencms.gwt.shared.rpc.I_CmsCoreService
124 * @see org.opencms.gwt.shared.rpc.I_CmsCoreServiceAsync
125 */
126public class CmsCoreService extends CmsGwtService implements I_CmsCoreService {
127
128    /** The editor back-link URI. */
129    private static final String EDITOR_BACKLINK_URI = "/system/workplace/commons/editor-backlink.html";
130
131    /** The xml-content editor URI. */
132    private static final String EDITOR_URI = "/system/workplace/editors/editor.jsp";
133
134    /** The log instance for this class. */
135    private static final Log LOG = CmsLog.getLog(CmsCoreService.class);
136
137    /** Serialization uid. */
138    private static final long serialVersionUID = 5915848952948986278L;
139
140    /** The workplace settings. */
141    private CmsWorkplaceSettings m_workplaceSettings;
142
143    /**
144     * Builds the tree structure for the given categories.<p>
145     *
146     * @param cms the current cms context
147     * @param categories the categories
148     *
149     * @return the tree root element
150     */
151    public static List<CmsCategoryTreeEntry> buildCategoryTree(CmsObject cms, List<CmsCategory> categories) {
152
153        List<CmsCategoryTreeEntry> result = new ArrayList<CmsCategoryTreeEntry>();
154        CmsUser user = cms.getRequestContext().getCurrentUser();
155        CmsUsedCategoriesList usedCategoriesBean = CmsUsedCategoriesList.fromJson(
156            (String)user.getAdditionalInfo(CmsUsedCategoriesList.ADDINFO_USED_CATEGORIES));
157        Set<String> usedCategories = usedCategoriesBean.getCategories();
158        for (CmsCategory category : categories) {
159            CmsCategoryTreeEntry current = new CmsCategoryTreeEntry(category);
160            current.setUsed(usedCategories.contains(category.getPath()));
161            current.setSitePath(cms.getRequestContext().removeSiteRoot(category.getRootPath()));
162            String parentPath = CmsResource.getParentFolder(current.getPath());
163            CmsCategoryTreeEntry parent = null;
164            parent = findCategory(result, parentPath);
165            if (parent != null) {
166                parent.addChild(current);
167            } else {
168                result.add(current);
169            }
170        }
171        return result;
172    }
173
174    /**
175     * Helper method for getting the category beans for the given site path.<p>
176     *
177     * @param cms the CMS context to use
178     * @param sitePath the site path
179     * @return the list of category beans
180     *
181     * @throws CmsException if something goes wrong
182     */
183    public static List<CmsCategoryTreeEntry> getCategoriesForSitePathStatic(CmsObject cms, String sitePath)
184    throws CmsException {
185
186        return getCategoriesForSitePathStatic(cms, sitePath, null);
187    }
188
189    /**
190     * Helper method for getting the category beans for the given site path.<p>
191     *
192     * @param cms the CMS context to use
193     * @param sitePath the site path
194     * @param localCategoryRepositoryPath the categories for this repository are added separately
195     * @return the list of category beans
196     *
197     * @throws CmsException if something goes wrong
198     */
199    public static List<CmsCategoryTreeEntry> getCategoriesForSitePathStatic(
200        CmsObject cms,
201        String sitePath,
202        String localCategoryRepositoryPath)
203    throws CmsException {
204
205        List<CmsCategoryTreeEntry> result;
206        CmsCategoryService catService = CmsCategoryService.getInstance();
207        List<CmsCategory> categories;
208        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
209        // get the categories
210        if (null == localCategoryRepositoryPath) {
211            categories = catService.readCategories(cms, "", true, sitePath);
212            categories = catService.localizeCategories(cms, categories, wpLocale);
213            result = buildCategoryTree(cms, categories);
214        } else {
215            List<String> repositories = catService.getCategoryRepositories(cms, sitePath);
216            repositories.remove(localCategoryRepositoryPath);
217            categories = catService.readCategoriesForRepositories(cms, "", true, repositories);
218            categories = catService.localizeCategories(cms, categories, wpLocale);
219            result = buildCategoryTree(cms, categories);
220            categories = catService.readCategoriesForRepositories(
221                cms,
222                "",
223                true,
224                Collections.singletonList(localCategoryRepositoryPath));
225            categories = catService.localizeCategories(cms, categories, wpLocale);
226            List<CmsCategoryTreeEntry> localCategories = buildCategoryTree(cms, categories);
227            result.addAll(localCategories);
228        }
229        removeHiddenCategories(cms, result, entry -> false);
230        return result;
231    }
232
233    /**
234     * Returns the context menu entries for the given URI.<p>
235     *
236     * @param cms the cms context
237     * @param structureId the currently requested structure id
238     * @param context the ade context (sitemap or containerpage)
239     * @param params the additional parameters
240     *
241     * @return the context menu entries
242     */
243    public static List<CmsContextMenuEntryBean> getContextMenuEntries(
244        final CmsObject cms,
245        CmsUUID structureId,
246        final AdeContext context,
247        Map<String, String> params) {
248
249        Map<String, CmsContextMenuEntryBean> entries = new LinkedHashMap<String, CmsContextMenuEntryBean>();
250        try {
251            final List<CmsResource> resources;
252            CmsResource resource = cms.readResource(structureId, CmsResourceFilter.ALL.addRequireVisible());
253            // in case of sitemap editor check visibility with empty list
254            if (context.equals(AdeContext.sitemapeditor)) {
255                resources = Collections.emptyList();
256                cms.getRequestContext().setAttribute(I_CmsDialogContext.ATTR_SITEMAP_CONFIG_RESOURCE, resource);
257            } else {
258                resources = Collections.singletonList(resource);
259            }
260            Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
261            final Map<String, String> paramsFinal = params != null ? params : new HashMap<>();
262            // context to check item visibility
263            I_CmsDialogContext dcontext = new I_CmsDialogContextWithAdeContext() {
264
265                public void error(Throwable error) {
266
267                    // not supported
268                }
269
270                public void finish(CmsProject project, String siteRoot) {
271
272                    // not supported
273                }
274
275                public void finish(Collection<CmsUUID> result) {
276
277                    // not supported
278                }
279
280                public void focus(CmsUUID id) {
281
282                    // not supported
283                }
284
285                public AdeContext getAdeContext() {
286
287                    return context;
288                }
289
290                public List<CmsUUID> getAllStructureIdsInView() {
291
292                    return null;
293                }
294
295                public String getAppId() {
296
297                    return context.name();
298                }
299
300                public CmsObject getCms() {
301
302                    return cms;
303                }
304
305                public ContextType getContextType() {
306
307                    ContextType type;
308                    switch (context) {
309                        case pageeditor:
310                        case editprovider:
311                            type = ContextType.containerpageToolbar;
312                            break;
313                        case sitemapeditor:
314                            type = ContextType.sitemapToolbar;
315                            break;
316                        default:
317                            type = ContextType.fileTable;
318                    }
319                    return type;
320                }
321
322                public Map<String, String> getParameters() {
323
324                    return paramsFinal;
325                }
326
327                public List<CmsResource> getResources() {
328
329                    return resources;
330                }
331
332                public void navigateTo(String appId) {
333
334                    // not supported
335                }
336
337                public void onViewChange() {
338
339                    // not supported
340                }
341
342                public void reload() {
343
344                    // not supported
345                }
346
347                public void setWindow(Window window) {
348
349                    // not supported
350                }
351
352                public void start(String title, Component dialog) {
353
354                    // not supported
355                }
356
357                public void start(String title, Component dialog, DialogWidth width) {
358
359                    // not supported
360                }
361
362                public void updateUserInfo() {
363
364                    // not supported
365                }
366            };
367            CmsContextMenuTreeBuilder builder = new CmsContextMenuTreeBuilder(dcontext);
368            List<I_CmsContextMenuItem> items = new ArrayList<I_CmsContextMenuItem>();
369            CmsTreeNode<I_CmsContextMenuItem> root = builder.buildAll(
370                OpenCms.getWorkplaceAppManager().getMenuItemProvider().getMenuItems());
371            for (CmsTreeNode<I_CmsContextMenuItem> child : root.getChildren()) {
372                child.addDataInPreOrder(items);
373            }
374            Map<String, List<CmsContextMenuEntryBean>> submenus = new HashMap<String, List<CmsContextMenuEntryBean>>();
375            for (I_CmsContextMenuItem item : items) {
376                if (!item.isLeafItem()) {
377                    CmsMenuItemVisibilityMode visibility = item.getVisibility(dcontext);
378                    entries.put(
379                        item.getId(),
380                        new CmsContextMenuEntryBean(
381                            visibility.isActive(),
382                            true,
383                            null,
384                            item.getTitle(locale),
385                            null,
386                            CmsStringUtil.isEmptyOrWhitespaceOnly(visibility.getMessageKey())
387                            ? null
388                            : OpenCms.getWorkplaceManager().getMessages(locale).getString(visibility.getMessageKey()),
389                            false,
390                            new ArrayList<CmsContextMenuEntryBean>()));
391
392                } else if ((item instanceof I_CmsADEAction) && ((I_CmsADEAction)item).isAdeSupported()) {
393                    CmsMenuItemVisibilityMode visibility = item.getVisibility(dcontext);
394
395                    String jspPath = ((I_CmsADEAction)item).getJspPath();
396                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(jspPath)) {
397                        jspPath = OpenCms.getLinkManager().substituteLink(cms, jspPath);
398                    }
399                    CmsContextMenuEntryBean itemBean = new CmsContextMenuEntryBean(
400                        visibility.isActive(),
401                        true,
402                        jspPath,
403                        item.getTitle(locale),
404                        ((I_CmsADEAction)item).getCommandClassName(),
405                        CmsStringUtil.isEmptyOrWhitespaceOnly(visibility.getMessageKey())
406                        ? null
407                        : OpenCms.getWorkplaceManager().getMessages(locale).getString(visibility.getMessageKey()),
408                        false,
409                        null);
410                    Map<String, String> clientParams = ((I_CmsADEAction)item).getParams();
411                    if (clientParams != null) {
412                        clientParams = new HashMap<String, String>(clientParams);
413                        for (Entry<String, String> param : clientParams.entrySet()) {
414                            String value = CmsVfsService.prepareFileNameForEditor(cms, resource, param.getValue());
415                            param.setValue(value);
416                        }
417                        itemBean.setParams(clientParams);
418                    }
419                    entries.put(item.getId(), itemBean);
420                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(item.getParentId())) {
421                        List<CmsContextMenuEntryBean> submenu;
422                        if (submenus.containsKey(item.getParentId())) {
423                            submenu = submenus.get(item.getParentId());
424                        } else {
425                            submenu = new ArrayList<CmsContextMenuEntryBean>();
426                            submenus.put(item.getParentId(), submenu);
427                        }
428                        submenu.add(itemBean);
429                    }
430                }
431            }
432            List<CmsContextMenuEntryBean> result = new ArrayList<CmsContextMenuEntryBean>();
433            for (I_CmsContextMenuItem item : items) {
434                if (entries.containsKey(item.getId())) {
435                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(item.getParentId())) {
436                        if (entries.containsKey(item.getParentId())) {
437                            CmsContextMenuEntryBean parent = entries.get(item.getParentId());
438                            if (parent.getSubMenu() != null) {
439                                parent.getSubMenu().add(entries.get(item.getId()));
440                            }
441                        }
442                    } else {
443                        result.add(entries.get(item.getId()));
444                    }
445                }
446            }
447            return result;
448        } catch (CmsException e) {
449            // ignore, the user probably has not enough permissions to read the resource
450            LOG.debug(e.getLocalizedMessage(), e);
451        }
452        return Collections.emptyList();
453    }
454
455    /**
456     * Returns the file explorer link prefix. Append resource site path for complete link.<p>
457     *
458     * @param cms the cms context
459     * @param siteRoot the site root
460     *
461     * @return the file explorer link prefix
462     */
463    public static String getFileExplorerLink(CmsObject cms, String siteRoot) {
464
465        return CmsVaadinUtils.getWorkplaceLink(
466            CmsFileExplorerConfiguration.APP_ID,
467            cms.getRequestContext().getCurrentProject().getUuid()
468                + A_CmsWorkplaceApp.PARAM_SEPARATOR
469                + siteRoot
470                + A_CmsWorkplaceApp.PARAM_SEPARATOR);
471    }
472
473    /**
474     * Returns the workplace link.<p>
475     *
476     * @param cms the cms context
477     * @param structureId the structure id of the current resource
478     *
479     * @return the workplace link
480     */
481    public static String getVaadinWorkplaceLink(CmsObject cms, CmsUUID structureId) {
482
483        String resourceRootFolder = null;
484
485        if (structureId != null) {
486            try {
487                resourceRootFolder = CmsResource.getFolderPath(
488                    cms.readResource(structureId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED).getRootPath());
489            } catch (CmsException e) {
490                LOG.debug("Error reading resource for workplace link.", e);
491            }
492        }
493        if (resourceRootFolder == null) {
494            resourceRootFolder = cms.getRequestContext().getSiteRoot();
495        }
496        return getVaadinWorkplaceLink(cms, resourceRootFolder);
497
498    }
499
500    /**
501     * Returns the workplace link.<p>
502     *
503     * @param cms the cms context
504     * @param resourceRootFolder the resource folder root path
505     *
506     * @return the workplace link
507     */
508    public static String getVaadinWorkplaceLink(CmsObject cms, String resourceRootFolder) {
509
510        CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(resourceRootFolder);
511        String siteRoot = site != null
512        ? site.getSiteRoot()
513        : OpenCms.getSiteManager().startsWithShared(resourceRootFolder)
514        ? OpenCms.getSiteManager().getSharedFolder()
515        : "";
516        String sitePath = resourceRootFolder.substring(siteRoot.length());
517        String link = getFileExplorerLink(cms, siteRoot) + sitePath;
518        return link;
519    }
520
521    /**
522     * Internal helper method for getting a validation service.<p>
523     *
524     * @param name the class name of the validation service
525     *
526     * @return the validation service
527     *
528     * @throws CmsException if something goes wrong
529     */
530    public static I_CmsValidationService getValidationService(String name) throws CmsException {
531
532        try {
533            Class<?> cls = Class.forName(name, false, I_CmsValidationService.class.getClassLoader());
534            if (!I_CmsValidationService.class.isAssignableFrom(cls)) {
535                throw new CmsIllegalArgumentException(
536                    Messages.get().container(Messages.ERR_VALIDATOR_INCORRECT_TYPE_1, name));
537            }
538            return (I_CmsValidationService)cls.newInstance();
539        } catch (ClassNotFoundException e) {
540            throw new CmsException(Messages.get().container(Messages.ERR_VALIDATOR_INSTANTIATION_FAILED_1, name), e);
541        } catch (InstantiationException e) {
542            throw new CmsException(Messages.get().container(Messages.ERR_VALIDATOR_INSTANTIATION_FAILED_1, name), e);
543        } catch (IllegalAccessException e) {
544            throw new CmsException(Messages.get().container(Messages.ERR_VALIDATOR_INSTANTIATION_FAILED_1, name), e);
545        }
546    }
547
548    /**
549     * Instantiates a class given its name using its default constructor.<p>
550     *
551     * Also checks whether the class with the given name is the subclass of another class/interface.<p>
552     *
553     *
554     * @param <T> the type of the interface/class passed as a parameter
555     *
556     * @param anInterface the interface or class against which the class should be checked
557     * @param className the name of the class
558     * @return a new instance of the class
559     *
560     * @throws CmsException if the instantiation fails
561     */
562    public static <T> T instantiate(Class<T> anInterface, String className) throws CmsException {
563
564        try {
565            Class<?> cls = Class.forName(className, false, anInterface.getClassLoader());
566            if (!anInterface.isAssignableFrom(cls)) {
567                // class was found, but does not implement the interface
568                throw new CmsIllegalArgumentException(
569                    Messages.get().container(
570                        Messages.ERR_INSTANTIATION_INCORRECT_TYPE_2,
571                        className,
572                        anInterface.getName()));
573            }
574
575            // we use another variable so we don't have to put the @SuppressWarnings on the method itself
576            @SuppressWarnings("unchecked")
577            Class<T> typedClass = (Class<T>)cls;
578            return typedClass.newInstance();
579        } catch (ClassNotFoundException e) {
580            throw new CmsException(Messages.get().container(Messages.ERR_INSTANTIATION_FAILED_1, className), e);
581        } catch (InstantiationException e) {
582            throw new CmsException(Messages.get().container(Messages.ERR_INSTANTIATION_FAILED_1, className), e);
583        } catch (IllegalAccessException e) {
584            throw new CmsException(Messages.get().container(Messages.ERR_INSTANTIATION_FAILED_1, className), e);
585        }
586    }
587
588    /**
589     * Implementation method for getting the link for a given return code.<p>
590     *
591     * @param cms the CMS context
592     * @param returnCode the return code
593     * @return the link for the return code
594     *
595     * @throws CmsException if something goes wrong
596     */
597    public static CmsReturnLinkInfo internalGetLinkForReturnCode(CmsObject cms, String returnCode) throws CmsException {
598
599        if (CmsUUID.isValidUUID(returnCode)) {
600            try {
601                CmsResource pageRes = cms.readResource(new CmsUUID(returnCode), CmsResourceFilter.IGNORE_EXPIRATION);
602                return new CmsReturnLinkInfo(
603                    OpenCms.getLinkManager().substituteLink(cms, pageRes),
604                    CmsReturnLinkInfo.Status.ok);
605            } catch (CmsVfsResourceNotFoundException e) {
606                LOG.debug(e.getLocalizedMessage(), e);
607                return new CmsReturnLinkInfo(null, CmsReturnLinkInfo.Status.notfound);
608            }
609        } else {
610            int colonIndex = returnCode.indexOf(':');
611            if (colonIndex >= 0) {
612                String before = returnCode.substring(0, colonIndex);
613                String after = returnCode.substring(colonIndex + 1);
614
615                if (CmsUUID.isValidUUID(before) && CmsUUID.isValidUUID(after)) {
616                    try {
617                        CmsUUID pageId = new CmsUUID(before);
618                        CmsUUID detailId = new CmsUUID(after);
619                        CmsResource pageRes = cms.readResource(pageId);
620                        CmsResource folder = pageRes.isFolder()
621                        ? pageRes
622                        : cms.readParentFolder(pageRes.getStructureId());
623                        String pageLink = OpenCms.getLinkManager().substituteLink(cms, folder);
624                        CmsResource detailRes = cms.readResource(detailId);
625                        String detailName = cms.getDetailName(
626                            detailRes,
627                            cms.getRequestContext().getLocale(),
628                            OpenCms.getLocaleManager().getDefaultLocales());
629                        String link = CmsFileUtil.removeTrailingSeparator(pageLink) + "/" + detailName;
630                        return new CmsReturnLinkInfo(link, CmsReturnLinkInfo.Status.ok);
631                    } catch (CmsVfsResourceNotFoundException e) {
632                        LOG.debug(e.getLocalizedMessage(), e);
633                        return new CmsReturnLinkInfo(null, CmsReturnLinkInfo.Status.notfound);
634
635                    }
636                }
637            }
638            throw new IllegalArgumentException("return code has wrong format");
639        }
640    }
641
642    /**
643     * Fetches the core data.<p>
644     *
645     * @param request the current request
646     *
647     * @return the core data
648     */
649    public static CmsCoreData prefetch(HttpServletRequest request) {
650
651        CmsCoreService srv = new CmsCoreService();
652        srv.setCms(CmsFlexController.getCmsObject(request));
653        srv.setRequest(request);
654        CmsCoreData result = null;
655        try {
656            result = srv.prefetch();
657        } finally {
658            srv.clearThreadStorage();
659        }
660        return result;
661    }
662
663    /**
664     * Recursively checks forced visibility for the entry and all its subentries.
665     *
666     * <p>A category is considered to have forced visibility if any of its subcategories match 'selectedCheck'.
667     *
668     * @param entry the category tree entry to check
669     * @param selectedCheck a predicate that checks whether the parents of a category tree entry should be forced to be visible
670     * @return
671     */
672    private static boolean checkForcedVisibility(
673        CmsCategoryTreeEntry entry,
674        Predicate<CmsCategoryTreeEntry> selectedCheck) {
675
676        if (entry.getForcedVisible() == null) {
677            boolean forcedVisible = selectedCheck.test(entry);
678            for (CmsCategoryTreeEntry child : entry.getChildren()) {
679                forcedVisible |= checkForcedVisibility(child, selectedCheck);
680                // don't break out of the loop, we need to call checkForcedVisibility on everything
681            }
682            entry.setForcedVisible(Boolean.valueOf(forcedVisible));
683        }
684        return entry.getForcedVisible().booleanValue();
685    }
686
687    /**
688     * FInds a category in the given tree.<p>
689     *
690     * @param tree the the tree to search in
691     * @param path the path to search for
692     *
693     * @return the category with the given path or <code>null</code> if not found
694     */
695    private static CmsCategoryTreeEntry findCategory(List<CmsCategoryTreeEntry> tree, String path) {
696
697        if (path == null) {
698            return null;
699        }
700        // we assume that the category to find is descendant of tree
701        List<CmsCategoryTreeEntry> children = tree;
702        boolean found = true;
703        while (found) {
704            if (children == null) {
705                return null;
706            }
707            // since the categories are sorted it is faster to go backwards
708            found = false;
709            for (int i = children.size() - 1; i >= 0; i--) {
710                CmsCategoryTreeEntry child = children.get(i);
711                if (path.equals(child.getPath())) {
712                    return child;
713                }
714                if (path.startsWith(child.getPath())) {
715                    children = child.getChildren();
716                    found = true;
717                    break;
718                }
719            }
720        }
721        return null;
722    }
723
724    /**
725     * Removes hidden category tree entries.
726     *
727     *  <p>A category entry is considered hidden if one of its ancestors has the 'category.hidden' property with a value of 'true', and none of its subcategories have a structure id that
728     *  is in 'selected'.
729     *
730     * @param cms the current CMS context
731     * @param entries the entries to filter
732     * @param selected the set of structure ids of categories whose ancestors should not be filtered (usually a set of categories already assigned to a resource)
733     */
734    private static void removeHiddenCategories(
735        CmsObject cms,
736        List<CmsCategoryTreeEntry> entries,
737        Predicate<CmsCategoryTreeEntry> selectedCheck) {
738
739        Iterator<CmsCategoryTreeEntry> iter = entries.iterator();
740        while (iter.hasNext()) {
741            CmsCategoryTreeEntry entry = iter.next();
742            if (checkForcedVisibility(entry, selectedCheck)) {
743                // this node is forced visible by one of the descendants, but there could still be other hidden children
744                removeHiddenCategories(cms, entry.getChildren(), selectedCheck);
745            } else {
746                boolean hidden = false;
747                try {
748                    CmsResource resource = cms.readResource(entry.getId(), CmsResourceFilter.IGNORE_EXPIRATION);
749                    CmsProperty hiddenProp = cms.readPropertyObject(
750                        resource,
751                        CmsPropertyDefinition.PROPERTY_CATEGORY_HIDDEN,
752                        true);
753                    hidden = Boolean.parseBoolean(hiddenProp.getValue());
754                } catch (CmsVfsResourceNotFoundException | CmsSecurityException e) {
755                    LOG.debug(e.getLocalizedMessage(), e);
756                } catch (Exception e) {
757                    LOG.error(e.getLocalizedMessage(), e);
758                }
759                if (hidden) {
760                    iter.remove();
761                } else {
762                    removeHiddenCategories(cms, entry.getChildren(), selectedCheck);
763                }
764
765            }
766        }
767    }
768
769    /**
770     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#changePassword(java.lang.String, java.lang.String, java.lang.String)
771     */
772    public String changePassword(String oldPassword, String newPassword, String newPasswordConfirm)
773    throws CmsRpcException {
774
775        CmsObject cms = getCmsObject();
776        CmsPasswordInfo passwordBean = new CmsPasswordInfo(cms);
777        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
778        try {
779            passwordBean.setCurrentPwd(oldPassword);
780            passwordBean.setNewPwd(newPassword);
781            passwordBean.setConfirmation(newPasswordConfirm);
782            passwordBean.applyChanges();
783            return null;
784        } catch (CmsSecurityException e) {
785            LOG.error(e.getLocalizedMessage(), e);
786            return e.getMessageContainer().key(wpLocale);
787        } catch (CmsIllegalArgumentException e) {
788            LOG.warn(e.getLocalizedMessage(), e);
789            return e.getMessageContainer().key(wpLocale);
790        } catch (Exception e) {
791            error(e);
792            return null; // will never be executed
793        }
794    }
795
796    /**
797     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#createUUID()
798     */
799    public CmsUUID createUUID() {
800
801        return new CmsUUID();
802    }
803
804    /**
805     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getBroadcast()
806     */
807    @SuppressWarnings("unchecked")
808    public List<CmsBroadcastMessage> getBroadcast() {
809
810        setBroadcastPoll();
811        CmsObject cms = getCmsObject();
812        Set<CmsBroadcast> repeatedBroadcasts = new HashSet<CmsBroadcast>();
813        OpenCms.getWorkplaceManager().checkWorkplaceRequest(getRequest(), getCmsObject());
814        CmsSessionInfo sessionInfo = OpenCms.getSessionManager().getSessionInfo(getRequest().getSession());
815        if (sessionInfo == null) {
816            return null;
817        }
818        String sessionId = sessionInfo.getSessionId().toString();
819        Buffer messageQueue = OpenCms.getSessionManager().getBroadcastQueue(sessionId);
820        if (!messageQueue.isEmpty()) {
821            CmsMessages messages = org.opencms.workplace.Messages.get().getBundle(
822                OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject()));
823            List<CmsBroadcastMessage> result = new ArrayList<CmsBroadcastMessage>();
824            // the user has pending messages, display them all
825            while (!messageQueue.isEmpty()) {
826
827                CmsBroadcast broadcastMessage = (CmsBroadcast)messageQueue.remove();
828                if ((broadcastMessage.getLastDisplay()
829                    + CmsBroadcast.DISPLAY_AGAIN_TIME) < System.currentTimeMillis()) {
830                    CmsUserIconHelper helper = OpenCms.getWorkplaceAppManager().getUserIconHelper();
831                    String picPath = "";
832                    if (broadcastMessage.getUser() != null) {
833                        picPath = helper.getSmallIconPath(getCmsObject(), broadcastMessage.getUser());
834                    }
835                    CmsBroadcastMessage message = new CmsBroadcastMessage(
836                        broadcastMessage.getUser() != null
837                        ? broadcastMessage.getUser().getName()
838                        : messages.key(org.opencms.workplace.Messages.GUI_LABEL_BROADCAST_FROM_SYSTEM_0),
839                        picPath,
840                        messages.getDateTime(broadcastMessage.getSendTime()),
841                        broadcastMessage.getMessage());
842                    result.add(message);
843                    if (broadcastMessage.isRepeat()) {
844                        repeatedBroadcasts.add(broadcastMessage.withLastDisplay(System.currentTimeMillis()));
845                    }
846                } else {
847                    repeatedBroadcasts.add(broadcastMessage);
848                }
849            }
850            if (!repeatedBroadcasts.isEmpty()) {
851                for (CmsBroadcast broadcast : repeatedBroadcasts) {
852                    messageQueue.add(broadcast);
853                }
854            }
855            return result;
856        }
857        // no message pending, return null
858        return null;
859    }
860
861    /**
862     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getCategories(java.lang.String, boolean, java.lang.String, boolean)
863     */
864    public List<CmsCategoryTreeEntry> getCategories(
865        String fromPath,
866        boolean includeSubCats,
867        String refPath,
868        boolean showWithRepositories,
869        Set<String> selected)
870    throws CmsRpcException {
871
872        CmsObject cms = getCmsObject();
873        Set<CmsUUID> selectedIds = new HashSet<>();
874        for (String path : selected) {
875            try {
876                CmsResource catResource = cms.readResource(path, CmsResourceFilter.IGNORE_EXPIRATION);
877                selectedIds.add(catResource.getStructureId());
878            } catch (CmsVfsResourceNotFoundException | CmsSecurityException e) {
879                LOG.debug(e.getLocalizedMessage(), e);
880            } catch (Exception e) {
881                LOG.error(e.getLocalizedMessage(), e);
882            }
883
884        }
885        return getCategoriesInternal(
886            fromPath,
887            includeSubCats,
888            refPath,
889            showWithRepositories,
890            entry -> selectedIds.contains(entry.getId()));
891    }
892
893    /**
894     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getCategoriesForSitePath(java.lang.String)
895     */
896    public List<CmsCategoryTreeEntry> getCategoriesForSitePath(String sitePath) throws CmsRpcException {
897
898        List<CmsCategoryTreeEntry> result = null;
899        CmsObject cms = getCmsObject();
900        try {
901            result = getCategoriesForSitePathStatic(cms, sitePath);
902        } catch (Throwable e) {
903            error(e);
904        }
905        return result;
906    }
907
908    /**
909     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getCategoryInfo(org.opencms.util.CmsUUID)
910     */
911    public CmsResourceCategoryInfo getCategoryInfo(CmsUUID structureId) throws CmsRpcException {
912
913        CmsObject cms = getCmsObject();
914        CmsCategoryService catService = CmsCategoryService.getInstance();
915        try {
916            CmsResource resource = cms.readResource(structureId, CmsResourceFilter.ignoreExpirationOffline(cms));
917            List<CmsCategory> categories = catService.readResourceCategories(cms, resource);
918            List<String> currentCategories = new ArrayList<String>();
919            Set<CmsUUID> selected = new HashSet<>();
920
921            for (CmsCategory category : categories) {
922                currentCategories.add(category.getPath());
923                selected.add(category.getId());
924            }
925            return new CmsResourceCategoryInfo(
926                structureId,
927                CmsVfsService.getPageInfoWithLock(cms, resource),
928                currentCategories,
929                getCategoriesInternal(
930                    null,
931                    true,
932                    cms.getSitePath(resource),
933                    OpenCms.getWorkplaceManager().isDisplayCategoriesByRepository(),
934                    entry -> selected.contains(entry.getId())));
935        } catch (CmsException e) {
936            error(e);
937        }
938        return null;
939    }
940
941    /**
942     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getContextMenuEntries(org.opencms.util.CmsUUID, org.opencms.gwt.shared.CmsCoreData.AdeContext)
943     */
944    public List<CmsContextMenuEntryBean> getContextMenuEntries(CmsUUID structureId, AdeContext context)
945    throws CmsRpcException {
946
947        List<CmsContextMenuEntryBean> result = null;
948        try {
949            result = getContextMenuEntries(getCmsObject(), structureId, context, new HashMap<>());
950        } catch (Throwable e) {
951            error(e);
952        }
953        return result;
954    }
955
956    /**
957     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getContextMenuEntries(org.opencms.util.CmsUUID, org.opencms.gwt.shared.CmsCoreData.AdeContext)
958     */
959    public List<CmsContextMenuEntryBean> getContextMenuEntries(
960        CmsUUID structureId,
961        AdeContext context,
962        Map<String, String> params)
963    throws CmsRpcException {
964
965        List<CmsContextMenuEntryBean> result = null;
966        try {
967            result = getContextMenuEntries(getCmsObject(), structureId, context, params);
968        } catch (Throwable e) {
969            error(e);
970        }
971        return result;
972    }
973
974    /**
975     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getLinkForReturnCode(java.lang.String)
976     */
977    public CmsReturnLinkInfo getLinkForReturnCode(String returnCode) throws CmsRpcException {
978
979        try {
980            return internalGetLinkForReturnCode(getCmsObject(), returnCode);
981        } catch (Throwable e) {
982            error(e);
983            return null;
984
985        }
986    }
987
988    /**
989     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getResourceState(org.opencms.util.CmsUUID)
990     */
991    public CmsResourceState getResourceState(CmsUUID structureId) throws CmsRpcException {
992
993        CmsObject cms = getCmsObject();
994        CmsResourceState result = null;
995        try {
996            try {
997                CmsResource res = cms.readResource(structureId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
998                result = res.getState();
999            } catch (CmsVfsResourceNotFoundException e) {
1000                LOG.debug(e.getLocalizedMessage(), e);
1001                result = CmsResourceState.STATE_DELETED;
1002            }
1003        } catch (CmsException e) {
1004            error(e);
1005        }
1006        return result;
1007    }
1008
1009    /**
1010     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getUniqueFileName(java.lang.String, java.lang.String)
1011     */
1012    public String getUniqueFileName(String parentFolder, String baseName) {
1013
1014        return OpenCms.getResourceManager().getNameGenerator().getUniqueFileName(
1015            getCmsObject(),
1016            parentFolder,
1017            baseName);
1018    }
1019
1020    /**
1021     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getUserInfo()
1022     */
1023    public UserInfo getUserInfo() {
1024
1025        CmsObject cms = getCmsObject();
1026        CmsRoleManager roleManager = OpenCms.getRoleManager();
1027        boolean isAdmin = roleManager.hasRole(cms, CmsRole.ADMINISTRATOR);
1028        boolean isDeveloper = roleManager.hasRole(cms, CmsRole.DEVELOPER);
1029        boolean isCategoryManager = roleManager.hasRole(cms, CmsRole.CATEGORY_EDITOR);
1030        boolean isWorkplaceUser = roleManager.hasRole(cms, CmsRole.WORKPLACE_USER);
1031        UserInfo userInfo = new UserInfo(
1032            cms.getRequestContext().getCurrentUser().getName(),
1033            OpenCms.getWorkplaceAppManager().getUserIconHelper().getSmallIconPath(
1034                cms,
1035                cms.getRequestContext().getCurrentUser()),
1036            isAdmin,
1037            isDeveloper,
1038            isCategoryManager,
1039            isWorkplaceUser,
1040            cms.getRequestContext().getCurrentUser().isManaged());
1041        return userInfo;
1042    }
1043
1044    /**
1045     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getWorkplaceLink(org.opencms.util.CmsUUID)
1046     */
1047    public String getWorkplaceLink(CmsUUID structureId) throws CmsRpcException {
1048
1049        String result = null;
1050        CmsObject cms = getCmsObject();
1051        try {
1052            String resourceRootFolder = structureId != null
1053            ? CmsResource.getFolderPath(
1054                cms.readResource(structureId, CmsResourceFilter.ALL.addRequireVisible()).getRootPath())
1055            : cms.getRequestContext().getSiteRoot();
1056            result = getVaadinWorkplaceLink(cms, resourceRootFolder);
1057        } catch (Exception e) {
1058            error(e);
1059        }
1060        return result;
1061    }
1062
1063    /**
1064     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#getWorkplaceLinkForPath(java.lang.String)
1065     */
1066    public String getWorkplaceLinkForPath(String path) throws CmsRpcException {
1067
1068        CmsObject cms = getCmsObject();
1069        try {
1070            CmsObject workCms = cms;
1071            if (path.startsWith("/sites/")) {
1072                workCms = OpenCms.initCmsObject(cms);
1073                workCms.getRequestContext().setSiteRoot("");
1074            }
1075            String currentPath = CmsResource.getParentFolder(path);
1076            CmsResource folder = null;
1077            try {
1078                folder = workCms.readResource(currentPath, CmsResourceFilter.IGNORE_EXPIRATION.addRequireVisible());
1079            } catch (CmsVfsResourceNotFoundException | CmsSecurityException e) {
1080                throw new CmsException(Messages.get().container(Messages.ERR_COULD_NOT_FIND_PARENT_FOLDER_1, path), e);
1081            }
1082            return getVaadinWorkplaceLink(cms, folder.getRootPath());
1083        } catch (Exception e) {
1084            error(e);
1085            return null;
1086        }
1087    }
1088
1089    /**
1090     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#loadUploadRestrictionInfo()
1091     */
1092    @Override
1093    public CmsUploadRestrictionInfo loadUploadRestrictionInfo() throws CmsRpcException {
1094
1095        CmsObject cms = getCmsObject();
1096        CmsUploadRestrictionInfo result = OpenCms.getWorkplaceManager().getUploadRestriction().getUploadRestrictionInfo(
1097            cms);
1098        return result;
1099
1100    }
1101
1102    /**
1103     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#loadUserSettings()
1104     */
1105    public CmsUserSettingsBean loadUserSettings() throws CmsRpcException {
1106
1107        CmsObject cms = getCmsObject();
1108        CmsClientUserSettingConverter converter = new CmsClientUserSettingConverter(cms, getRequest(), getResponse());
1109        try {
1110            return converter.loadSettings();
1111        } catch (Exception e) {
1112            error(e);
1113            return null;
1114        }
1115    }
1116
1117    /**
1118     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#lockIfExists(java.lang.String)
1119     */
1120    public String lockIfExists(String sitePath) throws CmsRpcException {
1121
1122        CmsObject cms = getCmsObject();
1123        String errorMessage = null;
1124        try {
1125            if (cms.existsResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION)) {
1126
1127                try {
1128                    ensureLock(cms.readResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION));
1129                } catch (CmsException e) {
1130                    errorMessage = e.getLocalizedMessage(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1131                }
1132
1133            } else {
1134                // check if parent folder may be locked by the current user
1135                String parentFolder = CmsResource.getParentFolder(sitePath);
1136                while ((parentFolder != null)
1137                    && !cms.existsResource(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
1138                    parentFolder = CmsResource.getParentFolder(parentFolder);
1139                }
1140                if (parentFolder != null) {
1141                    CmsResource ancestorFolder = cms.readResource(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION);
1142                    CmsUser user = cms.getRequestContext().getCurrentUser();
1143                    CmsLock lock = cms.getLock(ancestorFolder);
1144                    if (!lock.isLockableBy(user)) {
1145                        errorMessage = "Can not lock parent folder '" + parentFolder + "'.";
1146                    }
1147                } else {
1148                    errorMessage = "Can not access any parent folder.";
1149                }
1150            }
1151        } catch (Throwable e) {
1152            error(e);
1153        }
1154
1155        return errorMessage;
1156    }
1157
1158    /**
1159     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#lockIfExists(java.lang.String, long)
1160     */
1161    public String lockIfExists(String sitePath, long loadTime) throws CmsRpcException {
1162
1163        CmsObject cms = getCmsObject();
1164        String errorMessage = null;
1165        try {
1166            if (cms.existsResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION)) {
1167
1168                try {
1169                    CmsResource resource = cms.readResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION);
1170                    if (resource.getDateLastModified() > loadTime) {
1171                        // the resource has been changed since it was loaded
1172                        CmsUser user = null;
1173                        try {
1174                            user = cms.readUser(resource.getUserLastModified());
1175                        } catch (CmsException e) {
1176                            // ignore
1177                        }
1178                        CmsMessages messages = Messages.get().getBundle(
1179                            OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1180                        return user != null
1181                        ? messages.key(
1182                            Messages.ERR_LOCKING_MODIFIED_RESOURCE_2,
1183                            resource.getRootPath(),
1184                            user.getFullName())
1185                        : messages.key(Messages.ERR_LOCKING_MODIFIED_RESOURCE_1, resource.getRootPath());
1186                    }
1187                    ensureLock(resource);
1188                } catch (CmsException e) {
1189                    errorMessage = e.getLocalizedMessage(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1190                }
1191
1192            } else {
1193                // check if parent folder may be locked by the current user
1194                String parentFolder = CmsResource.getParentFolder(sitePath);
1195                while ((parentFolder != null)
1196                    && !cms.existsResource(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
1197                    parentFolder = CmsResource.getParentFolder(parentFolder);
1198                }
1199                if (parentFolder != null) {
1200                    CmsResource ancestorFolder = cms.readResource(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION);
1201                    CmsUser user = cms.getRequestContext().getCurrentUser();
1202                    CmsLock lock = cms.getLock(ancestorFolder);
1203                    if (!lock.isLockableBy(user)) {
1204                        errorMessage = "Can not lock parent folder '" + parentFolder + "'.";
1205                    }
1206                } else {
1207                    errorMessage = "Can not access any parent folder.";
1208                }
1209            }
1210        } catch (Throwable e) {
1211            error(e);
1212        }
1213
1214        return errorMessage;
1215    }
1216
1217    /**
1218     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#lockTemp(org.opencms.util.CmsUUID)
1219     */
1220    public String lockTemp(CmsUUID structureId) throws CmsRpcException {
1221
1222        CmsObject cms = getCmsObject();
1223        try {
1224            try {
1225                ensureLock(structureId);
1226            } catch (CmsException e) {
1227                return e.getLocalizedMessage(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1228            }
1229        } catch (Throwable e) {
1230            error(e);
1231        }
1232        return null;
1233    }
1234
1235    /**
1236     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#lockTemp(org.opencms.util.CmsUUID, long)
1237     */
1238    public String lockTemp(CmsUUID structureId, long loadTime) throws CmsRpcException {
1239
1240        CmsObject cms = getCmsObject();
1241        try {
1242            try {
1243                CmsResource resource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
1244                if (resource.getDateLastModified() > loadTime) {
1245                    // the resource has been changed since it was loaded
1246                    CmsUser user = null;
1247                    try {
1248                        user = cms.readUser(resource.getUserLastModified());
1249                    } catch (CmsException e) {
1250                        // ignore
1251                    }
1252                    CmsMessages messages = Messages.get().getBundle(
1253                        OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1254                    return user != null
1255                    ? messages.key(Messages.ERR_LOCKING_MODIFIED_RESOURCE_2, resource.getRootPath(), user.getFullName())
1256                    : messages.key(Messages.ERR_LOCKING_MODIFIED_RESOURCE_1, resource.getRootPath());
1257                }
1258                ensureLock(resource);
1259            } catch (CmsException e) {
1260                return e.getLocalizedMessage(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1261            }
1262        } catch (Throwable e) {
1263            error(e);
1264        }
1265        return null;
1266    }
1267
1268    /**
1269     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#prefetch()
1270     */
1271    public CmsCoreData prefetch() {
1272
1273        CmsObject cms = getCmsObject();
1274        String navigationUri = cms.getRequestContext().getUri();
1275        CmsADEConfigData sitemapConfig = OpenCms.getADEManager().lookupConfigurationWithCache(
1276            cms,
1277            cms.getRequestContext().getRootUri());
1278        boolean toolbarVisible = CmsADESessionCache.getCache(getRequest(), getCmsObject()).isToolbarVisible();
1279        boolean isShowHelp = OpenCms.getADEManager().isShowEditorHelp(cms);
1280
1281        CmsUUID structureId = null;
1282
1283        try {
1284            CmsResource requestedResource = cms.readResource(
1285                cms.getRequestContext().getUri(),
1286                CmsResourceFilter.ignoreExpirationOffline(cms));
1287            structureId = requestedResource.getStructureId();
1288        } catch (CmsException e) {
1289            // may happen in case of VAADIN UI
1290            LOG.debug("Could not read resource for URI.", e);
1291            structureId = CmsUUID.getNullUUID();
1292        }
1293        String loginUrl = CmsWorkplaceLoginHandler.LOGIN_FORM;
1294        try {
1295            loginUrl = cms.readPropertyObject(
1296                cms.getRequestContext().getUri(),
1297                CmsPropertyDefinition.PROPERTY_LOGIN_FORM,
1298                true).getValue(loginUrl);
1299        } catch (CmsException e) {
1300            log(e.getLocalizedMessage(), e);
1301        }
1302        String defaultWorkplaceLink = OpenCms.getSystemInfo().getWorkplaceContext();
1303        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
1304        UserInfo userInfo = getUserInfo();
1305        String aboutLink = OpenCms.getLinkManager().substituteLink(
1306            getCmsObject(),
1307            "/system/workplace/commons/about.jsp");
1308        String tinyMCE = CmsWorkplace.getStaticResourceUri("/editors/tinymce/jscripts/tinymce/tinymce.min.js");
1309        boolean uploadDisabled = OpenCms.getWorkplaceManager().isAdeGalleryUploadDisabled(cms);
1310        CmsUploadRestrictionInfo uploadRestrictionInfo = OpenCms.getWorkplaceManager().getUploadRestriction().getUploadRestrictionInfo(
1311            cms);
1312        String categoryBaseFolder = CmsCategoryService.getInstance().getRepositoryBaseFolderName(cms);
1313        CmsGalleryDisabledTypesMode disabledTypesMode = sitemapConfig.getDisabledTypeMode(
1314            CmsGalleryDisabledTypesMode.mark);
1315        boolean hideDisabledTypes = disabledTypesMode == CmsGalleryDisabledTypesMode.hide;
1316        getWorkplaceSettings().getUserSettings();
1317        String checkReuseWarning = CmsUserSettings.getAdditionalPreference(cms, "checkReuseWarning", true);
1318        boolean warnWhenEditingReusedElement = Boolean.parseBoolean(checkReuseWarning);
1319
1320        CmsCoreData data = new CmsCoreData(
1321            EDITOR_URI,
1322            EDITOR_BACKLINK_URI,
1323            loginUrl,
1324            OpenCms.getStaticExportManager().getVfsPrefix(),
1325            getFileExplorerLink(cms, cms.getRequestContext().getSiteRoot()),
1326            OpenCms.getSystemInfo().getStaticResourceContext(),
1327            CmsEmbeddedDialogsUI.getEmbeddedDialogsContextPath(),
1328            cms.getRequestContext().getSiteRoot(),
1329            OpenCms.getSiteManager().getSharedFolder(),
1330            cms.getRequestContext().getCurrentProject().getId(),
1331            cms.getRequestContext().getLocale().toString(),
1332            wpLocale.toString(),
1333            cms.getRequestContext().getUri(),
1334            navigationUri,
1335            structureId,
1336            new HashMap<String, String>(OpenCms.getResourceManager().getExtensionMapping()),
1337            CmsIconUtil.getExtensionIconMapping(),
1338            System.currentTimeMillis(),
1339            isShowHelp,
1340            toolbarVisible,
1341            defaultWorkplaceLink,
1342            aboutLink,
1343            userInfo,
1344            OpenCms.getWorkplaceManager().getFileBytesMaxUploadSize(getCmsObject()),
1345            OpenCms.getWorkplaceManager().isKeepAlive(),
1346            uploadDisabled,
1347            OpenCms.getADEManager().getParameters(getCmsObject()),
1348            uploadRestrictionInfo,
1349            categoryBaseFolder,
1350            hideDisabledTypes,
1351            warnWhenEditingReusedElement);
1352        CmsTinyMCEData tinyMCEData = new CmsTinyMCEData();
1353        tinyMCEData.setLink(tinyMCE);
1354        data.setTinymce(tinyMCEData);
1355        data.setMaxLocaleButtons(OpenCms.getWorkplaceManager().getEditorMaxLocaleButtons());
1356        return data;
1357    }
1358
1359    /**
1360     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#saveUsedCategory(java.lang.String)
1361     */
1362    @Override
1363    public void saveUsedCategory(String category) throws CmsRpcException {
1364
1365        try {
1366            CmsObject cms = getCmsObject();
1367            CmsUsedCategoriesList.addUsedCategoryForCurrentUser(cms, category);
1368        } catch (Exception e) {
1369            LOG.error(e.getLocalizedMessage(), e);
1370            error(e);
1371        }
1372    }
1373
1374    /**
1375     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#saveUserSettings(java.util.Map, java.util.Set)
1376     */
1377    public void saveUserSettings(Map<String, String> userSettings, Set<String> edited) throws CmsRpcException {
1378
1379        try {
1380            CmsObject cms = getCmsObject();
1381            CmsClientUserSettingConverter converter = new CmsClientUserSettingConverter(
1382                cms,
1383                getRequest(),
1384                getResponse());
1385            userSettings.keySet().retainAll(edited);
1386            converter.saveSettings(userSettings);
1387        } catch (Exception e) {
1388            error(e);
1389        }
1390    }
1391
1392    /**
1393     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#setResourceCategories(org.opencms.util.CmsUUID, java.util.List)
1394     */
1395    public void setResourceCategories(CmsUUID structureId, List<String> categories) throws CmsRpcException {
1396
1397        CmsObject cms = getCmsObject();
1398        CmsCategoryService catService = CmsCategoryService.getInstance();
1399        try {
1400            CmsResource resource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
1401            ensureLock(resource);
1402            String sitePath = cms.getSitePath(resource);
1403            List<CmsCategory> previousCategories = catService.readResourceCategories(cms, resource);
1404            for (CmsCategory category : previousCategories) {
1405                if (categories.contains(category.getPath())) {
1406                    categories.remove(category.getPath());
1407                } else {
1408                    catService.removeResourceFromCategory(cms, sitePath, category);
1409                }
1410            }
1411            for (String path : categories) {
1412                if (!path.isEmpty()) { // Prevent adding category repositories itself.
1413                    catService.addResourceToCategory(cms, sitePath, path);
1414                }
1415            }
1416            tryUnlock(resource);
1417        } catch (Throwable t) {
1418            error(t);
1419        }
1420    }
1421
1422    /**
1423     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#setShowEditorHelp(boolean)
1424     */
1425    public void setShowEditorHelp(boolean visible) throws CmsRpcException {
1426
1427        try {
1428            OpenCms.getADEManager().setShowEditorHelp(getCmsObject(), visible);
1429        } catch (Throwable e) {
1430            error(e);
1431        }
1432    }
1433
1434    /**
1435     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#setToolbarVisible(boolean)
1436     */
1437    public void setToolbarVisible(boolean visible) throws CmsRpcException {
1438
1439        try {
1440            ensureSession();
1441            CmsADESessionCache.getCache(getRequest(), getCmsObject()).setToolbarVisible(visible);
1442        } catch (Throwable e) {
1443            error(e);
1444        }
1445    }
1446
1447    /**
1448     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#unlock(org.opencms.util.CmsUUID)
1449     */
1450    public String unlock(CmsUUID structureId) throws CmsRpcException {
1451
1452        CmsObject cms = getCmsObject();
1453        try {
1454            CmsResource resource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
1455            tryUnlock(resource);
1456        } catch (CmsException e) {
1457            return e.getLocalizedMessage(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1458        } catch (Throwable e) {
1459            error(e);
1460        }
1461        return null;
1462    }
1463
1464    /**
1465     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#unlock(java.lang.String)
1466     */
1467    public String unlock(String sitePath) throws CmsRpcException {
1468
1469        try {
1470            CmsObject cms = OpenCms.initCmsObject(getCmsObject());
1471            cms.getRequestContext().setSiteRoot("");
1472            if (cms.existsResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION)) {
1473                CmsResource resource = cms.readResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION);
1474                tryUnlock(resource);
1475            }
1476        } catch (CmsException e) {
1477            return e.getLocalizedMessage(OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject()));
1478        } catch (Throwable e) {
1479            error(e);
1480        }
1481        return null;
1482    }
1483
1484    /**
1485     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#validate(java.util.Map)
1486     */
1487    public Map<String, CmsValidationResult> validate(Map<String, CmsValidationQuery> validationQueries)
1488    throws CmsRpcException {
1489
1490        try {
1491            Map<String, CmsValidationResult> result = new HashMap<String, CmsValidationResult>();
1492            for (Map.Entry<String, CmsValidationQuery> queryEntry : validationQueries.entrySet()) {
1493                String fieldName = queryEntry.getKey();
1494                CmsValidationQuery query = queryEntry.getValue();
1495                result.put(fieldName, validate(query.getValidatorId(), query.getValue(), query.getConfig()));
1496            }
1497            return result;
1498        } catch (Throwable e) {
1499            error(e);
1500        }
1501        return null;
1502    }
1503
1504    /**
1505     * @see org.opencms.gwt.shared.rpc.I_CmsCoreService#validate(java.lang.String, java.util.Map, java.util.Map, java.lang.String)
1506     */
1507    public Map<String, CmsValidationResult> validate(
1508        String formValidatorClass,
1509        Map<String, CmsValidationQuery> validationQueries,
1510        Map<String, String> values,
1511        String config)
1512    throws CmsRpcException {
1513
1514        try {
1515            I_CmsFormValidator formValidator = instantiate(I_CmsFormValidator.class, formValidatorClass);
1516            return formValidator.validate(getCmsObject(), validationQueries, values, config);
1517        } catch (Throwable e) {
1518            error(e);
1519        }
1520        return null;
1521    }
1522
1523    /**
1524     * Collect GWT build ids from the different ADE modules.<p>
1525     *
1526     * @return the map of GWT build ids
1527     */
1528    protected Map<String, String> getBuildIds() {
1529
1530        List<CmsModule> modules = OpenCms.getModuleManager().getAllInstalledModules();
1531        Map<String, String> result = new HashMap<String, String>();
1532        for (CmsModule module : modules) {
1533            String buildid = module.getParameter(CmsCoreData.KEY_GWT_BUILDID);
1534            if (buildid != null) {
1535                result.put(module.getName(), buildid);
1536            }
1537        }
1538        return result;
1539    }
1540
1541    /**
1542     * Helper method for locking a resource which returns some information on whether the locking
1543     * failed, and why.<p>
1544     *
1545     * @param structureId the structure id of the resource
1546     * @return the locking information
1547     *
1548     * @throws CmsException if something went wrong
1549     */
1550    protected CmsLockInfo getLock(CmsUUID structureId) throws CmsException {
1551
1552        CmsResource res = getCmsObject().readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
1553        return getLock(getCmsObject().getSitePath(res));
1554    }
1555
1556    /**
1557     * Helper method for locking a resource which returns some information on whether the locking
1558     * failed, and why.<p>
1559     *
1560     * @param sitepath the site path of the resource to lock
1561     * @return the locking information
1562     *
1563     * @throws CmsException if something went wrong
1564     */
1565    protected CmsLockInfo getLock(String sitepath) throws CmsException {
1566
1567        CmsObject cms = getCmsObject();
1568        CmsUser user = cms.getRequestContext().getCurrentUser();
1569        CmsLock lock = cms.getLock(sitepath);
1570        if (lock.isOwnedBy(user)) {
1571            return CmsLockInfo.forSuccess();
1572        }
1573        if (lock.getUserId().isNullUUID()) {
1574            cms.lockResourceTemporary(sitepath);
1575            return CmsLockInfo.forSuccess();
1576        }
1577        CmsUser owner = cms.readUser(lock.getUserId());
1578        return CmsLockInfo.forLockedResource(owner.getName());
1579    }
1580
1581    /**
1582     * Helper method for reading and filtering categories.
1583     *
1584     * @param fromCatPath the category path to start with, can be <code>null</code> or empty to use the root
1585     * @param includeSubCats if to include all categories, or first level child categories only
1586     * @param refVfsPath the reference path (site-relative path according to which the available category repositories are determined),  can be <code>null</code> to only use the system repository
1587     * @param withRepositories flag, indicating if also the category repositories should be returned as category
1588     * @param selectedCheck a predicate that checks for categories whose ancestors should be included even if they are marked as hidden
1589     *
1590     * @return the resource categories
1591     *
1592     * @throws CmsRpcException if something goes wrong
1593    
1594     *
1595     * @return
1596     * @throws CmsRpcException
1597     */
1598    private List<CmsCategoryTreeEntry> getCategoriesInternal(
1599        String fromPath,
1600        boolean includeSubCats,
1601        String refPath,
1602        boolean showWithRepositories,
1603        Predicate<CmsCategoryTreeEntry> selectedCheck)
1604    throws CmsRpcException {
1605
1606        CmsObject cms = getCmsObject();
1607        CmsCategoryService catService = CmsCategoryService.getInstance();
1608
1609        List<String> repositories = new ArrayList<String>();
1610        repositories.addAll(catService.getCategoryRepositories(getCmsObject(), refPath));
1611
1612        List<CmsCategoryTreeEntry> result = null;
1613        try {
1614            // get the categories
1615            List<CmsCategory> categories = catService.readCategoriesForRepositories(
1616                cms,
1617                fromPath,
1618                includeSubCats,
1619                repositories,
1620                showWithRepositories);
1621            categories = catService.localizeCategories(
1622                cms,
1623                categories,
1624                OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1625            result = buildCategoryTree(cms, categories);
1626            removeHiddenCategories(cms, result, selectedCheck);
1627
1628        } catch (Throwable e) {
1629            error(e);
1630        }
1631        return result;
1632    }
1633
1634    /**
1635     * Returns the workplace settings of the current user.<p>
1636     *
1637     * @return the workplace settings
1638     */
1639    private CmsWorkplaceSettings getWorkplaceSettings() {
1640
1641        if (m_workplaceSettings == null) {
1642            m_workplaceSettings = CmsWorkplace.getWorkplaceSettings(getCmsObject(), getRequest());
1643        }
1644        return m_workplaceSettings;
1645    }
1646
1647    /**
1648     * Internal helper method for validating a single value.<p>
1649     *
1650     * @param validator the class name of the validation service
1651     * @param value the value to validate
1652     * @param config the configuration for the validation service
1653     *
1654     * @return the result of the validation
1655     *
1656     * @throws Exception if something goes wrong
1657     */
1658    private CmsValidationResult validate(String validator, String value, String config) throws Exception {
1659
1660        I_CmsValidationService validationService = getValidationService(validator);
1661        return validationService.validate(getCmsObject(), value, config);
1662    }
1663}