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