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.ade.sitemap;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsADEConfigData.AttributeMode;
032import org.opencms.ade.configuration.CmsADEConfigDataInternal.AttributeValue;
033import org.opencms.ade.configuration.CmsADEManager;
034import org.opencms.ade.configuration.CmsFunctionAvailability;
035import org.opencms.ade.configuration.CmsFunctionReference;
036import org.opencms.ade.configuration.CmsModelPageConfig;
037import org.opencms.ade.configuration.CmsResourceTypeConfig;
038import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCacheState;
039import org.opencms.ade.detailpage.CmsDetailPageConfigurationWriter;
040import org.opencms.ade.detailpage.CmsDetailPageInfo;
041import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry;
042import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry.EntryType;
043import org.opencms.ade.sitemap.shared.CmsDetailPageTable;
044import org.opencms.ade.sitemap.shared.CmsGalleryFolderEntry;
045import org.opencms.ade.sitemap.shared.CmsGalleryType;
046import org.opencms.ade.sitemap.shared.CmsLocaleComparePropertyData;
047import org.opencms.ade.sitemap.shared.CmsModelInfo;
048import org.opencms.ade.sitemap.shared.CmsModelPageEntry;
049import org.opencms.ade.sitemap.shared.CmsNewResourceInfo;
050import org.opencms.ade.sitemap.shared.CmsSitemapAttributeData;
051import org.opencms.ade.sitemap.shared.CmsSitemapCategoryData;
052import org.opencms.ade.sitemap.shared.CmsSitemapChange;
053import org.opencms.ade.sitemap.shared.CmsSitemapChange.ChangeType;
054import org.opencms.ade.sitemap.shared.CmsSitemapClipboardData;
055import org.opencms.ade.sitemap.shared.CmsSitemapData;
056import org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode;
057import org.opencms.ade.sitemap.shared.CmsSitemapInfo;
058import org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService;
059import org.opencms.configuration.CmsDefaultUserSettings;
060import org.opencms.db.CmsAlias;
061import org.opencms.db.CmsAliasManager;
062import org.opencms.db.CmsRewriteAlias;
063import org.opencms.file.CmsFile;
064import org.opencms.file.CmsObject;
065import org.opencms.file.CmsProject;
066import org.opencms.file.CmsProperty;
067import org.opencms.file.CmsPropertyDefinition;
068import org.opencms.file.CmsRequestContext;
069import org.opencms.file.CmsResource;
070import org.opencms.file.CmsResourceFilter;
071import org.opencms.file.CmsUser;
072import org.opencms.file.CmsVfsResourceAlreadyExistsException;
073import org.opencms.file.CmsVfsResourceNotFoundException;
074import org.opencms.file.history.CmsHistoryResourceHandler;
075import org.opencms.file.types.CmsResourceTypeFolder;
076import org.opencms.file.types.CmsResourceTypeFolderExtended;
077import org.opencms.file.types.CmsResourceTypeFolderSubSitemap;
078import org.opencms.file.types.CmsResourceTypeFunctionConfig;
079import org.opencms.file.types.CmsResourceTypeUnknownFolder;
080import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
081import org.opencms.file.types.I_CmsResourceType;
082import org.opencms.flex.CmsFlexController;
083import org.opencms.gwt.CmsCoreService;
084import org.opencms.gwt.CmsGwtService;
085import org.opencms.gwt.CmsIconUtil;
086import org.opencms.gwt.CmsPropertyEditorHelper;
087import org.opencms.gwt.CmsRpcException;
088import org.opencms.gwt.CmsTemplateFinder;
089import org.opencms.gwt.CmsVfsService;
090import org.opencms.gwt.shared.CmsCategoryTreeEntry;
091import org.opencms.gwt.shared.CmsClientLock;
092import org.opencms.gwt.shared.CmsCoreData;
093import org.opencms.gwt.shared.CmsCoreData.AdeContext;
094import org.opencms.gwt.shared.CmsGwtConstants;
095import org.opencms.gwt.shared.CmsListInfoBean;
096import org.opencms.gwt.shared.alias.CmsAliasEditValidationReply;
097import org.opencms.gwt.shared.alias.CmsAliasEditValidationRequest;
098import org.opencms.gwt.shared.alias.CmsAliasImportResult;
099import org.opencms.gwt.shared.alias.CmsAliasInitialFetchResult;
100import org.opencms.gwt.shared.alias.CmsAliasSaveValidationRequest;
101import org.opencms.gwt.shared.alias.CmsAliasTableRow;
102import org.opencms.gwt.shared.alias.CmsRewriteAliasTableRow;
103import org.opencms.gwt.shared.alias.CmsRewriteAliasValidationReply;
104import org.opencms.gwt.shared.alias.CmsRewriteAliasValidationRequest;
105import org.opencms.gwt.shared.property.CmsClientProperty;
106import org.opencms.gwt.shared.property.CmsPropertyModification;
107import org.opencms.i18n.CmsLocaleManager;
108import org.opencms.json.JSONArray;
109import org.opencms.jsp.CmsJspNavBuilder;
110import org.opencms.jsp.CmsJspNavBuilder.Visibility;
111import org.opencms.jsp.CmsJspNavElement;
112import org.opencms.jsp.CmsJspTagLink;
113import org.opencms.loader.CmsLoaderException;
114import org.opencms.lock.CmsLock;
115import org.opencms.lock.CmsLockUtil;
116import org.opencms.main.CmsException;
117import org.opencms.main.CmsLog;
118import org.opencms.main.OpenCms;
119import org.opencms.main.OpenCmsServlet;
120import org.opencms.relations.CmsCategory;
121import org.opencms.relations.CmsCategoryService;
122import org.opencms.search.galleries.CmsGallerySearch;
123import org.opencms.search.galleries.CmsGallerySearchResult;
124import org.opencms.security.CmsPermissionSet;
125import org.opencms.security.CmsRole;
126import org.opencms.security.CmsRoleViolationException;
127import org.opencms.security.CmsSecurityException;
128import org.opencms.site.CmsSite;
129import org.opencms.ui.apps.CmsQuickLaunchLocationCache;
130import org.opencms.util.CmsDateUtil;
131import org.opencms.util.CmsFileUtil;
132import org.opencms.util.CmsMacroResolver;
133import org.opencms.util.CmsPath;
134import org.opencms.util.CmsStringUtil;
135import org.opencms.util.CmsUUID;
136import org.opencms.workplace.CmsWorkplaceManager;
137import org.opencms.workplace.CmsWorkplaceMessages;
138import org.opencms.workplace.explorer.CmsExplorerTypeAccess;
139import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
140import org.opencms.workplace.galleries.A_CmsAjaxGallery;
141import org.opencms.xml.CmsXmlException;
142import org.opencms.xml.I_CmsXmlDocument;
143import org.opencms.xml.containerpage.CmsADESessionCache;
144import org.opencms.xml.containerpage.CmsContainerBean;
145import org.opencms.xml.containerpage.CmsContainerElementBean;
146import org.opencms.xml.containerpage.CmsContainerPageBean;
147import org.opencms.xml.containerpage.CmsXmlContainerPage;
148import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
149import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
150import org.opencms.xml.containerpage.mutable.CmsContainerPageWrapper;
151import org.opencms.xml.containerpage.mutable.CmsMutableContainer;
152import org.opencms.xml.containerpage.mutable.CmsMutableContainerPage;
153import org.opencms.xml.content.CmsXmlContent;
154import org.opencms.xml.content.CmsXmlContentFactory;
155import org.opencms.xml.content.CmsXmlContentProperty;
156import org.opencms.xml.content.CmsXmlContentPropertyHelper;
157import org.opencms.xml.types.I_CmsXmlContentValue;
158
159import java.text.DateFormat;
160import java.util.ArrayList;
161import java.util.Arrays;
162import java.util.Collection;
163import java.util.Collections;
164import java.util.Comparator;
165import java.util.Date;
166import java.util.HashMap;
167import java.util.LinkedHashMap;
168import java.util.List;
169import java.util.ListIterator;
170import java.util.Locale;
171import java.util.Map;
172import java.util.regex.Pattern;
173import java.util.regex.PatternSyntaxException;
174
175import javax.servlet.http.HttpServletRequest;
176
177import org.apache.commons.logging.Log;
178
179import com.google.common.collect.ComparisonChain;
180import com.google.common.collect.Lists;
181import com.google.common.collect.Maps;
182import com.google.common.collect.Ordering;
183
184/**
185 * Handles all RPC services related to the vfs sitemap.<p>
186 *
187 * @since 8.0.0
188 *
189 * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService
190 * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapServiceAsync
191 */
192public class CmsVfsSitemapService extends CmsGwtService implements I_CmsSitemapService {
193
194    /** Helper class for representing information about a  lock. */
195    protected class LockInfo {
196
197        /** The lock state. */
198        private CmsLock m_lock;
199
200        /** True if the lock was just locked. */
201        private boolean m_wasJustLocked;
202
203        /**
204         * Creates a new LockInfo object.<p>
205         *
206         * @param lock the lock state
207         * @param wasJustLocked true if the lock was just locked
208         */
209        public LockInfo(CmsLock lock, boolean wasJustLocked) {
210
211            m_lock = lock;
212            m_wasJustLocked = wasJustLocked;
213        }
214
215        /**
216         * Returns the lock state.<p>
217         *
218         * @return the lock state
219         */
220        public CmsLock getLock() {
221
222            return m_lock;
223        }
224
225        /**
226         * Returns true if the lock was just locked.<p>
227         *
228         * @return true if the lock was just locked
229         */
230        public boolean wasJustLocked() {
231
232            return m_wasJustLocked;
233        }
234    }
235
236    /** The path of the JSP used to download aliases. */
237    public static final String ALIAS_DOWNLOAD_PATH = "/system/workplace/commons/download-aliases.jsp";
238
239    /** The path to the JSP used to upload aliases. */
240    public static final String ALIAS_IMPORT_PATH = "/system/workplace/commons/import-aliases.jsp";
241
242    /** The show model edit confirm dialog attribute name. */
243    public static final String ATTR_SHOW_MODEL_EDIT_CONFIRM = "showModelEditConfirm";
244
245    /** Properties to remove from the copied template when creating a new sitemap entry. */
246    public static final List<String> FILTER_PROPERTIES = Arrays.asList(
247        new String[] {
248            CmsPropertyDefinition.PROPERTY_TITLE,
249            CmsPropertyDefinition.PROPERTY_DESCRIPTION,
250            CmsPropertyDefinition.PROPERTY_DESCRIPTION_HTML,
251            CmsPropertyDefinition.PROPERTY_NAVTEXT,
252            CmsPropertyDefinition.PROPERTY_NAVINFO});
253
254    /** The galleries folder name. */
255    public static final String GALLERIES_FOLDER_NAME = ".galleries";
256
257    /** The configuration key for the functionDetail attribute in the container.info property. */
258    public static final String KEY_FUNCTION_DETAIL = "functionDetail";
259
260    /** The additional user info key for deleted list. */
261    private static final String ADDINFO_ADE_DELETED_LIST = "ADE_DELETED_LIST";
262
263    /** The additional user info key for modified list. */
264    private static final String ADDINFO_ADE_MODIFIED_LIST = "ADE_MODIFIED_LIST";
265
266    /** The lock table to prevent multiple users from editing the alias table concurrently. */
267    private static CmsAliasEditorLockTable aliasEditorLockTable = new CmsAliasEditorLockTable();
268
269    /** The table containing the alias import results. */
270    private static CmsAliasImportResponseTable aliasImportResponseTable = new CmsAliasImportResponseTable();
271
272    /** The static log object for this class. */
273    private static final Log LOG = CmsLog.getLog(CmsVfsSitemapService.class);
274
275    /** The redirect recource type name. */
276    private static final String RECOURCE_TYPE_NAME_REDIRECT = "htmlredirect";
277
278    /** The redirect target XPath. */
279    private static final String REDIRECT_LINK_TARGET_XPATH = "Link";
280
281    /** Serialization uid. */
282    private static final long serialVersionUID = -7236544324371767330L;
283
284    /** The VFS path of the redirect copy page for navigation level entries. */
285    private static final String SUB_LEVEL_REDIRECT_COPY_PAGE = "/system/modules/org.opencms.base/copyresources/sub-level-redirect.html";
286
287    /** The navigation builder. */
288    private CmsJspNavBuilder m_navBuilder;
289
290    /**
291     * Adds an alias import result.<p>
292     *
293     * @param results the list of alias import results to add
294     *
295     * @return the key to retrieve the alias import results
296     */
297    public static String addAliasImportResult(List<CmsAliasImportResult> results) {
298
299        return aliasImportResponseTable.addImportResult(results);
300    }
301
302    /**
303     * Creates a client property bean from a server-side property.<p>
304     *
305     * @param prop the property from which to create the client property
306     * @param preserveOrigin if true, the origin will be copied into the new object
307     *
308     * @return the new client property
309     */
310    public static CmsClientProperty createClientProperty(CmsProperty prop, boolean preserveOrigin) {
311
312        CmsClientProperty result = new CmsClientProperty(
313            prop.getName(),
314            prop.getStructureValue(),
315            prop.getResourceValue());
316        if (preserveOrigin) {
317            result.setOrigin(prop.getOrigin());
318        }
319        return result;
320    }
321
322    /**
323     * Fetches the sitemap data.<p>
324     *
325     * @param request the current request
326     * @param sitemapUri the site relative path
327     *
328     * @return the sitemap data
329     *
330     * @throws CmsRpcException if something goes wrong
331     */
332    public static CmsSitemapData prefetch(HttpServletRequest request, String sitemapUri) throws CmsRpcException {
333
334        CmsVfsSitemapService service = new CmsVfsSitemapService();
335        service.setCms(CmsFlexController.getCmsObject(request));
336        service.setRequest(request);
337        CmsSitemapData result = null;
338        try {
339            result = service.prefetch(sitemapUri);
340        } finally {
341            service.clearThreadStorage();
342        }
343        return result;
344    }
345
346    /**
347     * Converts a sequence of properties to a map of client-side property beans.<p>
348     *
349     * @param props the sequence of properties
350     * @param preserveOrigin if true, the origin of the properties should be copied to the client properties
351     *
352     * @return the map of client properties
353     *
354     */
355    static Map<String, CmsClientProperty> createClientProperties(Iterable<CmsProperty> props, boolean preserveOrigin) {
356
357        Map<String, CmsClientProperty> result = new HashMap<String, CmsClientProperty>();
358        for (CmsProperty prop : props) {
359            CmsClientProperty clientProp = createClientProperty(prop, preserveOrigin);
360            result.put(prop.getName(), clientProp);
361        }
362        return result;
363    }
364
365    /**
366     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#changeCategory(java.lang.String, org.opencms.util.CmsUUID, java.lang.String, java.lang.String)
367     */
368    public void changeCategory(String entryPoint, CmsUUID id, String title, String name) throws CmsRpcException {
369
370        try {
371            name = OpenCms.getResourceManager().getFileTranslator().translateResource(name.trim().replace('/', '-'));
372            CmsObject cms = getCmsObject();
373            CmsResource categoryResource = cms.readResource(id);
374            ensureLock(categoryResource);
375            String sitePath = cms.getSitePath(categoryResource);
376            String newPath = CmsStringUtil.joinPaths(CmsResource.getParentFolder(sitePath), name);
377            cms.writePropertyObject(sitePath, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, title, null));
378            if (!CmsStringUtil.joinPaths("/", newPath, "/").equals(CmsStringUtil.joinPaths("/", sitePath, "/"))) {
379                cms.moveResource(sitePath, newPath);
380            }
381        } catch (Exception e) {
382            error(e);
383        }
384    }
385
386    /**
387     * @see org.opencms.gwt.CmsGwtService#checkPermissions(org.opencms.file.CmsObject)
388     */
389    @Override
390    public void checkPermissions(CmsObject cms) throws CmsRoleViolationException {
391
392        OpenCms.getRoleManager().checkRole(cms, CmsRole.EDITOR);
393    }
394
395    /**
396     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#createCategory(java.lang.String, org.opencms.util.CmsUUID, java.lang.String, java.lang.String)
397     */
398    public void createCategory(String entryPoint, CmsUUID id, String title, String name) throws CmsRpcException {
399
400        try {
401            if ((name == null) || (name.length() == 0)) {
402                name = title;
403            }
404            name = OpenCms.getResourceManager().getFileTranslator().translateResource(name.trim().replace('/', '-'));
405            CmsObject cms = getCmsObject();
406            CmsCategoryService catService = CmsCategoryService.getInstance();
407
408            CmsCategory createdCategory = null;
409            if (id.isNullUUID()) {
410                String localRepositoryPath = CmsStringUtil.joinPaths(
411                    entryPoint,
412                    CmsCategoryService.getInstance().getRepositoryBaseFolderName(getCmsObject()));
413
414                // ensure category repository exists
415                if (!cms.existsResource(localRepositoryPath)) {
416                    tryUnlock(
417                        cms.createResource(
418                            localRepositoryPath,
419                            OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.getStaticTypeName())));
420                }
421                createdCategory = catService.createCategory(cms, null, name, title, "", localRepositoryPath);
422            } else {
423                CmsResource parentResource = cms.readResource(id);
424                CmsCategory parent = catService.getCategory(cms, parentResource);
425                createdCategory = catService.createCategory(
426                    cms,
427                    parent,
428                    name,
429                    title,
430                    "",
431                    cms.getRequestContext().removeSiteRoot(parentResource.getRootPath()));
432            }
433            tryUnlock(cms.readResource(createdCategory.getId()));
434        } catch (Exception e) {
435            error(e);
436        }
437    }
438
439    /**
440     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#createNewGalleryFolder(java.lang.String, java.lang.String, int)
441     */
442    public CmsGalleryFolderEntry createNewGalleryFolder(String parentFolder, String title, int folderTypeId)
443    throws CmsRpcException {
444
445        CmsObject cms = getCmsObject();
446        CmsGalleryFolderEntry folderEntry = null;
447        try {
448            if (!cms.existsResource(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
449                CmsResource parent = cms.createResource(
450                    parentFolder,
451                    OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.getStaticTypeName()));
452                tryUnlock(parent);
453            }
454            String folderName = OpenCms.getResourceManager().getFileTranslator().translateResource(title);
455
456            folderName = OpenCms.getResourceManager().getNameGenerator().getUniqueFileName(
457                cms,
458                parentFolder,
459                folderName);
460            String folderPath = CmsStringUtil.joinPaths(parentFolder, folderName);
461
462            @SuppressWarnings("deprecation")
463            CmsResource galleryFolder = cms.createResource(
464                folderPath,
465                folderTypeId,
466                null,
467                Collections.singletonList(new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, title, null)));
468            folderEntry = readGalleryFolderEntry(
469                galleryFolder,
470                OpenCms.getResourceManager().getResourceType(galleryFolder).getTypeName());
471            tryUnlock(galleryFolder);
472        } catch (CmsException e) {
473            error(e);
474        }
475        return folderEntry;
476    }
477
478    /**
479     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#createNewModelPage(java.lang.String, java.lang.String, java.lang.String, org.opencms.util.CmsUUID, boolean)
480     */
481    public CmsModelPageEntry createNewModelPage(
482        String entryPointUri,
483        String title,
484        String description,
485        CmsUUID copyId,
486        boolean isModelGroup)
487    throws CmsRpcException {
488
489        try {
490            CmsObject cms = getCmsObject();
491            CmsResource rootResource = cms.readResource(entryPointUri);
492            CmsModelPageHelper helper = new CmsModelPageHelper(getCmsObject(), rootResource);
493            CmsResource page;
494            if (isModelGroup) {
495                page = helper.createModelGroupPage(title, description, copyId);
496            } else {
497                page = helper.createPageInModelFolder(title, description, copyId);
498                String configPath = CmsStringUtil.joinPaths(entryPointUri, ".content/.config");
499                CmsResource configResource = cms.readResource(configPath);
500                helper.addModelPageToSitemapConfiguration(configResource, page, false);
501            }
502            CmsModelPageEntry result = helper.createModelPageEntry(page, false, false, getWorkplaceLocale());
503            OpenCms.getADEManager().waitForCacheUpdate(false);
504            return result;
505        } catch (Throwable e) {
506            error(e);
507            return null;
508        }
509    }
510
511    /**
512     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#createSubSitemap(org.opencms.util.CmsUUID)
513     */
514    public CmsSitemapChange createSubSitemap(CmsUUID entryId) throws CmsRpcException {
515
516        CmsObject cms = getCmsObject();
517        try {
518            ensureSession();
519            CmsResource subSitemapFolder = cms.readResource(entryId);
520            ensureLock(subSitemapFolder);
521            String sitePath = cms.getSitePath(subSitemapFolder);
522            createSitemapContentFolder(cms, subSitemapFolder);
523            subSitemapFolder.setType(getSubsitemapType());
524            cms.writeResource(subSitemapFolder);
525            tryUnlock(subSitemapFolder);
526            CmsSitemapClipboardData clipboard = getClipboardData();
527            CmsClientSitemapEntry entry = toClientEntry(
528                getNavBuilder().getNavigationForResource(sitePath, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED),
529                false);
530            clipboard.addModified(entry);
531            setClipboardData(clipboard);
532            CmsSitemapChange result = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.modify);
533            result.setUpdatedEntry(entry);
534            return result;
535        } catch (Exception e) {
536            error(e);
537        }
538        return null;
539
540    }
541
542    /**
543     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#disableModelPage(java.lang.String, org.opencms.util.CmsUUID, boolean)
544     */
545    public void disableModelPage(String baseUri, CmsUUID modelPageId, boolean disabled) throws CmsRpcException {
546
547        try {
548            CmsObject cms = getCmsObject();
549            CmsResource rootResource = cms.readResource(baseUri);
550            CmsModelPageHelper helper = new CmsModelPageHelper(getCmsObject(), rootResource);
551            String configPath = CmsStringUtil.joinPaths(baseUri, ".content/.config");
552            CmsResource configResource = cms.readResource(configPath);
553            helper.disableModelPage(configResource, modelPageId, disabled);
554            OpenCms.getADEManager().waitForCacheUpdate(false);
555        } catch (Throwable e) {
556            error(e);
557        }
558    }
559
560    /**
561     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#editAttributeData(org.opencms.util.CmsUUID)
562     */
563    public CmsSitemapAttributeData editAttributeData(CmsUUID rootId) throws CmsRpcException {
564
565        try {
566            CmsObject cms = getCmsObject();
567            CmsResource root = cms.readResource(rootId);
568            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(cms, root.getRootPath());
569            String sitePath = cms.getSitePath(root);
570            String configPath = CmsStringUtil.joinPaths(sitePath, "/.content/.config");
571            CmsResource configResource = cms.readResource(configPath);
572            cms.lockResourceTemporary(configResource);
573            CmsListInfoBean configInfo = CmsVfsService.getPageInfo(cms, configResource);
574            Map<String, CmsXmlContentProperty> definitions = config.getAttributeEditorConfiguration().getAttributeDefinitions();
575            cms.getRequestContext().setAttribute(
576                CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH,
577                configResource.getRootPath());
578            definitions = CmsXmlContentPropertyHelper.resolveMacrosInProperties(
579                definitions,
580                CmsMacroResolver.newWorkplaceLocaleResolver(cms));
581
582            HashMap<String, String> values = new HashMap<>();
583            Map<String, AttributeValue> attributes = config.getAttributes(AttributeMode.excludeInjectedForThisLevel);
584            for (String key : definitions.keySet()) {
585                AttributeValue val = attributes.get(key);
586                String strVal;
587                if (val == null) {
588                    strVal = "";
589                } else {
590                    strVal = val.getValue();
591                }
592                values.put(key, strVal);
593            }
594            String unlockUrl = CmsStringUtil.joinPaths(
595                OpenCms.getStaticExportManager().getVfsPrefix(),
596                OpenCmsServlet.HANDLE_BUILTIN_SERVICE,
597                CmsGwtConstants.UNLOCK_FILE_PREFIX,
598                configResource.getStructureId().toString());
599            CmsSitemapAttributeData result = new CmsSitemapAttributeData(configInfo, definitions, values, unlockUrl);
600            return result;
601        } catch (Exception e) {
602            error(e);
603            return null;
604        }
605    }
606
607    /**
608     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getAliasImportResult(java.lang.String)
609     */
610    public List<CmsAliasImportResult> getAliasImportResult(String resultKey) {
611
612        return aliasImportResponseTable.getAndRemove(resultKey);
613    }
614
615    /**
616     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getAliasTable()
617     */
618    public CmsAliasInitialFetchResult getAliasTable() throws CmsRpcException {
619
620        try {
621            CmsObject cms = getCmsObject();
622            CmsAliasManager aliasManager = OpenCms.getAliasManager();
623            List<CmsAlias> aliases = aliasManager.getAliasesForSite(cms, cms.getRequestContext().getSiteRoot());
624            List<CmsAliasTableRow> rows = new ArrayList<CmsAliasTableRow>();
625            CmsAliasInitialFetchResult result = new CmsAliasInitialFetchResult();
626            for (CmsAlias alias : aliases) {
627                CmsAliasTableRow row = createAliasTableEntry(cms, alias);
628                rows.add(row);
629            }
630            result.setRows(rows);
631
632            List<CmsRewriteAlias> rewriteAliases = aliasManager.getRewriteAliases(
633                cms,
634                cms.getRequestContext().getSiteRoot());
635
636            List<CmsRewriteAliasTableRow> rewriteRows = new ArrayList<CmsRewriteAliasTableRow>();
637            for (CmsRewriteAlias rewriteAlias : rewriteAliases) {
638                CmsRewriteAliasTableRow rewriteRow = new CmsRewriteAliasTableRow(
639                    rewriteAlias.getId(),
640                    rewriteAlias.getPatternString(),
641                    rewriteAlias.getReplacementString(),
642                    rewriteAlias.getMode());
643                rewriteRows.add(rewriteRow);
644            }
645            result.setRewriteRows(rewriteRows);
646            CmsUser otherLockOwner = aliasEditorLockTable.update(cms, cms.getRequestContext().getSiteRoot());
647            if (otherLockOwner != null) {
648                result.setAliasLockOwner(otherLockOwner.getName());
649            }
650            result.setDownloadUrl(OpenCms.getLinkManager().substituteLinkForRootPath(cms, ALIAS_DOWNLOAD_PATH));
651            return result;
652        } catch (Throwable e) {
653            error(e);
654            return null;
655        }
656    }
657
658    /**
659     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getCategoryData(java.lang.String)
660     */
661    public CmsSitemapCategoryData getCategoryData(String entryPoint) throws CmsRpcException {
662
663        CmsObject cms = getCmsObject();
664        try {
665            CmsResource entryPointResource = cms.readResource(entryPoint);
666            String basePath = CmsStringUtil.joinPaths(
667                entryPointResource.getRootPath(),
668                CmsCategoryService.getInstance().getRepositoryBaseFolderName(getCmsObject()));
669
670            List<CmsCategoryTreeEntry> entries = CmsCoreService.getCategoriesForSitePathStatic(
671                cms,
672                entryPoint,
673                cms.getRequestContext().removeSiteRoot(basePath));
674            CmsSitemapCategoryData categoryData = new CmsSitemapCategoryData();
675            for (CmsCategoryTreeEntry entry : entries) {
676                categoryData.add(entry);
677            }
678
679            categoryData.setBasePath(basePath);
680            return categoryData;
681        } catch (Exception e) {
682            error(e);
683            return null;
684        }
685    }
686
687    /**
688     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getChildren(java.lang.String, org.opencms.util.CmsUUID, int)
689     */
690    public CmsClientSitemapEntry getChildren(String entryPointUri, CmsUUID entryId, int levels) throws CmsRpcException {
691
692        CmsClientSitemapEntry entry = null;
693
694        try {
695            CmsObject cms = getCmsObject();
696
697            //ensure that root ends with a '/' if it's a folder
698            CmsResource rootRes = cms.readResource(entryId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
699            String root = cms.getSitePath(rootRes);
700            CmsJspNavElement navElement = getNavBuilder().getNavigationForResource(
701                root,
702                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
703            boolean isRoot = root.equals(entryPointUri);
704            entry = toClientEntry(navElement, isRoot);
705            if ((levels > 0) && (isRoot || (rootRes.isFolder()))) {
706                entry.setSubEntries(getChildren(root, levels, null), null);
707            }
708        } catch (Throwable e) {
709            error(e);
710        }
711        return entry;
712    }
713
714    /**
715     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getGalleryData(java.lang.String)
716     */
717    public Map<CmsGalleryType, List<CmsGalleryFolderEntry>> getGalleryData(String entryPointUri) {
718
719        List<String> subSitePaths = OpenCms.getADEManager().getSubSitePaths(
720            getCmsObject(),
721            getCmsObject().getRequestContext().addSiteRoot(entryPointUri));
722        List<CmsGalleryType> galleryTypes = collectGalleryTypes();
723        Map<CmsGalleryType, List<CmsGalleryFolderEntry>> result = new HashMap<CmsGalleryType, List<CmsGalleryFolderEntry>>();
724        for (CmsGalleryType type : galleryTypes) {
725            List<CmsGalleryFolderEntry> galleries = null;
726            try {
727                galleries = getGalleriesForType(entryPointUri, type, subSitePaths);
728            } catch (CmsException e) {
729                log(e.getLocalizedMessage(), e);
730            }
731            result.put(type, galleries);
732        }
733        return result;
734    }
735
736    /**
737     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getModelInfos(org.opencms.util.CmsUUID)
738     */
739    public CmsModelInfo getModelInfos(CmsUUID rootId) throws CmsRpcException {
740
741        try {
742            CmsObject cms = getCmsObject();
743            CmsResource rootResource = cms.readResource(rootId);
744            CmsModelPageHelper modelPageHelper = new CmsModelPageHelper(getCmsObject(), rootResource);
745            return modelPageHelper.getModelInfo();
746        } catch (Throwable e) {
747            error(e);
748            return null; // will  never be reached
749        }
750
751    }
752
753    /**
754     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getNewElementInfo(java.lang.String)
755     */
756    public List<CmsNewResourceInfo> getNewElementInfo(String entryPointUri) throws CmsRpcException {
757
758        try {
759            CmsObject cms = getCmsObject();
760            String rootPath = cms.getRequestContext().addSiteRoot(entryPointUri);
761            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(cms, rootPath);
762            List<CmsNewResourceInfo> result = getNewResourceInfos(cms, config, getWorkplaceLocale());
763            return result;
764        } catch (Throwable e) {
765            error(e);
766            return null;
767        }
768    }
769
770    /**
771     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#getResourceLink(org.opencms.util.CmsUUID, java.lang.String)
772     */
773    public String getResourceLink(CmsUUID baseId, String sitePath) throws CmsRpcException {
774
775        try {
776            CmsObject cms = OpenCms.initCmsObject(getCmsObject());
777            CmsResource baseResource = cms.readResource(baseId, CmsResourceFilter.IGNORE_EXPIRATION);
778            String contextPath = cms.getSitePath(baseResource);
779            cms.getRequestContext().setUri(contextPath);
780            Locale locale = CmsJspTagLink.getBaseUriLocale(cms, contextPath);
781            if (locale != null) {
782                cms.getRequestContext().setLocale(locale);
783            }
784            String result = OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, sitePath);
785            return result;
786        } catch (Exception e) {
787            error(e);
788        }
789        return null;
790    }
791
792    /**
793     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#loadPropertyDataForLocaleCompareView(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID)
794     */
795    public CmsLocaleComparePropertyData loadPropertyDataForLocaleCompareView(CmsUUID id, CmsUUID rootId)
796    throws CmsRpcException {
797
798        try {
799            CmsLocaleComparePropertyData result = new CmsLocaleComparePropertyData();
800            CmsObject cms = getCmsObject();
801            CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION;
802            CmsResource resource = cms.readResource(id, filter);
803            CmsResource defaultFile = cms.readDefaultFile(id.toString());
804            result.setDefaultFileId(defaultFile != null ? defaultFile.getStructureId() : null);
805            result.setId(id);
806            result.setFolder(resource.isFolder());
807            List<CmsProperty> props = cms.readPropertyObjects(resource, false);
808            List<CmsProperty> defaultFileProps = cms.readPropertyObjects(defaultFile, false);
809            Map<String, CmsClientProperty> clientProps = createClientProperties(props, true);
810            Map<String, CmsClientProperty> clientDefaultFileProps = createClientProperties(defaultFileProps, true);
811
812            result.setOwnProperties(clientProps);
813            result.setDefaultFileProperties(clientDefaultFileProps);
814            List<CmsResource> blockingLocked = cms.getBlockingLockedResources(resource);
815
816            CmsResource parent = cms.readParentFolder(resource.getStructureId());
817            List<CmsResource> resourcesInSameFolder = cms.readResources(parent, CmsResourceFilter.ALL, false);
818            List<String> forbiddenUrlNames = Lists.newArrayList();
819            for (CmsResource resourceInSameFolder : resourcesInSameFolder) {
820                String otherName = CmsResource.getName(resourceInSameFolder.getRootPath());
821                forbiddenUrlNames.add(otherName);
822            }
823            result.setForbiddenUrlNames(forbiddenUrlNames);
824            result.setInheritedProperties(createClientProperties(cms.readPropertyObjects(parent, true), true));
825            result.setPath(resource.getRootPath());
826            String name = CmsFileUtil.removeTrailingSeparator(CmsResource.getName(resource.getRootPath()));
827            result.setName(name);
828            result.setHasEditableName(!CmsStringUtil.isEmptyOrWhitespaceOnly(name) && blockingLocked.isEmpty());
829            return result;
830        } catch (Exception e) {
831            error(e);
832            return null;
833        }
834    }
835
836    /**
837     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#mergeSubSitemap(java.lang.String, org.opencms.util.CmsUUID)
838     */
839    @SuppressWarnings("deprecation")
840    public CmsSitemapChange mergeSubSitemap(String entryPoint, CmsUUID subSitemapId) throws CmsRpcException {
841
842        CmsObject cms = getCmsObject();
843        try {
844            ensureSession();
845            CmsResource subSitemapFolder = cms.readResource(subSitemapId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
846            ensureLock(subSitemapFolder);
847            subSitemapFolder.setType(
848                OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.RESOURCE_TYPE_NAME).getTypeId());
849            cms.writeResource(subSitemapFolder);
850            String sitePath = cms.getSitePath(subSitemapFolder);
851            String sitemapConfigName = CmsStringUtil.joinPaths(
852                sitePath,
853                CmsADEManager.CONTENT_FOLDER_NAME,
854                CmsADEManager.CONFIG_FILE_NAME);
855            if (cms.existsResource(sitemapConfigName)) {
856                cms.deleteResource(sitemapConfigName, CmsResource.DELETE_PRESERVE_SIBLINGS);
857            }
858            tryUnlock(subSitemapFolder);
859            CmsSitemapClipboardData clipboard = getClipboardData();
860            CmsClientSitemapEntry entry = toClientEntry(
861                getNavBuilder().getNavigationForResource(
862                    cms.getSitePath(subSitemapFolder),
863                    CmsResourceFilter.ONLY_VISIBLE_NO_DELETED),
864                false);
865            clipboard.addModified(entry);
866            setClipboardData(clipboard);
867            entry = getChildren(entryPoint, subSitemapId, 1);
868            CmsSitemapChange result = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.modify);
869            result.setUpdatedEntry(entry);
870            return result;
871        } catch (Exception e) {
872            error(e);
873        }
874        return null;
875    }
876
877    /**
878     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#prefetch(java.lang.String)
879     */
880    public CmsSitemapData prefetch(String sitemapUri) throws CmsRpcException {
881
882        CmsSitemapData result = null;
883        CmsObject cms = getCmsObject();
884
885        try {
886            OpenCms.getRoleManager().checkRole(cms, CmsRole.EDITOR);
887            String openPath = getRequest().getParameter(CmsCoreData.PARAM_PATH);
888            if (!isValidOpenPath(cms, openPath)) {
889                // if no path is supplied, start from root
890                openPath = "/";
891            }
892            CmsQuickLaunchLocationCache.getLocationCache(getRequest().getSession()).setSitemapEditorLocation(
893                cms.getRequestContext().getSiteRoot(),
894                openPath);
895            CmsADEConfigData configData = OpenCms.getADEManager().lookupConfiguration(
896                cms,
897                cms.getRequestContext().addSiteRoot(openPath));
898            Map<String, CmsXmlContentProperty> propertyConfig = new LinkedHashMap<String, CmsXmlContentProperty>(
899                configData.getPropertyConfigurationAsMap());
900            propertyConfig = CmsXmlContentPropertyHelper.resolveMacrosInProperties(
901                propertyConfig,
902                CmsMacroResolver.newWorkplaceLocaleResolver(getCmsObject()));
903
904            Map<String, CmsClientProperty> parentProperties = generateParentProperties(configData.getBasePath());
905            String siteRoot = cms.getRequestContext().getSiteRoot();
906            String exportRfsPrefix = OpenCms.getStaticExportManager().getDefaultRfsPrefix();
907            CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(siteRoot);
908            boolean isSecure = (site != null) && site.hasSecureServer();
909            String parentSitemap = null;
910
911            if (configData.getBasePath() != null) {
912                CmsADEConfigData parentConfigData = OpenCms.getADEManager().lookupConfiguration(
913                    cms,
914                    CmsResource.getParentFolder(configData.getBasePath()));
915                parentSitemap = parentConfigData.getBasePath();
916                if (parentSitemap != null) {
917                    parentSitemap = cms.getRequestContext().removeSiteRoot(parentSitemap);
918                }
919            }
920            String noEdit = "";
921            CmsNewResourceInfo defaultNewInfo = null;
922            List<CmsNewResourceInfo> newResourceInfos = null;
923            CmsDetailPageTable detailPages = null;
924            List<CmsNewResourceInfo> resourceTypeInfos = null;
925            boolean canEditDetailPages = false;
926            boolean isOnlineProject = CmsProject.isOnlineProject(cms.getRequestContext().getCurrentProject().getUuid());
927            String defaultGalleryFolder = GALLERIES_FOLDER_NAME;
928            try {
929                CmsObject rootCms = OpenCms.initCmsObject(cms);
930                rootCms.getRequestContext().setSiteRoot("");
931                CmsResource baseDir = rootCms.readResource(
932                    configData.getBasePath(),
933                    CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
934                CmsProperty galleryFolderProp = cms.readPropertyObject(
935                    baseDir,
936                    CmsPropertyDefinition.PROPERTY_GALLERIES_FOLDER,
937                    true);
938                if (!galleryFolderProp.isNullProperty()
939                    && CmsStringUtil.isNotEmptyOrWhitespaceOnly(galleryFolderProp.getValue())) {
940                    defaultGalleryFolder = galleryFolderProp.getValue();
941                }
942                CmsPropertyEditorHelper.updateWysiwygConfig(propertyConfig, cms, baseDir);
943            } catch (CmsException e) {
944                LOG.warn(e.getLocalizedMessage(), e);
945            }
946            boolean canManageDetailPages = true;
947            String detailPageRole = OpenCms.getWorkplaceManager().getSitemapEditorDetailPageManagementRole();
948
949            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(detailPageRole)) {
950                CmsRole role = null;
951                try {
952                    if (detailPageRole.indexOf("/") == -1) {
953                        role = CmsRole.valueOfRoleName(detailPageRole).forOrgUnit(null);
954                    } else {
955                        role = CmsRole.valueOfRoleName(detailPageRole);
956                    }
957                    if (role != null) {
958                        canManageDetailPages = OpenCms.getRoleManager().hasRoleForResource(
959                            cms,
960                            role,
961                            cms.getRequestContext().removeSiteRoot(configData.getBasePath()));
962                    } else {
963                        LOG.error("Configured role '" + detailPageRole + "' not found");
964                    }
965                } catch (Exception e) {
966                    LOG.error(e.getLocalizedMessage(), e);
967                }
968            }
969
970            detailPages = new CmsDetailPageTable(configData.getAllDetailPages());
971            if (!isOnlineProject) {
972                newResourceInfos = getNewResourceInfos(cms, configData, getWorkplaceLocale());
973                CmsResource modelResource = null;
974                if (configData.getDefaultModelPage() != null) {
975                    if (cms.existsResource(configData.getDefaultModelPage().getResource().getStructureId())) {
976                        modelResource = configData.getDefaultModelPage().getResource();
977                    } else {
978                        try {
979                            modelResource = cms.readResource(
980                                cms.getSitePath(configData.getDefaultModelPage().getResource()),
981                                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
982                        } catch (CmsException e) {
983                            LOG.warn(e.getLocalizedMessage(), e);
984                        }
985                    }
986                }
987                if ((modelResource == null) && !newResourceInfos.isEmpty()) {
988                    try {
989                        modelResource = cms.readResource(newResourceInfos.get(0).getCopyResourceId());
990                    } catch (CmsException e) {
991                        LOG.warn(e.getLocalizedMessage(), e);
992                    }
993                }
994                CmsFormatterConfigurationCacheState formatterState = OpenCms.getADEManager().getCachedFormatters(
995                    cms.getRequestContext().getCurrentProject().isOnlineProject());
996                if (modelResource != null) {
997                    resourceTypeInfos = getResourceTypeInfos(
998                        getCmsObject(),
999                        configData.getResourceTypes(),
1000                        configData.getFunctionReferences(),
1001                        configData.getDynamicFunctionAvailability(formatterState),
1002                        modelResource,
1003                        getWorkplaceLocale());
1004                    try {
1005                        defaultNewInfo = createNewResourceInfo(cms, modelResource, getWorkplaceLocale());
1006                    } catch (CmsException e) {
1007                        LOG.warn(e.getLocalizedMessage(), e);
1008                    }
1009                }
1010                canEditDetailPages = !(configData.isModuleConfiguration());
1011            }
1012            if (isOnlineProject) {
1013                noEdit = Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_SITEMAP_NO_EDIT_ONLINE_0);
1014            }
1015            List<String> allPropNames = getPropertyNames(cms);
1016            String returnCode = getRequest().getParameter(CmsCoreData.PARAM_RETURNCODE);
1017            if (!isValidReturnCode(returnCode)) {
1018                returnCode = null;
1019            }
1020            cms.getRequestContext().getSiteRoot();
1021            String aliasImportUrl = OpenCms.getLinkManager().substituteLinkForRootPath(cms, ALIAS_IMPORT_PATH);
1022            boolean canEditAliases = OpenCms.getAliasManager().hasPermissionsForMassEdit(cms, siteRoot);
1023            List<CmsListInfoBean> subsitemapFolderTypeInfos = collectSitemapTypeInfos(cms, configData);
1024
1025            // evaluate the editor mode
1026            EditorMode editorMode = CmsADESessionCache.getCache(getRequest(), getCmsObject()).getSitemapEditorMode();
1027            if ((editorMode == null) || (editorMode == EditorMode.compareLocales)) {
1028                editorMode = EditorMode.navigation;
1029            }
1030
1031            String basePath = configData.getBasePath();
1032
1033            if (!cms.existsResource(
1034                cms.getRequestContext().removeSiteRoot(basePath),
1035                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
1036                basePath = cms.getRequestContext().getSiteRoot();
1037            }
1038            Boolean showModelEditConfirm = (Boolean)getRequest().getSession().getAttribute(
1039                ATTR_SHOW_MODEL_EDIT_CONFIRM);
1040            if (showModelEditConfirm == null) {
1041                showModelEditConfirm = Boolean.TRUE;
1042            }
1043            boolean showLocaleComparison = !(getCmsObject().getRequestContext().getCurrentProject().isOnlineProject())
1044                && (site != null)
1045                && (site.getMainTranslationLocale(null) != null);
1046
1047            result = new CmsSitemapData(
1048                (new CmsTemplateFinder(cms)).getTemplates(),
1049                propertyConfig,
1050                getClipboardData(),
1051                CmsCoreService.getContextMenuEntries(
1052                    cms,
1053                    configData.getResource().getStructureId(),
1054                    AdeContext.sitemapeditor,
1055                    new HashMap<>()),
1056                parentProperties,
1057                allPropNames,
1058                exportRfsPrefix,
1059                isSecure,
1060                noEdit,
1061                isDisplayToolbar(getRequest()),
1062                defaultNewInfo,
1063                newResourceInfos,
1064                createResourceTypeInfo(OpenCms.getResourceManager().getResourceType(RECOURCE_TYPE_NAME_REDIRECT), null),
1065                createNavigationLevelTypeInfo(),
1066                getSitemapInfo(basePath),
1067                parentSitemap,
1068                getRootEntry(basePath, CmsResource.getFolderPath(openPath)),
1069                openPath,
1070                30,
1071                detailPages,
1072                resourceTypeInfos,
1073                returnCode,
1074                canEditDetailPages && canManageDetailPages,
1075                aliasImportUrl,
1076                canEditAliases,
1077                OpenCms.getWorkplaceManager().getDefaultUserSettings().getSubsitemapCreationMode() == CmsDefaultUserSettings.SubsitemapCreationMode.createfolder,
1078                OpenCms.getRoleManager().hasRole(cms, CmsRole.GALLERY_EDITOR),
1079                OpenCms.getRoleManager().hasRole(cms, CmsRole.CATEGORY_EDITOR),
1080                subsitemapFolderTypeInfos,
1081                editorMode,
1082                defaultGalleryFolder,
1083                showModelEditConfirm.booleanValue());
1084
1085            CmsUUID rootId = cms.readResource("/", CmsResourceFilter.ALL).getStructureId();
1086            result.setSiteRootId(rootId);
1087            result.setLocaleComparisonEnabled(showLocaleComparison);
1088            result.setCanManageDetailPages(canManageDetailPages);
1089
1090            boolean allowCreateNestedGalleries = Boolean.parseBoolean(
1091                "" + OpenCms.getRuntimeProperty("ade.sitemap.allowCreateNestedGalleries"));
1092            result.setAllowCreateNestedGalleries(allowCreateNestedGalleries);
1093
1094        } catch (Throwable e) {
1095            error(e);
1096        }
1097        return result;
1098    }
1099
1100    /**
1101     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#prepareReloadSitemap(org.opencms.util.CmsUUID, org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode)
1102     */
1103    public String prepareReloadSitemap(CmsUUID rootId, EditorMode mode) throws CmsRpcException {
1104
1105        try {
1106            CmsObject cms = getCmsObject();
1107            CmsResource res = cms.readResource(CmsADEManager.PATH_SITEMAP_EDITOR_JSP);
1108
1109            CmsResource target = cms.readResource(rootId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1110            String targetRootPath = OpenCms.getADEManager().getSubSiteRoot(cms, target.getRootPath());
1111            CmsSite targetSite = OpenCms.getSiteManager().getSiteForRootPath(targetRootPath);
1112            CmsADESessionCache.getCache(getRequest(), getCmsObject()).setSitemapEditorMode(mode);
1113            if (targetSite != null) {
1114                cms.getRequestContext().setSiteRoot(targetSite.getSiteRoot());
1115                String path = cms.getRequestContext().removeSiteRoot(targetRootPath);
1116                String link = OpenCms.getLinkManager().substituteLink(cms, res);
1117                link = link + "?path=" + path;
1118                return link;
1119            }
1120            return null;
1121        } catch (Exception e) {
1122            error(e);
1123            return null;
1124        }
1125    }
1126
1127    /**
1128     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#removeModelPage(java.lang.String, org.opencms.util.CmsUUID)
1129     */
1130    public void removeModelPage(String entryPointUri, CmsUUID id) throws CmsRpcException {
1131
1132        try {
1133            CmsObject cms = getCmsObject();
1134            CmsResource rootResource = cms.readResource(entryPointUri);
1135            CmsModelPageHelper helper = new CmsModelPageHelper(getCmsObject(), rootResource);
1136            String configPath = CmsStringUtil.joinPaths(entryPointUri, ".content/.config");
1137            CmsResource configResource = cms.readResource(configPath);
1138            helper.removeModelPage(configResource, id);
1139            OpenCms.getADEManager().waitForCacheUpdate(false);
1140        } catch (Throwable e) {
1141            error(e);
1142
1143        }
1144
1145    }
1146
1147    /**
1148     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#save(java.lang.String, org.opencms.ade.sitemap.shared.CmsSitemapChange)
1149     */
1150    public CmsSitemapChange save(String entryPoint, CmsSitemapChange change) throws CmsRpcException {
1151
1152        CmsSitemapChange result = null;
1153        try {
1154            result = saveInternal(entryPoint, change);
1155        } catch (Exception e) {
1156            error(e);
1157        }
1158        return result;
1159    }
1160
1161    /**
1162     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#saveAliases(org.opencms.gwt.shared.alias.CmsAliasSaveValidationRequest)
1163     */
1164    public CmsAliasEditValidationReply saveAliases(CmsAliasSaveValidationRequest saveRequest) throws CmsRpcException {
1165
1166        CmsObject cms = getCmsObject();
1167        CmsAliasBulkEditHelper helper = new CmsAliasBulkEditHelper(cms);
1168        try {
1169            return helper.saveAliases(saveRequest);
1170        } catch (Exception e) {
1171            error(e);
1172            return null;
1173        }
1174    }
1175
1176    /**
1177     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#savePropertiesForLocaleCompareMode(org.opencms.util.CmsUUID, java.lang.String, java.util.List, boolean)
1178     */
1179    public void savePropertiesForLocaleCompareMode(
1180        CmsUUID id,
1181        String newUrlName,
1182        List<CmsPropertyModification> propertyChanges,
1183        boolean editedName)
1184    throws CmsRpcException {
1185
1186        try {
1187            CmsObject cms = getCmsObject();
1188            CmsResource ownRes = cms.readResource(id, CmsResourceFilter.IGNORE_EXPIRATION);
1189            CmsResource defaultFileRes = cms.readDefaultFile("" + id);
1190            boolean shallow = !editedName;
1191            try (AutoCloseable c = CmsLockUtil.withLockedResources(cms, shallow, ownRes, defaultFileRes)) {
1192                updateProperties(cms, ownRes, defaultFileRes, propertyChanges);
1193                if (editedName) {
1194                    String parent = CmsResource.getParentFolder(ownRes.getRootPath());
1195                    newUrlName = CmsFileUtil.removeTrailingSeparator(newUrlName);
1196                    String newPath = CmsStringUtil.joinPaths(parent, newUrlName);
1197                    CmsObject rootCms = OpenCms.initCmsObject(cms);
1198                    rootCms.getRequestContext().setSiteRoot("");
1199                    if (!CmsPath.equal(ownRes.getRootPath(), newPath)) {
1200                        rootCms.moveResource(ownRes.getRootPath(), newPath);
1201                    }
1202                }
1203            }
1204        } catch (Exception e) {
1205            error(e);
1206        }
1207    }
1208
1209    /**
1210     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#saveSitemapAttributes(org.opencms.util.CmsUUID, java.util.Map)
1211     */
1212    public void saveSitemapAttributes(CmsUUID rootId, Map<String, String> attributes) throws CmsRpcException {
1213
1214        CmsResource configResource = null;
1215        try {
1216            CmsObject cms = getCmsObject();
1217            CmsResource root = cms.readResource(rootId);
1218            String sitePath = cms.getSitePath(root);
1219            String configPath = CmsStringUtil.joinPaths(sitePath, "/.content/.config");
1220            configResource = cms.readResource(configPath);
1221            CmsXmlContent configContent = CmsXmlContentFactory.unmarshal(cms, cms.readFile(configResource));
1222            CmsSitemapAttributeUpdater updater = new CmsSitemapAttributeUpdater(cms, configContent);
1223            if (updater.saveAttributesFromEditorDialog(attributes)) {
1224                byte[] newContentBytes = configContent.marshal();
1225                CmsFile file = configContent.getFile();
1226                file.setContents(newContentBytes);
1227                cms.writeFile(file);
1228                OpenCms.getADEManager().waitForCacheUpdate(false);
1229            }
1230        } catch (Exception e) {
1231            error(e);
1232        } finally {
1233            try {
1234                if (configResource != null) {
1235                    getCmsObject().unlockResource(configResource);
1236                }
1237            } catch (Exception e) {
1238                LOG.debug(e.getLocalizedMessage(), e);
1239            }
1240        }
1241
1242    }
1243
1244    /**
1245     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#setDefaultModel(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID)
1246     */
1247    public CmsModelInfo setDefaultModel(CmsUUID rootId, CmsUUID modelId) throws CmsRpcException {
1248
1249        CmsModelInfo result = null;
1250        try {
1251            CmsObject cms = getCmsObject();
1252            CmsResource rootResource = cms.readResource(rootId);
1253            CmsModelPageHelper modelPageHelper = new CmsModelPageHelper(getCmsObject(), rootResource);
1254            CmsResource configFile = OpenCms.getADEManager().lookupConfiguration(
1255                cms,
1256                rootResource.getRootPath()).getResource();
1257            ensureLock(configFile);
1258            result = modelPageHelper.setDefaultModelPage(configFile, modelId);
1259            tryUnlock(configFile);
1260        } catch (Throwable e) {
1261            error(e);
1262        }
1263        return result;
1264    }
1265
1266    /**
1267     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#setEditorMode(org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode)
1268     */
1269    public void setEditorMode(EditorMode editorMode) {
1270
1271        CmsADESessionCache.getCache(getRequest(), getCmsObject()).setSitemapEditorMode(editorMode);
1272    }
1273
1274    /**
1275     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#updateAliasEditorStatus(boolean)
1276     */
1277    public void updateAliasEditorStatus(boolean editing) {
1278
1279        CmsObject cms = getCmsObject();
1280        if (editing) {
1281            aliasEditorLockTable.update(cms, cms.getRequestContext().getSiteRoot());
1282        } else {
1283            aliasEditorLockTable.clear(cms, cms.getRequestContext().getSiteRoot());
1284        }
1285    }
1286
1287    /**
1288     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#validateAliases(org.opencms.gwt.shared.alias.CmsAliasEditValidationRequest)
1289     */
1290    public CmsAliasEditValidationReply validateAliases(CmsAliasEditValidationRequest validationRequest) {
1291
1292        CmsObject cms = getCmsObject();
1293        CmsAliasBulkEditHelper helper = new CmsAliasBulkEditHelper(cms);
1294        return helper.validateAliases(validationRequest);
1295    }
1296
1297    /**
1298     * @see org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService#validateRewriteAliases(org.opencms.gwt.shared.alias.CmsRewriteAliasValidationRequest)
1299     */
1300    public CmsRewriteAliasValidationReply validateRewriteAliases(CmsRewriteAliasValidationRequest validationRequest) {
1301
1302        CmsRewriteAliasValidationReply result = new CmsRewriteAliasValidationReply();
1303        for (CmsRewriteAliasTableRow editedRow : validationRequest.getEditedRewriteAliases()) {
1304            try {
1305                String patternString = editedRow.getPatternString();
1306                Pattern.compile(patternString);
1307            } catch (PatternSyntaxException e) {
1308                result.addError(editedRow.getId(), "Syntax error in regular expression: " + e.getMessage());
1309            }
1310        }
1311        return result;
1312    }
1313
1314    /**
1315     * Locks the given resource with a temporary, if not already locked by the current user.
1316     * Will throw an exception if the resource could not be locked for the current user.<p>
1317     *
1318     * @param resource the resource to lock
1319     *
1320     * @return the assigned lock
1321     *
1322     * @throws CmsException if the resource could not be locked
1323     */
1324    protected LockInfo ensureLockAndGetInfo(CmsResource resource) throws CmsException {
1325
1326        CmsObject cms = getCmsObject();
1327        boolean justLocked = false;
1328        List<CmsResource> blockingResources = cms.getBlockingLockedResources(resource);
1329        if ((blockingResources != null) && !blockingResources.isEmpty()) {
1330            throw new CmsException(
1331                org.opencms.gwt.Messages.get().container(
1332                    org.opencms.gwt.Messages.ERR_RESOURCE_HAS_BLOCKING_LOCKED_CHILDREN_1,
1333                    cms.getSitePath(resource)));
1334        }
1335        CmsUser user = cms.getRequestContext().getCurrentUser();
1336        CmsLock lock = cms.getLock(resource);
1337        if (!lock.isOwnedBy(user)) {
1338            cms.lockResourceTemporary(resource);
1339            lock = cms.getLock(resource);
1340            justLocked = true;
1341        } else if (!lock.isOwnedInProjectBy(user, cms.getRequestContext().getCurrentProject())) {
1342            cms.changeLock(resource);
1343            lock = cms.getLock(resource);
1344            justLocked = true;
1345        }
1346        return new LockInfo(lock, justLocked);
1347    }
1348
1349    /**
1350     * Internal method for saving a sitemap.<p>
1351     *
1352     * @param entryPoint the URI of the sitemap to save
1353     * @param change the change to save
1354     *
1355     * @return list of changed sitemap entries
1356     *
1357     * @throws CmsException if something goes wrong
1358     */
1359    protected CmsSitemapChange saveInternal(String entryPoint, CmsSitemapChange change) throws CmsException {
1360
1361        ensureSession();
1362        switch (change.getChangeType()) {
1363            case clipboardOnly:
1364                // do nothing
1365                break;
1366            case remove:
1367                change = removeEntryFromNavigation(change);
1368                break;
1369            case undelete:
1370                change = undelete(change);
1371                break;
1372            default:
1373                change = applyChange(entryPoint, change);
1374        }
1375        setClipboardData(change.getClipBoardData());
1376        return change;
1377    }
1378
1379    /**
1380     * Gets the properties of a resource as a map of client properties.<p>
1381     *
1382     * @param cms the CMS context to use
1383     * @param res the resource whose properties to read
1384     * @param search true if the inherited properties should be read
1385     *
1386     * @return the client properties as a map
1387     *
1388     * @throws CmsException if something goes wrong
1389     */
1390    Map<String, CmsClientProperty> getClientProperties(CmsObject cms, CmsResource res, boolean search)
1391    throws CmsException {
1392
1393        List<CmsProperty> props = cms.readPropertyObjects(res, search);
1394        Map<String, CmsClientProperty> result = createClientProperties(props, false);
1395        return result;
1396    }
1397
1398    /**
1399     * Adds a function detail element to a container page.<p>
1400     *
1401     * @param cms the current CMS context
1402     * @param page the container page which should be changed
1403     * @param containerName the name of the container which should be used for function detail elements
1404     * @param elementId the structure id of the element to add
1405     * @param formatterId the structure id of the formatter for the element
1406     *
1407     * @throws CmsException if something goes wrong
1408     */
1409    private void addFunctionDetailElement(
1410        CmsObject cms,
1411        CmsXmlContainerPage page,
1412        String containerName,
1413        CmsUUID elementId,
1414        CmsUUID formatterId)
1415    throws CmsException {
1416
1417        CmsContainerPageBean bean = page.getContainerPage(cms);
1418        List<CmsContainerBean> containerBeans = new ArrayList<CmsContainerBean>();
1419        Collection<CmsContainerBean> originalContainers = bean.getContainers().values();
1420        if ((containerName == null) && !originalContainers.isEmpty()) {
1421            CmsContainerBean firstContainer = originalContainers.iterator().next();
1422            containerName = firstContainer.getName();
1423        }
1424        boolean foundContainer = false;
1425        for (CmsContainerBean cntBean : originalContainers) {
1426            boolean isDetailTarget = cntBean.getName().equals(containerName);
1427            if (isDetailTarget && !foundContainer) {
1428                foundContainer = true;
1429                List<CmsContainerElementBean> newElems = new ArrayList<CmsContainerElementBean>();
1430                newElems.addAll(cntBean.getElements());
1431                CmsContainerElementBean newElement = new CmsContainerElementBean(
1432                    elementId,
1433                    formatterId,
1434                    new HashMap<String, String>(),
1435                    false);
1436                newElems.add(0, newElement);
1437                CmsContainerBean newCntBean = cntBean.copyWithNewElements(newElems);
1438                containerBeans.add(newCntBean);
1439            } else {
1440                containerBeans.add(cntBean);
1441            }
1442        }
1443        if (!foundContainer) {
1444            throw new CmsException(
1445                Messages.get().container(Messages.ERR_NO_FUNCTION_DETAIL_CONTAINER_1, page.getFile().getRootPath()));
1446        }
1447        CmsContainerPageBean bean2 = new CmsContainerPageBean(new ArrayList<CmsContainerBean>(containerBeans));
1448        page.writeContainerPage(cms, bean2);
1449
1450    }
1451
1452    /**
1453     * Applys the given change to the VFS.<p>
1454     *
1455     * @param entryPoint the sitemap entry-point
1456     * @param change the change
1457     *
1458     * @return the updated entry
1459     *
1460     * @throws CmsException if something goes wrong
1461     */
1462    private CmsSitemapChange applyChange(String entryPoint, CmsSitemapChange change) throws CmsException {
1463
1464        CmsObject cms = getCmsObject();
1465        CmsResource configFile = null;
1466        // lock the config file first, to avoid half done changes
1467        if (change.hasDetailPageInfos()) {
1468            CmsADEConfigData configData = OpenCms.getADEManager().lookupConfiguration(
1469                cms,
1470                cms.getRequestContext().addSiteRoot(entryPoint));
1471            if (!configData.isModuleConfiguration() && (configData.getResource() != null)) {
1472                configFile = configData.getResource();
1473            }
1474            if (configFile != null) {
1475                ensureLock(configFile);
1476            }
1477        }
1478        if (change.isNew()) {
1479            CmsClientSitemapEntry newEntry = createNewEntry(entryPoint, change);
1480            change.setUpdatedEntry(newEntry);
1481            change.setEntryId(newEntry.getId());
1482        } else if (change.getChangeType() == ChangeType.delete) {
1483            delete(change);
1484        } else if (change.getEntryId() != null) {
1485            modifyEntry(change);
1486        }
1487        if (change.hasDetailPageInfos() && (configFile != null)) {
1488            saveDetailPages(change.getDetailPageInfos(), configFile, change.getEntryId(), change.getUpdatedEntry());
1489            tryUnlock(configFile);
1490        }
1491
1492        return change;
1493    }
1494
1495    /**
1496     * Changes the navigation for a moved entry and its neighbors.<p>
1497     *
1498     * @param change the sitemap change
1499     * @param entryFolder the moved entry
1500     *
1501     * @throws CmsException if something goes wrong
1502     */
1503    private void applyNavigationChanges(CmsSitemapChange change, CmsResource entryFolder) throws CmsException {
1504
1505        CmsObject cms = getCmsObject();
1506        String parentPath = null;
1507        if (change.hasNewParent()) {
1508            CmsResource parent = cms.readResource(change.getParentId());
1509            parentPath = cms.getSitePath(parent);
1510        } else {
1511            parentPath = CmsResource.getParentFolder(cms.getSitePath(entryFolder));
1512        }
1513        List<CmsJspNavElement> navElements = getNavBuilder().getNavigationForFolder(
1514            parentPath,
1515            Visibility.all,
1516            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1517        CmsSitemapNavPosCalculator npc = new CmsSitemapNavPosCalculator(navElements, entryFolder, change.getPosition());
1518        List<CmsJspNavElement> navs = npc.getNavigationChanges();
1519        List<CmsResource> needToUnlock = new ArrayList<CmsResource>();
1520
1521        try {
1522            for (CmsJspNavElement nav : navs) {
1523                LockInfo lockInfo = ensureLockAndGetInfo(nav.getResource());
1524                if (!nav.getResource().equals(entryFolder) && lockInfo.wasJustLocked()) {
1525                    needToUnlock.add(nav.getResource());
1526                }
1527            }
1528            for (CmsJspNavElement nav : navs) {
1529                CmsProperty property = new CmsProperty(
1530                    CmsPropertyDefinition.PROPERTY_NAVPOS,
1531                    "" + nav.getNavPosition(),
1532                    null);
1533                cms.writePropertyObject(cms.getSitePath(nav.getResource()), property);
1534            }
1535        } finally {
1536            for (CmsResource lockedRes : needToUnlock) {
1537                try {
1538                    cms.unlockResource(lockedRes);
1539                } catch (CmsException e) {
1540                    // we catch this because we still want to unlock the other resources
1541                    LOG.error(e.getLocalizedMessage(), e);
1542                }
1543            }
1544        }
1545    }
1546
1547    /**
1548     * Builds the list info bean for a resource type which can be used as a sub-sitemap folder.<p>
1549     *
1550     * @param sitemapType the sitemap folder type
1551     *
1552     * @return the list info bean for the given type
1553     */
1554    private CmsListInfoBean buildSitemapTypeInfo(I_CmsResourceType sitemapType) {
1555
1556        CmsListInfoBean result = new CmsListInfoBean();
1557        CmsWorkplaceManager wm = OpenCms.getWorkplaceManager();
1558        String typeName = sitemapType.getTypeName();
1559        Locale wpLocale = wm.getWorkplaceLocale(getCmsObject());
1560        String title = typeName;
1561        String description = "";
1562        try {
1563            title = CmsWorkplaceMessages.getResourceTypeName(wpLocale, typeName);
1564        } catch (Throwable e) {
1565            LOG.warn(e.getLocalizedMessage(), e);
1566        }
1567        try {
1568            description = CmsWorkplaceMessages.getResourceTypeDescription(wpLocale, typeName);
1569        } catch (Throwable e) {
1570            LOG.warn(e.getLocalizedMessage(), e);
1571        }
1572        result.setResourceType(typeName);
1573        result.setTitle(title);
1574        result.setSubTitle(description);
1575        return result;
1576    }
1577
1578    /**
1579     * Returns all available gallery folder resource types.<p>
1580     *
1581     * @return the gallery folder resource types
1582     */
1583    @SuppressWarnings("deprecation")
1584    private List<CmsGalleryType> collectGalleryTypes() {
1585
1586        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject());
1587        Map<String, List<String>> galleryContentTypes = new HashMap<String, List<String>>();
1588        List<CmsGalleryType> result = new ArrayList<CmsGalleryType>();
1589        for (I_CmsResourceType resourceType : OpenCms.getResourceManager().getResourceTypes()) {
1590            if (resourceType instanceof CmsResourceTypeFolderExtended) {
1591                // found a configured extended folder resource type
1592                CmsResourceTypeFolderExtended galleryType = (CmsResourceTypeFolderExtended)resourceType;
1593                String folderClassName = galleryType.getFolderClassName();
1594                if (CmsStringUtil.isNotEmpty(folderClassName)) {
1595                    // only process this as a gallery if the folder name is not empty
1596                    try {
1597                        // check, if the folder class is a subclass of A_CmsGallery
1598                        if (A_CmsAjaxGallery.class.isAssignableFrom(Class.forName(folderClassName))) {
1599                            CmsGalleryType gallery = new CmsGalleryType();
1600                            gallery.setTypeId(resourceType.getTypeId());
1601                            gallery.setResourceType(resourceType.getTypeName());
1602                            gallery.setTitle(
1603                                CmsWorkplaceMessages.getResourceTypeName(wpLocale, resourceType.getTypeName()));
1604                            gallery.setSubTitle(
1605                                CmsWorkplaceMessages.getResourceTypeDescription(wpLocale, resourceType.getTypeName()));
1606                            gallery.setBigIconClasses(
1607                                CmsIconUtil.getIconClasses(resourceType.getTypeName(), null, false));
1608                            result.add(gallery);
1609                        }
1610                    } catch (Exception e) {
1611                        this.log(e.getLocalizedMessage(), e);
1612                    }
1613                }
1614            } else {
1615                List<I_CmsResourceType> galleryTypes = resourceType.getGalleryTypes();
1616                if ((galleryTypes != null) && !galleryTypes.isEmpty()) {
1617                    for (I_CmsResourceType galleryType : galleryTypes) {
1618                        List<String> typeList = galleryContentTypes.get(galleryType.getTypeName());
1619                        if (typeList == null) {
1620                            typeList = new ArrayList<String>();
1621                            galleryContentTypes.put(galleryType.getTypeName(), typeList);
1622                        }
1623                        typeList.add(resourceType.getTypeName());
1624                    }
1625                }
1626            }
1627        }
1628        for (CmsGalleryType galleryType : result) {
1629            galleryType.setContentTypeNames(galleryContentTypes.get(galleryType.getResourceType()));
1630        }
1631        return result;
1632    }
1633
1634    /**
1635     * Gets the information for the available sitemap folder types.<p>
1636     *
1637     * @param cms the current CMS context
1638     * @param configData the configuration data for the current subsitemap
1639     * @return the list info beans corresponding to available sitemap folder types
1640     */
1641    private List<CmsListInfoBean> collectSitemapTypeInfos(CmsObject cms, CmsADEConfigData configData) {
1642
1643        List<CmsListInfoBean> subsitemapFolderTypeInfos = new ArrayList<CmsListInfoBean>();
1644        List<Integer> sitemapTypeIds = CmsResourceTypeFolderSubSitemap.getSubSitemapResourceTypeIds();
1645        String checkPath = configData.getBasePath();
1646        if (checkPath != null) {
1647            checkPath = cms.getRequestContext().removeSiteRoot(checkPath);
1648        } else {
1649            checkPath = "/";
1650        }
1651        CmsResource checkResource = null;
1652        try {
1653            checkResource = cms.readResource(checkPath);
1654        } catch (CmsException e) {
1655            LOG.warn(e.getLocalizedMessage(), e);
1656        }
1657
1658        for (Integer typeId : sitemapTypeIds) {
1659            try {
1660                I_CmsResourceType sitemapType = OpenCms.getResourceManager().getResourceType(typeId.intValue());
1661                String typeName = sitemapType.getTypeName();
1662                CmsExplorerTypeSettings explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(typeName);
1663
1664                // if explorer type and check resource available, perform a permission check
1665                if ((explorerType != null) && (checkResource != null)) {
1666                    try {
1667                        CmsExplorerTypeAccess access = explorerType.getAccess();
1668                        if (!access.getPermissions(cms, checkResource).requiresControlPermission()) {
1669                            continue;
1670                        }
1671                    } catch (Exception e) {
1672                        LOG.error(e.getLocalizedMessage(), e);
1673                    }
1674                }
1675                CmsListInfoBean infoBean = buildSitemapTypeInfo(sitemapType);
1676                subsitemapFolderTypeInfos.add(infoBean);
1677
1678            } catch (CmsLoaderException e) {
1679                LOG.warn("Could not read sitemap folder type " + typeId + ": " + e.getLocalizedMessage(), e);
1680            }
1681        }
1682        return subsitemapFolderTypeInfos;
1683    }
1684
1685    /**
1686     * Creates a client alias bean from a server-side alias.<p>
1687     *
1688     * @param cms the current CMS context
1689     * @param alias the alias to convert
1690     *
1691     * @return the alias table row
1692     *
1693     * @throws CmsException if something goes wrong
1694     */
1695    private CmsAliasTableRow createAliasTableEntry(CmsObject cms, CmsAlias alias) throws CmsException {
1696
1697        CmsResource resource = cms.readResource(alias.getStructureId(), CmsResourceFilter.ALL);
1698        CmsAliasTableRow result = new CmsAliasTableRow();
1699        result.setStructureId(alias.getStructureId());
1700        result.setOriginalStructureId(alias.getStructureId());
1701        result.setAliasPath(alias.getAliasPath());
1702        result.setResourcePath(cms.getSitePath(resource));
1703        result.setKey((new CmsUUID()).toString());
1704        result.setMode(alias.getMode());
1705        result.setChanged(false);
1706        return result;
1707    }
1708
1709    /**
1710     * Creates a navigation level type info.<p>
1711     *
1712     * @return the navigation level type info bean
1713     *
1714     * @throws CmsException if reading the sub level redirect copy page fails
1715     */
1716    private CmsNewResourceInfo createNavigationLevelTypeInfo() throws CmsException {
1717
1718        String name = CmsResourceTypeFolder.getStaticTypeName();
1719        Locale locale = getWorkplaceLocale();
1720        String subtitle = Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_NAVIGATION_LEVEL_SUBTITLE_0);
1721        if (CmsStringUtil.isEmptyOrWhitespaceOnly(subtitle)) {
1722            subtitle = CmsWorkplaceMessages.getResourceTypeName(locale, name);
1723        }
1724        CmsNewResourceInfo result = new CmsNewResourceInfo(
1725            CmsResourceTypeFolder.getStaticTypeId(),
1726            name,
1727            Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_NAVIGATION_LEVEL_TITLE_0),
1728            subtitle,
1729            getCmsObject().readResource(SUB_LEVEL_REDIRECT_COPY_PAGE).getStructureId(),
1730            false,
1731            subtitle);
1732        result.setBigIconClasses(CmsIconUtil.ICON_NAV_LEVEL_BIG);
1733        result.setCreateParameter(CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER);
1734        return result;
1735    }
1736
1737    /**
1738     * Creates new content elements if required by the model page.<p>
1739     *
1740     * @param cms the cms context
1741     * @param page the page
1742     * @param sitePath the resource site path
1743     *
1744     * @throws CmsException when unable to create the content elements
1745     */
1746    private boolean createNewContainerElements(CmsObject cms, CmsMutableContainerPage page, String sitePath)
1747    throws CmsException {
1748
1749        boolean needsChanges = false;
1750        CmsADEConfigData configData = OpenCms.getADEManager().lookupConfiguration(cms, cms.addSiteRoot(sitePath));
1751        Locale contentLocale = OpenCms.getLocaleManager().getDefaultLocale(cms, CmsResource.getFolderPath(sitePath));
1752        CmsObject cloneCms = OpenCms.initCmsObject(cms);
1753        cloneCms.getRequestContext().setLocale(contentLocale);
1754        for (CmsMutableContainer container : page.containers()) {
1755            ListIterator<CmsContainerElementBean> iter = container.elements().listIterator();
1756            while (iter.hasNext()) {
1757                CmsContainerElementBean elem = iter.next();
1758                if (elem.isCreateNew() && !elem.isGroupContainer(cms) && !elem.isInheritedContainer(cms)) {
1759                    String typeName = OpenCms.getResourceManager().getResourceType(elem.getResource()).getTypeName();
1760                    CmsResourceTypeConfig typeConfig = configData.getResourceType(typeName);
1761                    if (typeConfig == null) {
1762                        throw new IllegalArgumentException(
1763                            "Can not copy template model element '"
1764                                + elem.getResource().getRootPath()
1765                                + "' because the resource type '"
1766                                + typeName
1767                                + "' is not available in this sitemap.");
1768                    }
1769
1770                    CmsResource newResource = typeConfig.createNewElement(
1771                        cloneCms,
1772                        elem.getResource(),
1773                        CmsResource.getParentFolder(cms.getRequestContext().addSiteRoot(sitePath)));
1774                    CmsContainerElementBean newBean = new CmsContainerElementBean(
1775                        newResource.getStructureId(),
1776                        elem.getFormatterId(),
1777                        elem.getIndividualSettings(),
1778                        false);
1779                    iter.set(newBean);
1780                    needsChanges = true;
1781                }
1782            }
1783        }
1784        return needsChanges;
1785    }
1786
1787    /**
1788     * Creates new content elements if required by the model page.<p>
1789     *
1790     * @param cms the cms context
1791     * @param page the page
1792     * @param sitePath the resource site path
1793     *
1794     * @throws CmsException when unable to create the content elements
1795     */
1796    private void createNewContainerElements(CmsObject cms, CmsXmlContainerPage page, String sitePath)
1797    throws CmsException {
1798
1799        CmsADEConfigData configData = OpenCms.getADEManager().lookupConfiguration(cms, cms.addSiteRoot(sitePath));
1800        Locale contentLocale = OpenCms.getLocaleManager().getDefaultLocale(cms, CmsResource.getFolderPath(sitePath));
1801        CmsObject cloneCms = OpenCms.initCmsObject(cms);
1802        cloneCms.getRequestContext().setLocale(contentLocale);
1803        CmsContainerPageBean pageBean = page.getContainerPage(cms);
1804        boolean needsChanges = false;
1805        List<CmsContainerBean> updatedContainers = new ArrayList<CmsContainerBean>();
1806        for (CmsContainerBean container : pageBean.getContainers().values()) {
1807            List<CmsContainerElementBean> updatedElements = new ArrayList<CmsContainerElementBean>();
1808            for (CmsContainerElementBean element : container.getElements()) {
1809                if (element.isCreateNew() && !element.isGroupContainer(cms) && !element.isInheritedContainer(cms)) {
1810                    needsChanges = true;
1811                    String typeName = OpenCms.getResourceManager().getResourceType(element.getResource()).getTypeName();
1812                    CmsResourceTypeConfig typeConfig = configData.getResourceType(typeName);
1813                    if (typeConfig == null) {
1814                        throw new IllegalArgumentException(
1815                            "Can not copy template model element '"
1816                                + element.getResource().getRootPath()
1817                                + "' because the resource type '"
1818                                + typeName
1819                                + "' is not available in this sitemap.");
1820                    }
1821
1822                    CmsResource newResource = typeConfig.createNewElement(
1823                        cloneCms,
1824                        element.getResource(),
1825                        CmsResource.getParentFolder(cms.getRequestContext().addSiteRoot(sitePath)));
1826                    CmsContainerElementBean newBean = new CmsContainerElementBean(
1827                        newResource.getStructureId(),
1828                        element.getFormatterId(),
1829                        element.getIndividualSettings(),
1830                        false);
1831                    updatedElements.add(newBean);
1832                } else {
1833                    updatedElements.add(element);
1834                }
1835            }
1836
1837            CmsContainerBean updatedContainer = container.copyWithNewElements(updatedElements);
1838            updatedContainers.add(updatedContainer);
1839        }
1840        if (needsChanges) {
1841            CmsContainerPageBean updatedPage = new CmsContainerPageBean(updatedContainers);
1842            page.writeContainerPage(cms, updatedPage);
1843        }
1844    }
1845
1846    /**
1847     * Creates a new page in navigation.<p>
1848     *
1849     * @param entryPoint the site-map entry-point
1850     * @param change the new change
1851     *
1852     * @return the updated entry
1853     *
1854     * @throws CmsException if something goes wrong
1855     */
1856    @SuppressWarnings("deprecation")
1857    private CmsClientSitemapEntry createNewEntry(String entryPoint, CmsSitemapChange change) throws CmsException {
1858
1859        CmsObject cms = getCmsObject();
1860        CmsClientSitemapEntry newEntry = null;
1861        if (change.getParentId() != null) {
1862            CmsResource parentFolder = cms.readResource(
1863                change.getParentId(),
1864                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1865            String entryPath = "";
1866            CmsResource entryFolder = null;
1867            CmsResource newRes = null;
1868            byte[] content = null;
1869            List<CmsProperty> properties = Collections.emptyList();
1870            CmsResource copyPage = null;
1871            if (change.getNewCopyResourceId() != null) {
1872                copyPage = cms.readResource(change.getNewCopyResourceId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1873                content = cms.readFile(copyPage).getContents();
1874                properties = cms.readPropertyObjects(copyPage, false);
1875            }
1876            List<CmsProperty> filteredProperties = new ArrayList<CmsProperty>();
1877            for (CmsProperty property : properties) {
1878                boolean filter = false;
1879                if (FILTER_PROPERTIES.contains(property.getName())) {
1880                    filter = true;
1881
1882                } else {
1883                    // filter localized versions also
1884                    for (String filterProp : FILTER_PROPERTIES) {
1885                        if (property.getName().startsWith(filterProp + "_")) {
1886                            filter = true;
1887                            break;
1888                        }
1889                    }
1890                }
1891                if (!filter) {
1892                    filteredProperties.add(property);
1893                }
1894            }
1895            properties = filteredProperties;
1896            if (isRedirectType(change.getNewResourceTypeId())) {
1897                entryPath = CmsStringUtil.joinPaths(cms.getSitePath(parentFolder), change.getName());
1898                newRes = cms.createResource(
1899                    entryPath,
1900                    change.getNewResourceTypeId(),
1901                    null,
1902                    Collections.singletonList(
1903                        new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, change.getName(), null)));
1904                cms.writePropertyObjects(newRes, generateInheritProperties(change, newRes));
1905                applyNavigationChanges(change, newRes);
1906            } else {
1907                String entryFolderPath = CmsStringUtil.joinPaths(cms.getSitePath(parentFolder), change.getName() + "/");
1908                boolean idWasNull = change.getEntryId() == null;
1909                // we don't really need to create a folder object here anymore.
1910                if (idWasNull) {
1911                    // need this for calculateNavPosition, even though the id will get overwritten
1912                    change.setEntryId(new CmsUUID());
1913                }
1914
1915                boolean isFunctionDetail = false;
1916                boolean isNavigationLevel = false;
1917                if (change.getCreateParameter() != null) {
1918                    if (CmsUUID.isValidUUID(change.getCreateParameter())) {
1919                        isFunctionDetail = true;
1920                    } else if (CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER.equals(change.getCreateParameter())) {
1921                        isNavigationLevel = true;
1922                    }
1923                }
1924                String folderType = CmsResourceTypeFolder.getStaticTypeName();
1925                String createSitemapFolderType = change.getCreateSitemapFolderType();
1926                if (createSitemapFolderType != null) {
1927                    folderType = createSitemapFolderType;
1928                }
1929
1930                int folderTypeId = OpenCms.getResourceManager().getResourceType(folderType).getTypeId();
1931                entryFolder = new CmsResource(
1932                    change.getEntryId(),
1933                    new CmsUUID(),
1934                    entryFolderPath,
1935                    folderTypeId,
1936                    true,
1937                    0,
1938                    cms.getRequestContext().getCurrentProject().getUuid(),
1939                    CmsResource.STATE_NEW,
1940                    System.currentTimeMillis(),
1941                    cms.getRequestContext().getCurrentUser().getId(),
1942                    System.currentTimeMillis(),
1943                    cms.getRequestContext().getCurrentUser().getId(),
1944                    CmsResource.DATE_RELEASED_DEFAULT,
1945                    CmsResource.DATE_EXPIRED_DEFAULT,
1946                    1,
1947                    0,
1948                    System.currentTimeMillis(),
1949                    0);
1950                List<CmsProperty> folderProperties = generateInheritProperties(change, entryFolder);
1951                if (isNavigationLevel) {
1952                    folderProperties.add(
1953                        new CmsProperty(
1954                            CmsPropertyDefinition.PROPERTY_DEFAULT_FILE,
1955                            CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER,
1956                            null));
1957                }
1958                entryFolder = cms.createResource(entryFolderPath, folderTypeId, null, folderProperties);
1959                if (createSitemapFolderType != null) {
1960                    createSitemapContentFolder(cms, entryFolder);
1961                }
1962                if (idWasNull) {
1963                    change.setEntryId(entryFolder.getStructureId());
1964                }
1965                applyNavigationChanges(change, entryFolder);
1966                entryPath = CmsStringUtil.joinPaths(entryFolderPath, "index.html");
1967                boolean isContainerPage = change.getNewResourceTypeId() == CmsResourceTypeXmlContainerPage.getContainerPageTypeIdSafely();
1968                if (isContainerPage && (copyPage != null)) {
1969
1970                    // do *NOT* get this from the cache, because we perform some destructive operation on the XML content
1971                    CmsXmlContainerPage page = CmsXmlContainerPageFactory.unmarshal(
1972                        cms,
1973                        cms.readFile(copyPage),
1974                        true,
1975                        true);
1976                    CmsContainerPageWrapper wrapper = new CmsContainerPageWrapper(cms, page);
1977                    if (isFunctionDetail) {
1978                        String functionDetailContainer = getFunctionDetailContainerName(parentFolder);
1979                        if (functionDetailContainer != null) {
1980                            CmsUUID functionStructureId = new CmsUUID(change.getCreateParameter());
1981                            CmsResource functionRes = cms.readResource(
1982                                functionStructureId,
1983                                CmsResourceFilter.IGNORE_EXPIRATION);
1984                            CmsResource functionFormatter;
1985                            if (OpenCms.getResourceManager().matchResourceType(
1986                                CmsResourceTypeFunctionConfig.TYPE_NAME,
1987                                functionRes.getTypeId())) {
1988                                functionFormatter = cms.readResource(CmsResourceTypeFunctionConfig.FORMATTER_PATH);
1989                            } else {
1990                                functionFormatter = cms.readResource(
1991                                    CmsResourceTypeFunctionConfig.FORMATTER_PATH,
1992                                    CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1993                            }
1994                            if (!wrapper.addElementToContainer(
1995                                functionDetailContainer,
1996                                new CmsContainerElementBean(
1997                                    functionStructureId,
1998                                    functionFormatter.getStructureId(),
1999                                    new HashMap<>(),
2000                                    false))) {
2001
2002                                throw new CmsException(
2003                                    Messages.get().container(
2004                                        Messages.ERR_NO_FUNCTION_DETAIL_CONTAINER_1,
2005                                        page.getFile().getRootPath()));
2006                            }
2007                        } else {
2008                            LOG.debug("function detail container is null for " + parentFolder.getRootPath());
2009                        }
2010                    }
2011                    createNewContainerElements(cms, wrapper.page(), entryPath);
2012                    content = wrapper.marshal();
2013                }
2014                newRes = cms.createResource(
2015                    entryPath,
2016                    copyPage != null ? copyPage.getTypeId() : change.getNewResourceTypeId(),
2017                    content,
2018                    properties);
2019                cms.writePropertyObjects(newRes, generateOwnProperties(change));
2020            }
2021
2022            if (entryFolder != null) {
2023                tryUnlock(entryFolder);
2024                String sitePath = cms.getSitePath(entryFolder);
2025                newEntry = toClientEntry(
2026                    getNavBuilder().getNavigationForResource(sitePath, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED),
2027                    false);
2028                newEntry.setSubEntries(getChildren(sitePath, 1, null), null);
2029                newEntry.setChildrenLoadedInitially(true);
2030            }
2031            if (newRes != null) {
2032                tryUnlock(newRes);
2033            }
2034            if (newEntry == null) {
2035                newEntry = toClientEntry(
2036                    getNavBuilder().getNavigationForResource(
2037                        cms.getSitePath(newRes),
2038                        CmsResourceFilter.ONLY_VISIBLE_NO_DELETED),
2039                    false);
2040            }
2041            // mark position as not set
2042            newEntry.setPosition(-1);
2043            newEntry.setNew(true);
2044            change.getClipBoardData().getModifications().remove(null);
2045            change.getClipBoardData().getModifications().put(newEntry.getId(), newEntry);
2046        }
2047        return newEntry;
2048    }
2049
2050    /**
2051     * Creates a new resource info to a given model page resource.<p>
2052     *
2053     * @param cms the current CMS context
2054     * @param modelResource the model page resource
2055     * @param locale the locale used for retrieving descriptions/titles
2056     *
2057     * @return the new resource info
2058     *
2059     * @throws CmsException if something goes wrong
2060     */
2061    private CmsNewResourceInfo createNewResourceInfo(CmsObject cms, CmsResource modelResource, Locale locale)
2062    throws CmsException {
2063
2064        // if model page got overwritten by another resource, reread from site path
2065        if (!cms.existsResource(modelResource.getStructureId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
2066            modelResource = cms.readResource(cms.getSitePath(modelResource), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2067        }
2068        int typeId = modelResource.getTypeId();
2069        String name = OpenCms.getResourceManager().getResourceType(typeId).getTypeName();
2070        String title = cms.readPropertyObject(
2071            modelResource,
2072            CmsPropertyDefinition.PROPERTY_TITLE,
2073            false,
2074            locale).getValue();
2075        String description = cms.readPropertyObject(
2076            modelResource,
2077            CmsPropertyDefinition.PROPERTY_DESCRIPTION,
2078            false,
2079            locale).getValue();
2080
2081        boolean editable = false;
2082        try {
2083            CmsResource freshModelResource = cms.readResource(
2084                modelResource.getStructureId(),
2085                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2086            editable = cms.hasPermissions(
2087                freshModelResource,
2088                CmsPermissionSet.ACCESS_WRITE,
2089                false,
2090                CmsResourceFilter.DEFAULT);
2091        } catch (CmsException e) {
2092            LOG.warn(e.getLocalizedMessage(), e);
2093        }
2094        CmsNewResourceInfo info = new CmsNewResourceInfo(
2095            typeId,
2096            name,
2097            title,
2098            description,
2099            modelResource.getStructureId(),
2100            editable,
2101            description);
2102        info.setBigIconClasses(CmsIconUtil.getIconClasses(name, null, false));
2103        Float navpos = null;
2104        try {
2105            CmsProperty navposProp = cms.readPropertyObject(modelResource, CmsPropertyDefinition.PROPERTY_NAVPOS, true);
2106            String navposStr = navposProp.getValue();
2107            if (navposStr != null) {
2108                try {
2109                    navpos = Float.valueOf(navposStr);
2110                } catch (NumberFormatException e) {
2111                    // noop
2112                }
2113            }
2114        } catch (CmsException e) {
2115            LOG.warn(e.getLocalizedMessage(), e);
2116        }
2117
2118        info.setNavPos(navpos);
2119        info.setDate(
2120            CmsDateUtil.getDate(new Date(modelResource.getDateLastModified()), DateFormat.LONG, getWorkplaceLocale()));
2121        info.setVfsPath(modelResource.getRootPath());
2122        return info;
2123    }
2124
2125    /**
2126     * Creates a resource type info bean for a given resource type.<p>
2127     *
2128     * @param resType the resource type
2129     * @param copyResource the structure id of the copy resource
2130     *
2131     * @return the resource type info bean
2132     */
2133    @SuppressWarnings("deprecation")
2134    private CmsNewResourceInfo createResourceTypeInfo(I_CmsResourceType resType, CmsResource copyResource) {
2135
2136        String name = resType.getTypeName();
2137        Locale locale = getWorkplaceLocale();
2138        String subtitle = CmsWorkplaceMessages.getResourceTypeDescription(locale, name);
2139        if (CmsStringUtil.isEmptyOrWhitespaceOnly(subtitle)) {
2140            subtitle = CmsWorkplaceMessages.getResourceTypeName(locale, name);
2141        }
2142        if (copyResource != null) {
2143            CmsNewResourceInfo info = new CmsNewResourceInfo(
2144                copyResource.getTypeId(),
2145                name,
2146                CmsWorkplaceMessages.getResourceTypeName(locale, name),
2147                CmsWorkplaceMessages.getResourceTypeDescription(locale, name),
2148                copyResource.getStructureId(),
2149                false,
2150                subtitle);
2151            info.setBigIconClasses(CmsIconUtil.getIconClasses(name, null, false));
2152            return info;
2153        } else {
2154            CmsNewResourceInfo info = new CmsNewResourceInfo(
2155                resType.getTypeId(),
2156                name,
2157                CmsWorkplaceMessages.getResourceTypeName(locale, name),
2158                CmsWorkplaceMessages.getResourceTypeDescription(locale, name),
2159                null,
2160                false,
2161                subtitle);
2162            info.setBigIconClasses(CmsIconUtil.getIconClasses(name, null, false));
2163            return info;
2164        }
2165    }
2166
2167    /**
2168     * Helper method for creating the .content folder of a sub-sitemap.<p>
2169     *
2170     * @param cms the current CMS context
2171     * @param subSitemapFolder the sub-sitemap folder in which the .content folder should be created
2172     *
2173     * @throws CmsException if something goes wrong
2174     */
2175    @SuppressWarnings("deprecation")
2176    private void createSitemapContentFolder(CmsObject cms, CmsResource subSitemapFolder) throws CmsException {
2177
2178        String sitePath = cms.getSitePath(subSitemapFolder);
2179        String folderName = CmsStringUtil.joinPaths(sitePath, CmsADEManager.CONTENT_FOLDER_NAME + "/");
2180        String sitemapConfigName = CmsStringUtil.joinPaths(folderName, CmsADEManager.CONFIG_FILE_NAME);
2181        if (!cms.existsResource(folderName)) {
2182            cms.createResource(
2183                folderName,
2184                OpenCms.getResourceManager().getResourceType(CmsADEManager.CONFIG_FOLDER_TYPE));
2185        }
2186        I_CmsResourceType configType = OpenCms.getResourceManager().getResourceType(CmsADEManager.CONFIG_TYPE);
2187        if (cms.existsResource(sitemapConfigName)) {
2188            CmsResource configFile = cms.readResource(sitemapConfigName);
2189            if (configFile.getTypeId() != configType.getTypeId()) {
2190                throw new CmsException(
2191                    Messages.get().container(
2192                        Messages.ERR_CREATING_SUB_SITEMAP_WRONG_CONFIG_FILE_TYPE_2,
2193                        sitemapConfigName,
2194                        CmsADEManager.CONFIG_TYPE));
2195            }
2196        } else {
2197            cms.createResource(
2198                sitemapConfigName,
2199                OpenCms.getResourceManager().getResourceType(CmsADEManager.CONFIG_TYPE));
2200        }
2201
2202    }
2203
2204    /**
2205     * Deletes a resource according to the change data.<p>
2206     *
2207     * @param change the change data
2208     *
2209     * @return CmsClientSitemapEntry always null
2210     *
2211     *
2212     * @throws CmsException if something goes wrong
2213     */
2214    private CmsClientSitemapEntry delete(CmsSitemapChange change) throws CmsException {
2215
2216        CmsObject cms = getCmsObject();
2217        CmsResource resource = cms.readResource(change.getEntryId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2218        ensureLock(resource);
2219        cms.deleteResource(cms.getSitePath(resource), CmsResource.DELETE_PRESERVE_SIBLINGS);
2220        tryUnlock(resource);
2221        return null;
2222    }
2223
2224    /**
2225     * Generates a client side lock info object representing the current lock state of the given resource.<p>
2226     *
2227     * @param resource the resource
2228     *
2229     * @return the client lock
2230     *
2231     * @throws CmsException if something goes wrong
2232     */
2233    private CmsClientLock generateClientLock(CmsResource resource) throws CmsException {
2234
2235        CmsObject cms = getCmsObject();
2236        CmsLock lock = cms.getLock(resource);
2237        CmsClientLock clientLock = new CmsClientLock();
2238        clientLock.setLockType(CmsClientLock.LockType.valueOf(lock.getType().getMode()));
2239        CmsUUID ownerId = lock.getUserId();
2240        if (!lock.isUnlocked() && (ownerId != null)) {
2241            clientLock.setLockOwner(cms.readUser(ownerId).getDisplayName(cms, cms.getRequestContext().getLocale()));
2242            clientLock.setOwnedByUser(cms.getRequestContext().getCurrentUser().getId().equals(ownerId));
2243        }
2244        return clientLock;
2245    }
2246
2247    /**
2248     * Generates a list of property object to save to the sitemap entry folder to apply the given change.<p>
2249     *
2250     * @param change the change
2251     * @param entryFolder the entry folder
2252     *
2253     * @return the property objects
2254     */
2255    private List<CmsProperty> generateInheritProperties(CmsSitemapChange change, CmsResource entryFolder) {
2256
2257        List<CmsProperty> result = new ArrayList<CmsProperty>();
2258        Map<String, CmsClientProperty> clientProps = change.getOwnInternalProperties();
2259        boolean hasTitle = false;
2260        if (clientProps != null) {
2261            for (CmsClientProperty clientProp : clientProps.values()) {
2262                if (CmsPropertyDefinition.PROPERTY_TITLE.equals(clientProp.getName())) {
2263                    hasTitle = true;
2264                }
2265                CmsProperty prop = new CmsProperty(
2266                    clientProp.getName(),
2267                    clientProp.getStructureValue(),
2268                    clientProp.getResourceValue());
2269                result.add(prop);
2270            }
2271        }
2272        if (!hasTitle) {
2273            result.add(new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, change.getName(), null));
2274        }
2275        return result;
2276    }
2277
2278    /**
2279     * Generates a list of property object to save to the sitemap entry resource to apply the given change.<p>
2280     *
2281     * @param change the change
2282     *
2283     * @return the property objects
2284     */
2285    private List<CmsProperty> generateOwnProperties(CmsSitemapChange change) {
2286
2287        List<CmsProperty> result = new ArrayList<CmsProperty>();
2288
2289        Map<String, CmsClientProperty> clientProps = change.getDefaultFileProperties();
2290        boolean hasTitle = false;
2291        if (clientProps != null) {
2292            for (CmsClientProperty clientProp : clientProps.values()) {
2293                if (CmsPropertyDefinition.PROPERTY_TITLE.equals(clientProp.getName())) {
2294                    hasTitle = true;
2295                }
2296                CmsProperty prop = new CmsProperty(
2297                    clientProp.getName(),
2298                    clientProp.getStructureValue(),
2299                    clientProp.getResourceValue());
2300                result.add(prop);
2301            }
2302        }
2303        if (!hasTitle) {
2304            result.add(new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, change.getName(), null));
2305        }
2306        return result;
2307    }
2308
2309    /**
2310     * Generates a list of property values inherited to the site-map root entry.<p>
2311     *
2312     * @param rootPath the root entry name
2313     *
2314     * @return the list of property values inherited to the site-map root entry
2315     *
2316     * @throws CmsException if something goes wrong reading the properties
2317     */
2318    private Map<String, CmsClientProperty> generateParentProperties(String rootPath) throws CmsException {
2319
2320        CmsObject cms = getCmsObject();
2321        if (rootPath == null) {
2322            rootPath = cms.getRequestContext().addSiteRoot("/");
2323        }
2324        CmsObject rootCms = OpenCms.initCmsObject(cms);
2325        rootCms.getRequestContext().setSiteRoot("");
2326        String parentRootPath = CmsResource.getParentFolder(rootPath);
2327
2328        Map<String, CmsClientProperty> result = new HashMap<String, CmsClientProperty>();
2329        if (parentRootPath != null) {
2330            List<CmsProperty> props = rootCms.readPropertyObjects(parentRootPath, true);
2331            for (CmsProperty prop : props) {
2332                CmsClientProperty clientProp = createClientProperty(prop, true);
2333                result.put(clientProp.getName(), clientProp);
2334            }
2335        }
2336        return result;
2337    }
2338
2339    /**
2340     * Returns the sitemap children for the given path with all descendants up to the given level or to the given target path, ie.
2341     * <dl><dt>levels=1 <dd>only children<dt>levels=2<dd>children and great children</dl>
2342     * and so on.<p>
2343     *
2344     * @param root the site relative root
2345     * @param levels the levels to recurse
2346     * @param targetPath the target path
2347     *
2348     * @return the sitemap children
2349     */
2350    private List<CmsClientSitemapEntry> getChildren(String root, int levels, String targetPath) {
2351
2352        List<CmsClientSitemapEntry> children = new ArrayList<CmsClientSitemapEntry>();
2353        int i = 0;
2354        for (CmsJspNavElement navElement : getNavBuilder().getNavigationForFolder(
2355            root,
2356            Visibility.all,
2357            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
2358            try {
2359                CmsClientSitemapEntry child = toClientEntry(navElement, false);
2360                if (child != null) {
2361                    child.setPosition(i);
2362                    children.add(child);
2363                    int nextLevels = levels;
2364                    if ((nextLevels == 2) && (targetPath != null) && targetPath.startsWith(child.getSitePath())) {
2365                        nextLevels = 3;
2366                    }
2367                    if (child.isFolderType() && ((nextLevels > 1) || (nextLevels == -1)) && !isSubSitemap(navElement)) {
2368
2369                        child.setSubEntries(getChildren(child.getSitePath(), nextLevels - 1, targetPath), null);
2370                        child.setChildrenLoadedInitially(true);
2371                    }
2372                    i++;
2373                }
2374            } catch (CmsException e) {
2375                LOG.error("Could not read sitemap entry.", e);
2376            }
2377        }
2378        return children;
2379    }
2380
2381    /**
2382     * Returns the clipboard data from the current user.<p>
2383     *
2384     * @return the clipboard data
2385     */
2386    private CmsSitemapClipboardData getClipboardData() {
2387
2388        CmsSitemapClipboardData result = new CmsSitemapClipboardData();
2389        result.setModifications(getModifiedList());
2390        result.setDeletions(getDeletedList());
2391        return result;
2392    }
2393
2394    /**
2395     * Returns the deleted list from the current user.<p>
2396     *
2397     * @return the deleted list
2398     */
2399    private LinkedHashMap<CmsUUID, CmsClientSitemapEntry> getDeletedList() {
2400
2401        CmsObject cms = getCmsObject();
2402        CmsUser user = cms.getRequestContext().getCurrentUser();
2403        Object obj = user.getAdditionalInfo(ADDINFO_ADE_DELETED_LIST);
2404        LinkedHashMap<CmsUUID, CmsClientSitemapEntry> result = new LinkedHashMap<CmsUUID, CmsClientSitemapEntry>();
2405        if (obj instanceof String) {
2406            try {
2407                JSONArray array = new JSONArray((String)obj);
2408                for (int i = 0; i < array.length(); i++) {
2409                    try {
2410                        CmsUUID delId = new CmsUUID(array.getString(i));
2411                        CmsResource res = cms.readResource(delId, CmsResourceFilter.ALL);
2412                        if (res.getState().isDeleted()) {
2413                            // make sure resource is still deleted
2414                            CmsClientSitemapEntry delEntry = new CmsClientSitemapEntry();
2415                            delEntry.setSitePath(cms.getSitePath(res));
2416                            delEntry.setOwnProperties(getClientProperties(cms, res, false));
2417                            delEntry.setName(res.getName());
2418                            delEntry.setVfsPath(cms.getSitePath(res));
2419                            delEntry.setResourceTypeName(
2420                                OpenCms.getResourceManager().getResourceType(res).getTypeName());
2421                            delEntry.setEntryType(
2422                                res.isFolder()
2423                                ? EntryType.folder
2424                                : isRedirectType(res.getTypeId()) ? EntryType.redirect : EntryType.leaf);
2425                            delEntry.setNavModeIcon(
2426                                CmsIconUtil.getIconClasses(
2427                                    delEntry.getResourceTypeName(),
2428                                    delEntry.getVfsPath(),
2429                                    false));
2430                            delEntry.setId(delId);
2431                            result.put(delId, delEntry);
2432                        }
2433                    } catch (Throwable e) {
2434                        // should never happen, catches wrong or no longer existing values
2435                        LOG.warn(e.getLocalizedMessage());
2436                    }
2437                }
2438            } catch (Throwable e) {
2439                // should never happen, catches json parsing
2440                LOG.warn(e.getLocalizedMessage());
2441            }
2442        }
2443        return result;
2444    }
2445
2446    /**
2447     * Gets the container name for function detail elements depending on the parent folder.<p>
2448     *
2449     * @param parent the parent folder
2450     * @return the name of the function detail container
2451     */
2452    private String getFunctionDetailContainerName(CmsResource parent) {
2453
2454        CmsObject cms = getCmsObject();
2455        String notFound = null;
2456        String containerInfo = OpenCms.getTemplateContextManager().readPropertyFromTemplate(
2457            cms,
2458            parent,
2459            CmsPropertyDefinition.PROPERTY_CONTAINER_INFO,
2460            notFound);
2461        if (containerInfo == notFound) {
2462            return null;
2463        }
2464        Map<String, String> attrs = CmsStringUtil.splitAsMap(containerInfo, "|", "="); //$NON-NLS-2$
2465        String functionDetailContainerName = attrs.get(KEY_FUNCTION_DETAIL);
2466        return functionDetailContainerName;
2467    }
2468
2469    /**
2470     * Returns the galleries of the given sub site for the requested gallery type.<p>
2471     *
2472     * @param entryPointUri the sub site entry point
2473     * @param galleryType the gallery type
2474     * @param subSitePaths the sub site paths
2475     *
2476     * @return the gallery folder entries
2477     *
2478     * @throws CmsException if reading the resources fails
2479     */
2480    private List<CmsGalleryFolderEntry> getGalleriesForType(
2481        String entryPointUri,
2482        CmsGalleryType galleryType,
2483        List<String> subSitePaths)
2484    throws CmsException {
2485
2486        List<CmsGalleryFolderEntry> galleries = new ArrayList<CmsGalleryFolderEntry>();
2487        @SuppressWarnings("deprecation")
2488        List<CmsResource> galleryFolders = getCmsObject().readResources(
2489            entryPointUri,
2490            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(galleryType.getTypeId()));
2491        for (CmsResource folder : galleryFolders) {
2492            try {
2493                if (!isInSubsite(subSitePaths, folder.getRootPath())) {
2494                    galleries.add(readGalleryFolderEntry(folder, galleryType.getResourceType()));
2495                }
2496            } catch (CmsException ex) {
2497                log(ex.getLocalizedMessage(), ex);
2498            }
2499        }
2500        // create a tree structure
2501        Collections.sort(galleries, new Comparator<CmsGalleryFolderEntry>() {
2502
2503            public int compare(CmsGalleryFolderEntry o1, CmsGalleryFolderEntry o2) {
2504
2505                return o1.getSitePath().compareTo(o2.getSitePath());
2506            }
2507        });
2508        List<CmsGalleryFolderEntry> galleryTree = new ArrayList<CmsGalleryFolderEntry>();
2509        for (int i = 0; i < galleries.size(); i++) {
2510            boolean isSubGallery = false;
2511            if (i > 0) {
2512                for (int j = i - 1; j >= 0; j--) {
2513                    if (galleries.get(i).getSitePath().startsWith(galleries.get(j).getSitePath())) {
2514                        galleries.get(j).addSubGallery(galleries.get(i));
2515                        isSubGallery = true;
2516                        break;
2517                    }
2518                }
2519            }
2520            if (!isSubGallery) {
2521                galleryTree.add(galleries.get(i));
2522            }
2523        }
2524        return galleryTree;
2525    }
2526
2527    /**
2528     * Returns the modified list from the current user.<p>
2529     *
2530     * @return the modified list
2531     */
2532    private LinkedHashMap<CmsUUID, CmsClientSitemapEntry> getModifiedList() {
2533
2534        CmsObject cms = getCmsObject();
2535        CmsUser user = cms.getRequestContext().getCurrentUser();
2536        Object obj = user.getAdditionalInfo(ADDINFO_ADE_MODIFIED_LIST);
2537        LinkedHashMap<CmsUUID, CmsClientSitemapEntry> result = new LinkedHashMap<CmsUUID, CmsClientSitemapEntry>();
2538        if (obj instanceof String) {
2539            try {
2540                JSONArray array = new JSONArray((String)obj);
2541                for (int i = 0; i < array.length(); i++) {
2542                    try {
2543                        CmsUUID modId = new CmsUUID(array.getString(i));
2544                        CmsResource res = cms.readResource(modId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2545                        String sitePath = cms.getSitePath(res);
2546                        CmsJspNavElement navEntry = getNavBuilder().getNavigationForResource(
2547                            sitePath,
2548                            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2549                        if (navEntry.isInNavigation()) {
2550                            CmsClientSitemapEntry modEntry = toClientEntry(navEntry, false);
2551                            result.put(modId, modEntry);
2552                        }
2553                    } catch (Throwable e) {
2554                        // should never happen, catches wrong or no longer existing values
2555                        LOG.warn(e.getLocalizedMessage());
2556                    }
2557                }
2558            } catch (Throwable e) {
2559                // should never happen, catches json parsing
2560                LOG.warn(e.getLocalizedMessage());
2561            }
2562        }
2563        return result;
2564    }
2565
2566    /**
2567     * Returns a navigation builder reference.<p>
2568     *
2569     * @return the navigation builder
2570     */
2571    private CmsJspNavBuilder getNavBuilder() {
2572
2573        if (m_navBuilder == null) {
2574            m_navBuilder = new CmsJspNavBuilder(getCmsObject());
2575        }
2576        return m_navBuilder;
2577    }
2578
2579    /**
2580     * Returns the new resource infos.<p>
2581     *
2582     * @param cms the current CMS context
2583     * @param configData the configuration data from which the new resource infos should be read
2584     * @param locale locale used for retrieving descriptions/titles
2585     *
2586     * @return the new resource infos
2587     */
2588    private List<CmsNewResourceInfo> getNewResourceInfos(CmsObject cms, CmsADEConfigData configData, Locale locale) {
2589
2590        List<CmsNewResourceInfo> result = new ArrayList<CmsNewResourceInfo>();
2591        for (CmsModelPageConfig modelConfig : configData.getModelPages()) {
2592            try {
2593                CmsNewResourceInfo info = createNewResourceInfo(cms, modelConfig.getResource(), locale);
2594                info.setDefault(modelConfig.isDefault());
2595                result.add(info);
2596            } catch (CmsException e) {
2597                LOG.debug(e.getLocalizedMessage(), e);
2598            }
2599        }
2600        Collections.sort(result, new Comparator<CmsNewResourceInfo>() {
2601
2602            public int compare(CmsNewResourceInfo a, CmsNewResourceInfo b) {
2603
2604                return ComparisonChain.start().compareTrueFirst(a.isDefault(), b.isDefault()).compare(
2605                    a.getNavPos(),
2606                    b.getNavPos(),
2607                    Ordering.natural().nullsLast()).result();
2608            }
2609        });
2610        return result;
2611    }
2612
2613    /**
2614     * Gets the names of all available properties.<p>
2615     *
2616     * @param cms the CMS context
2617     *
2618     * @return the list of all property names
2619     *
2620     * @throws CmsException if something goes wrong
2621     */
2622    private List<String> getPropertyNames(CmsObject cms) throws CmsException {
2623
2624        List<CmsPropertyDefinition> propDefs = cms.readAllPropertyDefinitions();
2625        List<String> result = new ArrayList<String>();
2626        for (CmsPropertyDefinition propDef : propDefs) {
2627            result.add(propDef.getName());
2628        }
2629        return result;
2630    }
2631
2632    /**
2633     * Gets the resource type info beans for types for which new detail pages can be created.<p>
2634     *
2635     * @param cms the current CMS context
2636     * @param resourceTypeConfigs the resource type configurations
2637     * @param functionReferences the function references
2638     * @param dynamicFunctionRestriction
2639     * @param modelResource the model resource
2640     * @param locale the locale used for retrieving descriptions/titles
2641     *
2642     * @return the resource type info beans for types for which new detail pages can be created
2643     */
2644    private List<CmsNewResourceInfo> getResourceTypeInfos(
2645        CmsObject cms,
2646        List<CmsResourceTypeConfig> resourceTypeConfigs,
2647        List<CmsFunctionReference> functionReferences,
2648        CmsFunctionAvailability dynamicFunctionAvailability,
2649        CmsResource modelResource,
2650        Locale locale) {
2651
2652        List<CmsNewResourceInfo> result = new ArrayList<CmsNewResourceInfo>();
2653        CmsNewResourceInfo defaultPageInfo;
2654        if (modelResource != null) {
2655            defaultPageInfo = new CmsNewResourceInfo(
2656                modelResource.getTypeId(),
2657                CmsADEManager.DEFAULT_DETAILPAGE_TYPE,
2658                Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_DEFAULT_DETAIL_PAGE_TITLE_0),
2659                Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_DEFAULT_DETAIL_PAGE_DESCRIPTION_0),
2660                modelResource.getStructureId(),
2661                false,
2662                Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_DEFAULT_DETAIL_PAGE_DESCRIPTION_0));
2663
2664        } else {
2665            defaultPageInfo = new CmsNewResourceInfo(
2666                CmsResourceTypeXmlContainerPage.getContainerPageTypeIdSafely(),
2667                CmsADEManager.DEFAULT_DETAILPAGE_TYPE,
2668                Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_DEFAULT_DETAIL_PAGE_TITLE_0),
2669                Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_DEFAULT_DETAIL_PAGE_DESCRIPTION_0),
2670                null,
2671                false,
2672                Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_DEFAULT_DETAIL_PAGE_DESCRIPTION_0));
2673        }
2674
2675        defaultPageInfo.setBigIconClasses(
2676            CmsIconUtil.getIconClasses(CmsResourceTypeXmlContainerPage.getStaticTypeName(), null, false));
2677        result.add(defaultPageInfo);
2678        for (CmsResourceTypeConfig typeConfig : resourceTypeConfigs) {
2679            if (typeConfig.isDetailPagesDisabled()) {
2680                continue;
2681            }
2682            String typeName = typeConfig.getTypeName();
2683            try {
2684                I_CmsResourceType resourceType = OpenCms.getResourceManager().getResourceType(typeName);
2685                result.add(createResourceTypeInfo(resourceType, modelResource));
2686            } catch (CmsLoaderException e) {
2687                LOG.error(e.getLocalizedMessage(), e);
2688            }
2689        }
2690        for (CmsFunctionReference functionRef : functionReferences) {
2691            if (dynamicFunctionAvailability.isDefined()) {
2692                if (!dynamicFunctionAvailability.checkAvailable(functionRef.getStructureId())) {
2693                    continue;
2694                }
2695            }
2696            try {
2697                CmsResource functionRes = cms.readResource(functionRef.getStructureId());
2698                String description = cms.readPropertyObject(
2699                    functionRes,
2700                    CmsPropertyDefinition.PROPERTY_DESCRIPTION,
2701                    false).getValue();
2702                String subtitle = description;
2703                try {
2704                    CmsGallerySearchResult searchResult = CmsGallerySearch.searchById(
2705                        cms,
2706                        functionRef.getStructureId(),
2707                        getWorkplaceLocale());
2708                    subtitle = searchResult.getDescription();
2709                } catch (CmsException e) {
2710                    LOG.warn(e.getLocalizedMessage(), e);
2711                }
2712
2713                CmsNewResourceInfo info = new CmsNewResourceInfo(
2714                    modelResource.getTypeId(),
2715                    CmsDetailPageInfo.FUNCTION_PREFIX + functionRef.getName(),
2716                    functionRef.getName(),
2717                    description,
2718                    modelResource.getStructureId(),
2719                    false,
2720                    subtitle);
2721                info.setCreateParameter(functionRef.getStructureId().toString());
2722                info.setIsFunction(true);
2723                info.setBigIconClasses(
2724                    CmsIconUtil.getIconClasses(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION, null, false));
2725                result.add(info);
2726            } catch (CmsVfsResourceNotFoundException e) {
2727                LOG.warn(e.getLocalizedMessage(), e);
2728            } catch (CmsException e) {
2729                LOG.error(e.getLocalizedMessage(), e);
2730            }
2731        }
2732        return result;
2733    }
2734
2735    /**
2736     * Reeds the site root entry.<p>
2737     *
2738     * @param rootPath the root path of the sitemap root
2739     * @param targetPath the target path to open
2740     *
2741     * @return the site root entry
2742     *
2743     * @throws CmsSecurityException in case of insufficient permissions
2744     * @throws CmsException if something goes wrong
2745     */
2746    private CmsClientSitemapEntry getRootEntry(String rootPath, String targetPath)
2747    throws CmsSecurityException, CmsException {
2748
2749        String sitePath = "/";
2750        if ((rootPath != null)) {
2751            sitePath = getCmsObject().getRequestContext().removeSiteRoot(rootPath);
2752        }
2753        CmsJspNavElement navElement = getNavBuilder().getNavigationForResource(
2754            sitePath,
2755            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2756        CmsClientSitemapEntry result = toClientEntry(navElement, true);
2757        if (result != null) {
2758            result.setPosition(0);
2759            result.setChildrenLoadedInitially(true);
2760            result.setSubEntries(getChildren(sitePath, 2, targetPath), null);
2761        }
2762        return result;
2763    }
2764
2765    /**
2766     * Returns the sitemap info for the given base path.<p>
2767     *
2768     * @param basePath the base path
2769     *
2770     * @return the sitemap info
2771     *
2772     * @throws CmsException if something goes wrong reading the resources
2773     */
2774    private CmsSitemapInfo getSitemapInfo(String basePath) throws CmsException {
2775
2776        CmsObject cms = getCmsObject();
2777        CmsResource baseFolder = null;
2778        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(basePath)) {
2779            baseFolder = cms.readResource(
2780                cms.getRequestContext().removeSiteRoot(basePath),
2781                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2782        } else {
2783            // in case of an empty base path, use base folder of the current site
2784            basePath = "/";
2785            baseFolder = cms.readResource("/");
2786        }
2787        CmsResource defaultFile = cms.readDefaultFile(baseFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2788        String title = cms.readPropertyObject(baseFolder, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue();
2789        if (CmsStringUtil.isEmptyOrWhitespaceOnly(title) && (defaultFile != null)) {
2790            title = cms.readPropertyObject(defaultFile, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue();
2791        }
2792        String description = cms.readPropertyObject(
2793            baseFolder,
2794            CmsPropertyDefinition.PROPERTY_DESCRIPTION,
2795            false).getValue();
2796        if (CmsStringUtil.isEmptyOrWhitespaceOnly(description) && (defaultFile != null)) {
2797            description = cms.readPropertyObject(
2798                defaultFile,
2799                CmsPropertyDefinition.PROPERTY_DESCRIPTION,
2800                false).getValue();
2801        }
2802        return new CmsSitemapInfo(
2803            cms.getRequestContext().getCurrentProject().getName(),
2804            description,
2805            OpenCms.getLocaleManager().getDefaultLocale(cms, baseFolder).toString(),
2806            OpenCms.getSiteManager().getCurrentSite(cms).getUrl()
2807                + OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, basePath),
2808            title);
2809    }
2810
2811    /**
2812     * Gets the default type id for subsitemap folders.<p>
2813     *
2814     * @return the default type id for subsitemap folders
2815     *
2816     * @throws CmsRpcException in case of an error
2817     */
2818    @SuppressWarnings("deprecation")
2819    private int getSubsitemapType() throws CmsRpcException {
2820
2821        try {
2822            return OpenCms.getResourceManager().getResourceType(
2823                CmsResourceTypeFolderSubSitemap.TYPE_SUBSITEMAP).getTypeId();
2824        } catch (CmsLoaderException e) {
2825            error(e);
2826        }
2827        return CmsResourceTypeUnknownFolder.getStaticTypeId();
2828    }
2829
2830    /**
2831     * Returns the workplace locale for the current user.<p>
2832     *
2833     * @return the workplace locale
2834     */
2835    private Locale getWorkplaceLocale() {
2836
2837        Locale result = OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject());
2838        if (result == null) {
2839            result = CmsLocaleManager.getDefaultLocale();
2840        }
2841        if (result == null) {
2842            result = Locale.getDefault();
2843        }
2844        return result;
2845    }
2846
2847    /**
2848     * Checks whether the sitemap change has default file changes.<p>
2849     *
2850     * @param change a sitemap change
2851     * @return true if the change would change the default file
2852     */
2853    private boolean hasDefaultFileChanges(CmsSitemapChange change) {
2854
2855        return (change.getDefaultFileId() != null) && !change.isNew();
2856    }
2857
2858    /**
2859     * Checks whether the sitemap change has changes for the sitemap entry resource.<p>
2860     *
2861     * @param change the sitemap change
2862     * @return true if the change would change the original sitemap entry resource
2863     */
2864    private boolean hasOwnChanges(CmsSitemapChange change) {
2865
2866        return !change.isNew();
2867    }
2868
2869    /**
2870     * Checks whether a resource is a default file of a folder.<p>
2871     *
2872     * @param resource the resource to check
2873     *
2874     * @return true if the resource is the default file of a folder
2875     *
2876     * @throws CmsException if something goes wrong
2877     */
2878    private boolean isDefaultFile(CmsResource resource) throws CmsException {
2879
2880        CmsObject cms = getCmsObject();
2881        if (resource.isFolder()) {
2882            return false;
2883        }
2884
2885        CmsResource parent = cms.readResource(
2886            CmsResource.getParentFolder(cms.getSitePath(resource)),
2887            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2888        CmsResource defaultFile = cms.readDefaultFile(parent, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2889        return resource.equals(defaultFile);
2890    }
2891
2892    /**
2893     * Checks if the toolbar should be displayed.<p>
2894     *
2895     * @param request the current request to get the default locale from
2896     *
2897     * @return <code>true</code> if the toolbar should be displayed
2898     */
2899    private boolean isDisplayToolbar(HttpServletRequest request) {
2900
2901        // display the toolbar by default
2902        boolean displayToolbar = true;
2903        if (CmsHistoryResourceHandler.isHistoryRequest(request)) {
2904            // we do not want to display the toolbar in case of an historical request
2905            displayToolbar = false;
2906        }
2907        return displayToolbar;
2908    }
2909
2910    /**
2911     * Returns if the given path is located below one of the given sub site paths.<p>
2912     *
2913     * @param subSitePaths the sub site root paths
2914     * @param path the root path to check
2915     *
2916     * @return <code>true</code> if the given path is located below one of the given sub site paths
2917     */
2918    private boolean isInSubsite(List<String> subSitePaths, String path) {
2919
2920        boolean result = false;
2921        for (String subSite : subSitePaths) {
2922            if (path.startsWith(subSite)) {
2923                result = true;
2924                break;
2925            }
2926        }
2927        return result;
2928    }
2929
2930    /**
2931     * Returns if the given type id matches the xml-redirect resource type.<p>
2932     *
2933     * @param typeId the resource type id
2934     *
2935     * @return <code>true</code> if the given type id matches the xml-redirect resource type
2936     */
2937    @SuppressWarnings("deprecation")
2938    private boolean isRedirectType(int typeId) {
2939
2940        try {
2941            return typeId == OpenCms.getResourceManager().getResourceType(RECOURCE_TYPE_NAME_REDIRECT).getTypeId();
2942        } catch (Exception e) {
2943            return false;
2944        }
2945    }
2946
2947    /**
2948     * Returns if the given nav-element resembles a sub-sitemap entry-point.<p>
2949     *
2950     * @param navElement the nav-element
2951     *
2952     * @return <code>true</code> if the given nav-element resembles a sub-sitemap entry-point.<p>
2953     */
2954    private boolean isSubSitemap(CmsJspNavElement navElement) {
2955
2956        return CmsResourceTypeFolderSubSitemap.isSubSitemap(navElement.getResource());
2957    }
2958
2959    /**
2960     * Checks if the given open path is valid.<p>
2961     *
2962     * @param cms the cms context
2963     * @param openPath the open path
2964     *
2965     * @return <code>true</code> if the given open path is valid
2966     */
2967    private boolean isValidOpenPath(CmsObject cms, String openPath) {
2968
2969        if (CmsStringUtil.isEmptyOrWhitespaceOnly(openPath)) {
2970            return false;
2971        }
2972        if (!cms.existsResource(openPath)) {
2973            // in case of a detail-page check the parent folder
2974            String parent = CmsResource.getParentFolder(openPath);
2975            if (CmsStringUtil.isEmptyOrWhitespaceOnly(parent) || !cms.existsResource(parent)) {
2976                return false;
2977            }
2978        }
2979        return true;
2980    }
2981
2982    /**
2983     * Returns if the given return code is valid.<p>
2984     *
2985     * @param returnCode the return code to check
2986     *
2987     * @return <code>true</code> if the return code is valid
2988     */
2989    private boolean isValidReturnCode(String returnCode) {
2990
2991        if (CmsStringUtil.isEmptyOrWhitespaceOnly(returnCode)) {
2992            return false;
2993        }
2994        int pos = returnCode.indexOf(":");
2995        if (pos > 0) {
2996            return CmsUUID.isValidUUID(returnCode.substring(0, pos))
2997                && CmsUUID.isValidUUID(returnCode.substring(pos + 1));
2998        } else {
2999            return CmsUUID.isValidUUID(returnCode);
3000        }
3001    }
3002
3003    /**
3004     * Applys the given changes to the entry.<p>
3005     *
3006     * @param change the change to apply
3007     *
3008     * @throws CmsException if something goes wrong
3009     */
3010    private void modifyEntry(CmsSitemapChange change) throws CmsException {
3011
3012        CmsObject cms = getCmsObject();
3013        CmsResource entryPage = null;
3014        CmsResource entryFolder = null;
3015
3016        CmsResource ownRes = null;
3017        CmsResource defaultFileRes = null;
3018        try {
3019            // lock all resources necessary first to avoid doing changes only half way through
3020
3021            if (hasOwnChanges(change)) {
3022                ownRes = cms.readResource(change.getEntryId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3023                boolean shallow = !change.hasChangedName() && !change.hasChangedPosition() && !change.hasNewParent();
3024
3025                ensureLock(ownRes, shallow);
3026            }
3027
3028            if (hasDefaultFileChanges(change)) {
3029                defaultFileRes = cms.readResource(change.getDefaultFileId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3030                ensureLock(defaultFileRes, false);
3031            }
3032
3033            if ((ownRes != null) && ownRes.isFolder()) {
3034                entryFolder = ownRes;
3035            }
3036
3037            if ((ownRes != null) && ownRes.isFile()) {
3038                entryPage = ownRes;
3039            }
3040
3041            if (defaultFileRes != null) {
3042                entryPage = defaultFileRes;
3043            }
3044
3045            if (change.isLeafType()) {
3046                entryFolder = entryPage;
3047            }
3048
3049            String moveSrc = null;
3050            String moveDest = null;
3051
3052            if (entryFolder != null) {
3053                if (change.hasNewParent() || change.hasChangedName()) {
3054                    String destinationPath;
3055                    if (change.hasNewParent()) {
3056                        CmsResource futureParent = cms.readResource(
3057                            change.getParentId(),
3058                            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3059                        destinationPath = CmsStringUtil.joinPaths(cms.getSitePath(futureParent), change.getName());
3060                    } else {
3061                        destinationPath = CmsStringUtil.joinPaths(
3062                            CmsResource.getParentFolder(cms.getSitePath(entryFolder)),
3063                            change.getName());
3064                    }
3065                    if (change.isLeafType() && destinationPath.endsWith("/")) {
3066                        destinationPath = destinationPath.substring(0, destinationPath.length() - 1);
3067                    }
3068                    // only if the site-path has really changed
3069                    if (!CmsFileUtil.removeTrailingSeparator(cms.getSitePath(entryFolder)).equals(
3070                        CmsFileUtil.removeTrailingSeparator(destinationPath))) {
3071                        moveSrc = cms.getSitePath(entryFolder);
3072                        moveDest = CmsFileUtil.removeTrailingSeparator(destinationPath);
3073                    }
3074                }
3075            }
3076            if ((moveDest != null) && cms.existsResource(moveDest, CmsResourceFilter.IGNORE_EXPIRATION)) {
3077                throw new CmsVfsResourceAlreadyExistsException(
3078                    org.opencms.db.generic.Messages.get().container(
3079                        org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
3080                        moveDest));
3081            }
3082
3083            updateProperties(cms, ownRes, defaultFileRes, change.getPropertyChanges());
3084            if (change.hasChangedPosition()) {
3085                updateNavPos(ownRes, change);
3086            }
3087
3088            if (moveDest != null) {
3089                cms.moveResource(moveSrc, moveDest);
3090            }
3091            entryFolder = cms.readResource(entryFolder.getStructureId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3092
3093        } finally {
3094            if (entryPage != null) {
3095                tryUnlock(entryPage);
3096            }
3097            if (entryFolder != null) {
3098                tryUnlock(entryFolder);
3099            }
3100        }
3101    }
3102
3103    /**
3104     * Reads the gallery folder properties.<p>
3105     *
3106     * @param folder the folder resource
3107     * @param typeName the  resource type name
3108     *
3109     * @return the folder entry data
3110     *
3111     * @throws CmsException if the folder properties can not be read
3112     */
3113    private CmsGalleryFolderEntry readGalleryFolderEntry(CmsResource folder, String typeName) throws CmsException {
3114
3115        CmsObject cms = getCmsObject();
3116        CmsGalleryFolderEntry folderEntry = new CmsGalleryFolderEntry();
3117        folderEntry.setResourceType(typeName);
3118        folderEntry.setSitePath(cms.getSitePath(folder));
3119        folderEntry.setStructureId(folder.getStructureId());
3120        folderEntry.setOwnProperties(getClientProperties(cms, folder, false));
3121        folderEntry.setIconClasses(CmsIconUtil.getIconClasses(typeName, null, false));
3122        return folderEntry;
3123    }
3124
3125    /**
3126     * Helper method for removing all locales except one from a container page.<p>
3127     *
3128     * @param page the container page to proces
3129     * @param localeToKeep the locale which should be kept
3130     *
3131     * @throws CmsXmlException if something goes wrong
3132     */
3133    private void removeAllLocalesExcept(CmsXmlContainerPage page, Locale localeToKeep) throws CmsXmlException {
3134
3135        List<Locale> locales = page.getLocales();
3136        for (Locale locale : locales) {
3137            if (!locale.equals(localeToKeep)) {
3138                page.removeLocale(locale);
3139            }
3140        }
3141    }
3142
3143    /**
3144     * Applys the given remove change.<p>
3145     *
3146     * @param change the change to apply
3147     *
3148     * @return the changed client sitemap entry or <code>null</code>
3149     *
3150     * @throws CmsException if something goes wrong
3151     */
3152    private CmsSitemapChange removeEntryFromNavigation(CmsSitemapChange change) throws CmsException {
3153
3154        CmsObject cms = getCmsObject();
3155        CmsResource entryFolder = cms.readResource(change.getEntryId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3156        ensureLock(entryFolder);
3157        List<CmsProperty> properties = new ArrayList<CmsProperty>();
3158        properties.add(
3159            new CmsProperty(
3160                CmsPropertyDefinition.PROPERTY_NAVTEXT,
3161                CmsProperty.DELETE_VALUE,
3162                CmsProperty.DELETE_VALUE));
3163        properties.add(
3164            new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVPOS, CmsProperty.DELETE_VALUE, CmsProperty.DELETE_VALUE));
3165        cms.writePropertyObjects(cms.getSitePath(entryFolder), properties);
3166        tryUnlock(entryFolder);
3167        return change;
3168    }
3169
3170    /**
3171     * Saves the detail page information of a sitemap to the sitemap's configuration file.<p>
3172     *
3173     * @param detailPages saves the detailpage configuration
3174     * @param resource the configuration file resource
3175     * @param newId the structure id to use for new detail page entries
3176     * @param updateEntry the new detail page entry
3177     *
3178     * @throws CmsException if something goes wrong
3179     */
3180    private void saveDetailPages(
3181        List<CmsDetailPageInfo> detailPages,
3182        CmsResource resource,
3183        CmsUUID newId,
3184        CmsClientSitemapEntry updateEntry)
3185    throws CmsException {
3186
3187        CmsObject cms = getCmsObject();
3188        if (updateEntry != null) {
3189            for (CmsDetailPageInfo info : detailPages) {
3190                if (info.getId() == null) {
3191                    updateEntry.setDetailpageTypeName(info.getType());
3192                    break;
3193                }
3194            }
3195        }
3196        CmsDetailPageConfigurationWriter writer = new CmsDetailPageConfigurationWriter(cms, resource);
3197        writer.updateAndSave(detailPages, newId);
3198    }
3199
3200    /**
3201     * Saves the given clipboard data to the session.<p>
3202     *
3203     * @param clipboardData the clipboard data to save
3204     *
3205     * @throws CmsException if something goes wrong writing the user
3206     */
3207    private void setClipboardData(CmsSitemapClipboardData clipboardData) throws CmsException {
3208
3209        CmsObject cms = getCmsObject();
3210        CmsUser user = cms.getRequestContext().getCurrentUser();
3211        if (clipboardData != null) {
3212            JSONArray modified = new JSONArray();
3213            if (clipboardData.getModifications() != null) {
3214                for (CmsUUID id : clipboardData.getModifications().keySet()) {
3215                    if (id != null) {
3216                        modified.put(id.toString());
3217                    }
3218                }
3219            }
3220            user.setAdditionalInfo(ADDINFO_ADE_MODIFIED_LIST, modified.toString());
3221            JSONArray deleted = new JSONArray();
3222            if (clipboardData.getDeletions() != null) {
3223                for (CmsUUID id : clipboardData.getDeletions().keySet()) {
3224                    if (id != null) {
3225                        deleted.put(id.toString());
3226                    }
3227                }
3228            }
3229            user.setAdditionalInfo(ADDINFO_ADE_DELETED_LIST, deleted.toString());
3230            cms.writeUser(user);
3231        }
3232    }
3233
3234    /**
3235     * Determines if the title property of the default file should be changed.<p>
3236     *
3237     * @param properties the current default file properties
3238     * @param folderNavtext the 'NavText' property of the folder
3239     *
3240     * @return <code>true</code> if the title property should be changed
3241     */
3242    private boolean shouldChangeDefaultFileTitle(Map<String, CmsProperty> properties, CmsProperty folderNavtext) {
3243
3244        return (properties == null)
3245            || (properties.get(CmsPropertyDefinition.PROPERTY_TITLE) == null)
3246            || (properties.get(CmsPropertyDefinition.PROPERTY_TITLE).getValue() == null)
3247            || ((folderNavtext != null)
3248                && properties.get(CmsPropertyDefinition.PROPERTY_TITLE).getValue().equals(folderNavtext.getValue()));
3249    }
3250
3251    /**
3252     * Determines if the title property should be changed in case of a 'NavText' change.<p>
3253     *
3254     * @param properties the current resource properties
3255     *
3256     * @return <code>true</code> if the title property should be changed in case of a 'NavText' change
3257     */
3258    private boolean shouldChangeTitle(Map<String, CmsProperty> properties) {
3259
3260        return (properties == null)
3261            || (properties.get(CmsPropertyDefinition.PROPERTY_TITLE) == null)
3262            || (properties.get(CmsPropertyDefinition.PROPERTY_TITLE).getValue() == null)
3263            || ((properties.get(CmsPropertyDefinition.PROPERTY_NAVTEXT) != null)
3264                && properties.get(CmsPropertyDefinition.PROPERTY_TITLE).getValue().equals(
3265                    properties.get(CmsPropertyDefinition.PROPERTY_NAVTEXT).getValue()));
3266    }
3267
3268    /**
3269     * Synchronizes the title from the NavText for a specific resource after properties have been edited, if NavText isn't empty and the title isn't already set to a different non-empty value.
3270     *
3271     * @param resource the resource
3272     * @param oldNavText the NavText before editing the properties
3273     * @param newNavText the NavText after editing the properties (may be the same as oldNavText)
3274     * @throws CmsException if something goes wrong
3275     */
3276    private void synchTitleFromNavText(CmsResource resource, String oldNavText, String newNavText) throws CmsException {
3277
3278        CmsObject cms = getCmsObject();
3279
3280        if (CmsStringUtil.isEmpty(newNavText)) {
3281            return;
3282        }
3283        CmsProperty titleProp = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_TITLE, false);
3284        String title = titleProp.getValue();
3285        if (title == null) {
3286            title = "";
3287        }
3288        // We don't check if oldNavText is different from newNavText, because we also want the synchronization to happen
3289        // when we don't actually change the NavText, but e.g. when we change the title to an empty string.
3290        if (CmsStringUtil.isEmpty(title) || title.equals(oldNavText)) {
3291            if (!newNavText.equals(title)) {
3292                cms.writePropertyObjects(
3293                    resource,
3294                    Arrays.asList(new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, newNavText, null)));
3295            }
3296        }
3297    }
3298
3299    /**
3300     * Converts a jsp navigation element into a client sitemap entry.<p>
3301     *
3302     * @param navElement the jsp navigation element
3303     * @param isRoot true if the entry is a root entry
3304     *
3305     * @return the client sitemap entry
3306     *
3307     * @throws CmsException if something goes wrong
3308     */
3309    private CmsClientSitemapEntry toClientEntry(CmsJspNavElement navElement, boolean isRoot) throws CmsException {
3310
3311        CmsResource entryPage = null;
3312        CmsObject cms = getCmsObject();
3313        CmsClientSitemapEntry clientEntry = new CmsClientSitemapEntry();
3314        CmsResource entryFolder = null;
3315
3316        CmsResource ownResource = navElement.getResource();
3317        clientEntry.setResourceState(ownResource.getState());
3318        CmsResource defaultFileResource = null;
3319        if (ownResource.isFolder() && !navElement.isNavigationLevel()) {
3320            defaultFileResource = cms.readDefaultFile(ownResource, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3321        }
3322
3323        Map<String, CmsClientProperty> ownProps = getClientProperties(cms, ownResource, false);
3324
3325        Map<String, CmsClientProperty> defaultFileProps = null;
3326        if (defaultFileResource != null) {
3327            defaultFileProps = getClientProperties(cms, defaultFileResource, false);
3328            clientEntry.setDefaultFileId(defaultFileResource.getStructureId());
3329            clientEntry.setDefaultFileType(
3330                OpenCms.getResourceManager().getResourceType(defaultFileResource.getTypeId()).getTypeName());
3331            clientEntry.setDefaultFileReleased(defaultFileResource.isReleasedAndNotExpired(System.currentTimeMillis()));
3332        } else {
3333            defaultFileProps = new HashMap<String, CmsClientProperty>();
3334        }
3335        boolean isDefault = isDefaultFile(ownResource);
3336        clientEntry.setId(ownResource.getStructureId());
3337        clientEntry.setResourceTypeId(ownResource.getTypeId());
3338        clientEntry.setFolderDefaultPage(isDefault);
3339        if (navElement.getResource().isFolder()) {
3340            entryFolder = navElement.getResource();
3341            entryPage = defaultFileResource;
3342            clientEntry.setName(entryFolder.getName());
3343            if (entryPage == null) {
3344                entryPage = entryFolder;
3345            }
3346            if (!isRoot && isSubSitemap(navElement)) {
3347                clientEntry.setEntryType(EntryType.subSitemap);
3348                clientEntry.setDefaultFileType(null);
3349            } else if (navElement.isNavigationLevel()) {
3350                clientEntry.setEntryType(EntryType.navigationLevel);
3351            }
3352            CmsLock folderLock = cms.getLock(entryFolder);
3353            clientEntry.setHasForeignFolderLock(
3354                !folderLock.isUnlocked() && !folderLock.isOwnedBy(cms.getRequestContext().getCurrentUser()));
3355            if (!cms.getRequestContext().getCurrentProject().isOnlineProject()) {
3356                List<CmsResource> blockingChildren = cms.getBlockingLockedResources(entryFolder);
3357                clientEntry.setBlockingLockedChildren((blockingChildren != null) && !blockingChildren.isEmpty());
3358            }
3359        } else {
3360            entryPage = navElement.getResource();
3361            clientEntry.setName(entryPage.getName());
3362            if (isRedirectType(entryPage.getTypeId())) {
3363                clientEntry.setEntryType(EntryType.redirect);
3364                CmsFile file = getCmsObject().readFile(entryPage);
3365                I_CmsXmlDocument content = CmsXmlContentFactory.unmarshal(getCmsObject(), file);
3366                I_CmsXmlContentValue linkValue = content.getValue(
3367                    REDIRECT_LINK_TARGET_XPATH,
3368                    getCmsObject().getRequestContext().getLocale());
3369                String link = linkValue != null
3370                ? linkValue.getStringValue(getCmsObject())
3371                : Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_REDIRECT_SUB_LEVEL_0);
3372                clientEntry.setRedirectTarget(link);
3373            } else {
3374                clientEntry.setEntryType(EntryType.leaf);
3375            }
3376        }
3377        if (entryPage.isFile()) {
3378            List<CmsAlias> aliases = OpenCms.getAliasManager().getAliasesForStructureId(
3379                getCmsObject(),
3380                entryPage.getStructureId());
3381            if (!aliases.isEmpty()) {
3382                List<String> aliasList = new ArrayList<String>();
3383                for (CmsAlias alias : aliases) {
3384                    String aliasPath = alias.getAliasPath();
3385                    aliasList.add(aliasPath);
3386                }
3387                clientEntry.setAliases(aliasList);
3388            }
3389        }
3390        long dateExpired = navElement.getResource().getDateExpired();
3391        if (dateExpired != CmsResource.DATE_EXPIRED_DEFAULT) {
3392            clientEntry.setDateExpired(
3393                CmsDateUtil.getDate(new Date(dateExpired), DateFormat.SHORT, getWorkplaceLocale()));
3394        }
3395        long dateReleased = navElement.getResource().getDateReleased();
3396        if (dateReleased != CmsResource.DATE_RELEASED_DEFAULT) {
3397            clientEntry.setDateReleased(
3398                CmsDateUtil.getDate(new Date(dateReleased), DateFormat.SHORT, getWorkplaceLocale()));
3399        }
3400        clientEntry.setResleasedAndNotExpired(
3401            navElement.getResource().isReleasedAndNotExpired(System.currentTimeMillis()));
3402        String path = cms.getSitePath(entryPage);
3403        clientEntry.setVfsPath(path);
3404        clientEntry.setOwnProperties(ownProps);
3405        clientEntry.setDefaultFileProperties(defaultFileProps);
3406        clientEntry.setSitePath(entryFolder != null ? cms.getSitePath(entryFolder) : path);
3407        clientEntry.setLock(generateClientLock(entryPage));
3408        clientEntry.setInNavigation(isRoot || navElement.isInNavigation());
3409        String type = OpenCms.getResourceManager().getResourceType(ownResource).getTypeName();
3410        clientEntry.setResourceTypeName(type);
3411        clientEntry.setVfsModeIcon(CmsIconUtil.getIconClasses(type, ownResource.getName(), false));
3412
3413        if (!clientEntry.isSubSitemapType()) {
3414            if (clientEntry.isNavigationLevelType()) {
3415                clientEntry.setNavModeIcon(CmsIconUtil.ICON_NAV_LEVEL_BIG);
3416            } else if (defaultFileResource != null) {
3417                clientEntry.setNavModeIcon(
3418                    CmsIconUtil.getIconClasses(clientEntry.getDefaultFileType(), defaultFileResource.getName(), false));
3419            }
3420        }
3421        clientEntry.setPermissionInfo(OpenCms.getADEManager().getPermissionInfo(cms, ownResource, null));
3422        return clientEntry;
3423    }
3424
3425    /**
3426     * Un-deletes a resource according to the change data.<p>
3427     *
3428     * @param change the change data
3429     *
3430     * @return the changed entry or <code>null</code>
3431     *
3432     * @throws CmsException if something goes wrong
3433     */
3434    private CmsSitemapChange undelete(CmsSitemapChange change) throws CmsException {
3435
3436        CmsObject cms = getCmsObject();
3437        CmsResource deleted = cms.readResource(change.getEntryId(), CmsResourceFilter.ALL);
3438        if (deleted.getState().isDeleted()) {
3439            ensureLock(deleted);
3440            cms.undeleteResource(getCmsObject().getSitePath(deleted), true);
3441            tryUnlock(deleted);
3442        }
3443        String parentPath = CmsResource.getParentFolder(cms.getSitePath(deleted));
3444        CmsJspNavElement navElement = getNavBuilder().getNavigationForResource(
3445            parentPath,
3446            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
3447        CmsClientSitemapEntry entry = toClientEntry(navElement, navElement.isInNavigation());
3448        entry.setSubEntries(getChildren(parentPath, 2, null), null);
3449        change.setUpdatedEntry(entry);
3450        change.setParentId(cms.readParentFolder(deleted.getStructureId()).getStructureId());
3451        return change;
3452    }
3453
3454    /**
3455     * Updates the navigation position for a resource.<p>
3456     *
3457     * @param res the resource for which to update the navigation position
3458     * @param change the sitemap change
3459     *
3460     * @throws CmsException if something goes wrong
3461     */
3462    private void updateNavPos(CmsResource res, CmsSitemapChange change) throws CmsException {
3463
3464        if (change.hasChangedPosition()) {
3465            applyNavigationChanges(change, res);
3466        }
3467    }
3468
3469    /**
3470     * Updates properties for a resource and possibly its detail page.<p>
3471     *
3472     * @param cms the CMS context
3473     * @param ownRes the resource
3474     * @param defaultFileRes the default file resource (possibly null)
3475     * @param propertyModifications the property modifications
3476     *
3477     * @throws CmsException if something goes wrong
3478     */
3479    private void updateProperties(
3480        CmsObject cms,
3481        CmsResource ownRes,
3482        CmsResource defaultFileRes,
3483        List<CmsPropertyModification> propertyModifications)
3484    throws CmsException {
3485
3486        Map<String, CmsProperty> ownProps = getPropertiesByName(cms.readPropertyObjects(ownRes, false));
3487        String oldNavText = null;
3488        if (ownProps.containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT)) {
3489            oldNavText = ownProps.get(CmsPropertyDefinition.PROPERTY_NAVTEXT).getValue();
3490        }
3491        if (oldNavText == null) {
3492            oldNavText = "";
3493        }
3494        Map<String, CmsProperty> defaultFileProps = Maps.newHashMap();
3495        if (defaultFileRes != null) {
3496            defaultFileProps = getPropertiesByName(cms.readPropertyObjects(defaultFileRes, false));
3497        }
3498        String newNavText = oldNavText;
3499        List<CmsProperty> ownPropertyChanges = new ArrayList<CmsProperty>();
3500        List<CmsProperty> defaultFilePropertyChanges = new ArrayList<CmsProperty>();
3501        for (CmsPropertyModification propMod : propertyModifications) {
3502            CmsProperty propToModify = null;
3503            Map<String, CmsProperty> propMap = null;
3504            List<CmsProperty> changeList = null;
3505
3506            if (ownRes.getStructureId().equals(propMod.getId())) {
3507                if (CmsPropertyDefinition.PROPERTY_NAVTEXT.equals(propMod.getName())) {
3508                    newNavText = propMod.getValue();
3509                }
3510                propMap = ownProps;
3511                changeList = ownPropertyChanges;
3512            } else {
3513                propMap = defaultFileProps;
3514                changeList = defaultFilePropertyChanges;
3515            }
3516            propToModify = propMap.get(propMod.getName());
3517            if (propToModify == null) {
3518                propToModify = new CmsProperty(propMod.getName(), null, null);
3519            }
3520            String newValue = propMod.getValue();
3521            if (newValue == null) {
3522                newValue = "";
3523            }
3524            if (propMod.isStructureValue()) {
3525                propToModify.setStructureValue(newValue);
3526            } else {
3527                propToModify.setResourceValue(newValue);
3528            }
3529            changeList.add(propToModify);
3530        }
3531
3532        if (!ownPropertyChanges.isEmpty()) {
3533            cms.writePropertyObjects(ownRes, ownPropertyChanges);
3534        }
3535        if (!defaultFilePropertyChanges.isEmpty() && (defaultFileRes != null)) {
3536            cms.writePropertyObjects(defaultFileRes, defaultFilePropertyChanges);
3537        }
3538        synchTitleFromNavText(ownRes, oldNavText, newNavText);
3539        if (defaultFileRes != null) {
3540            synchTitleFromNavText(defaultFileRes, oldNavText, newNavText);
3541        }
3542    }
3543
3544}