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