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