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