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.containerpage;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsADEManager;
032import org.opencms.ade.configuration.CmsElementView;
033import org.opencms.ade.configuration.CmsModelPageConfig;
034import org.opencms.ade.configuration.CmsResourceTypeConfig;
035import org.opencms.ade.containerpage.inherited.CmsInheritanceReference;
036import org.opencms.ade.containerpage.inherited.CmsInheritanceReferenceParser;
037import org.opencms.ade.containerpage.inherited.CmsInheritedContainerState;
038import org.opencms.ade.containerpage.shared.CmsCntPageData;
039import org.opencms.ade.containerpage.shared.CmsCntPageData.ElementDeleteMode;
040import org.opencms.ade.containerpage.shared.CmsCntPageData.ElementReuseMode;
041import org.opencms.ade.containerpage.shared.CmsContainer;
042import org.opencms.ade.containerpage.shared.CmsContainerElement;
043import org.opencms.ade.containerpage.shared.CmsContainerElement.ModelGroupState;
044import org.opencms.ade.containerpage.shared.CmsContainerElementData;
045import org.opencms.ade.containerpage.shared.CmsContainerPageGalleryData;
046import org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext;
047import org.opencms.ade.containerpage.shared.CmsCreateElementData;
048import org.opencms.ade.containerpage.shared.CmsDialogOptions;
049import org.opencms.ade.containerpage.shared.CmsDialogOptionsAndInfo;
050import org.opencms.ade.containerpage.shared.CmsElementSettingsConfig;
051import org.opencms.ade.containerpage.shared.CmsElementViewInfo;
052import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
053import org.opencms.ade.containerpage.shared.CmsGroupContainer;
054import org.opencms.ade.containerpage.shared.CmsGroupContainerSaveResult;
055import org.opencms.ade.containerpage.shared.CmsInheritanceContainer;
056import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
057import org.opencms.ade.containerpage.shared.CmsLocaleLinkBean;
058import org.opencms.ade.containerpage.shared.CmsRemovedElementStatus;
059import org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService;
060import org.opencms.ade.detailpage.CmsDetailPageInfo;
061import org.opencms.ade.detailpage.CmsDetailPageResourceHandler;
062import org.opencms.ade.galleries.CmsGalleryService;
063import org.opencms.ade.galleries.shared.CmsGalleryDataBean;
064import org.opencms.ade.galleries.shared.CmsGallerySearchBean;
065import org.opencms.ade.galleries.shared.CmsResourceTypeBean;
066import org.opencms.ade.galleries.shared.CmsVfsEntryBean;
067import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryTabId;
068import org.opencms.ade.sitemap.CmsVfsSitemapService;
069import org.opencms.file.CmsFile;
070import org.opencms.file.CmsObject;
071import org.opencms.file.CmsProperty;
072import org.opencms.file.CmsPropertyDefinition;
073import org.opencms.file.CmsResource;
074import org.opencms.file.CmsResourceFilter;
075import org.opencms.file.CmsUser;
076import org.opencms.file.CmsVfsResourceNotFoundException;
077import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
078import org.opencms.file.types.CmsResourceTypeXmlContent;
079import org.opencms.file.types.I_CmsResourceType;
080import org.opencms.flex.CmsFlexController;
081import org.opencms.gwt.CmsDefaultResourceStatusProvider;
082import org.opencms.gwt.CmsGwtActionElement;
083import org.opencms.gwt.CmsGwtService;
084import org.opencms.gwt.CmsIconUtil;
085import org.opencms.gwt.CmsRpcException;
086import org.opencms.gwt.CmsVfsService;
087import org.opencms.gwt.shared.CmsListInfoBean;
088import org.opencms.gwt.shared.CmsModelResourceInfo;
089import org.opencms.gwt.shared.CmsTemplateContextInfo;
090import org.opencms.gwt.shared.I_CmsUnlockData;
091import org.opencms.gwt.shared.I_CmsUnlockDataFactory;
092import org.opencms.i18n.CmsEncoder;
093import org.opencms.i18n.CmsLocaleGroup;
094import org.opencms.i18n.CmsLocaleManager;
095import org.opencms.jsp.util.CmsJspStandardContextBean.TemplateBean;
096import org.opencms.loader.CmsTemplateContextManager;
097import org.opencms.lock.CmsLock;
098import org.opencms.lock.CmsLockType;
099import org.opencms.main.CmsException;
100import org.opencms.main.CmsIllegalArgumentException;
101import org.opencms.main.CmsLog;
102import org.opencms.main.OpenCms;
103import org.opencms.module.CmsModule;
104import org.opencms.relations.CmsRelation;
105import org.opencms.relations.CmsRelationFilter;
106import org.opencms.relations.CmsRelationType;
107import org.opencms.search.galleries.CmsGallerySearch;
108import org.opencms.search.galleries.CmsGallerySearchResult;
109import org.opencms.security.CmsPermissionSet;
110import org.opencms.security.CmsRole;
111import org.opencms.site.CmsSite;
112import org.opencms.site.CmsSiteManagerImpl;
113import org.opencms.ui.apps.CmsQuickLaunchLocationCache;
114import org.opencms.util.CmsFileUtil;
115import org.opencms.util.CmsPair;
116import org.opencms.util.CmsRequestUtil;
117import org.opencms.util.CmsStringUtil;
118import org.opencms.util.CmsUUID;
119import org.opencms.workplace.CmsWorkplace;
120import org.opencms.workplace.CmsWorkplaceSettings;
121import org.opencms.workplace.editors.CmsWorkplaceEditorManager;
122import org.opencms.workplace.editors.directedit.I_CmsEditHandler;
123import org.opencms.workplace.explorer.CmsResourceUtil;
124import org.opencms.xml.CmsXmlException;
125import org.opencms.xml.containerpage.CmsADESessionCache;
126import org.opencms.xml.containerpage.CmsContainerBean;
127import org.opencms.xml.containerpage.CmsContainerElementBean;
128import org.opencms.xml.containerpage.CmsContainerPageBean;
129import org.opencms.xml.containerpage.CmsFormatterConfiguration;
130import org.opencms.xml.containerpage.CmsGroupContainerBean;
131import org.opencms.xml.containerpage.CmsMacroFormatterBean;
132import org.opencms.xml.containerpage.CmsXmlContainerPage;
133import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
134import org.opencms.xml.containerpage.CmsXmlGroupContainer;
135import org.opencms.xml.containerpage.CmsXmlGroupContainerFactory;
136import org.opencms.xml.containerpage.I_CmsFormatterBean;
137import org.opencms.xml.content.CmsXmlContent;
138import org.opencms.xml.content.CmsXmlContentFactory;
139import org.opencms.xml.content.CmsXmlContentProperty;
140import org.opencms.xml.content.CmsXmlContentPropertyHelper;
141
142import java.util.ArrayList;
143import java.util.Arrays;
144import java.util.Collection;
145import java.util.Collections;
146import java.util.HashMap;
147import java.util.HashSet;
148import java.util.Iterator;
149import java.util.LinkedHashMap;
150import java.util.List;
151import java.util.Locale;
152import java.util.Map;
153import java.util.Map.Entry;
154import java.util.Set;
155
156import javax.servlet.http.HttpServletRequest;
157import javax.servlet.http.HttpServletResponse;
158
159import org.apache.commons.logging.Log;
160
161import com.google.common.base.Optional;
162import com.google.common.collect.Lists;
163import com.google.common.collect.Maps;
164import com.google.common.collect.Sets;
165import com.google.web.bindery.autobean.shared.AutoBean;
166import com.google.web.bindery.autobean.shared.AutoBeanCodex;
167import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
168
169/**
170 * The RPC service used by the container-page editor.<p>
171 *
172 * @since 8.0.0
173 */
174public class CmsContainerpageService extends CmsGwtService implements I_CmsContainerpageService {
175
176    /**
177     * Helper class used to determine both the available views and the active start view when loading a container page.<p>
178     */
179    private class InitialElementViewProvider {
180
181        /** Start view id. */
182        private CmsUUID m_defaultView;
183
184        /** Map of available views. */
185        private Map<CmsUUID, CmsElementViewInfo> m_viewMap;
186
187        /**
188         * Empty default constructor.<p>
189         */
190        public InitialElementViewProvider() {
191
192            // do nothing
193        }
194
195        /**
196         * Returns the default view info.<p>
197         *
198         * @return the default view info
199         */
200        public CmsElementViewInfo getDefaultView() {
201
202            return getViewMap().get(getDefaultViewId());
203        }
204
205        /**
206         * Gets the start view id.<p>
207         *
208         * @return the start view id
209         */
210        public CmsUUID getDefaultViewId() {
211
212            return m_defaultView;
213        }
214
215        /**
216         * Gets the map of available views.<p>
217         *
218         * @return the map of available views
219         */
220        public Map<CmsUUID, CmsElementViewInfo> getViewMap() {
221
222            return m_viewMap;
223        }
224
225        /**
226         * Initializes this object.<p>
227         *
228         * @param defaultValue the default view id from the session cache
229         * @param checkRes the resource used to check permissions
230         */
231        @SuppressWarnings("synthetic-access")
232        public void init(CmsUUID defaultValue, CmsResource checkRes) {
233
234            Map<CmsUUID, CmsElementViewInfo> result = new LinkedHashMap<CmsUUID, CmsElementViewInfo>();
235            CmsObject cms = getCmsObject();
236
237            // collect the actually used element view ids
238            CmsADEConfigData config = getConfigData(
239                cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()));
240            Set<CmsUUID> usedIds = new HashSet<CmsUUID>();
241            for (CmsResourceTypeConfig typeConfig : config.getResourceTypes()) {
242                usedIds.add(typeConfig.getElementView());
243            }
244
245            Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
246            Map<CmsUUID, CmsElementView> realViewMap = OpenCms.getADEManager().getElementViews(cms);
247
248            Set<CmsUUID> parentIds = Sets.newHashSet();
249            for (CmsElementView view : realViewMap.values()) {
250                if (view.getParentViewId() != null) {
251                    parentIds.add(view.getParentViewId());
252                }
253                // add only element view that are used within the type configuration and the user has sufficient permissions for
254                if (usedIds.contains(view.getId()) && view.hasPermission(cms, checkRes) && !view.isOther()) {
255                    result.put(view.getId(), new CmsElementViewInfo(view.getTitle(cms, wpLocale), view.getId()));
256                }
257
258            }
259            m_viewMap = result;
260            for (Map.Entry<CmsUUID, CmsElementViewInfo> viewEntry : m_viewMap.entrySet()) {
261                CmsElementView realView = realViewMap.get(viewEntry.getKey());
262                CmsUUID parentViewId = realView.getParentViewId();
263                if ((parentViewId != null) && !parentIds.contains(viewEntry.getKey())) {
264                    CmsElementViewInfo parentBean = m_viewMap.get(parentViewId);
265                    if (parentBean != null) {
266                        viewEntry.getValue().setParent(parentBean);
267                    }
268                }
269            }
270            if (m_viewMap.containsKey(defaultValue)) {
271                m_defaultView = defaultValue;
272            } else if (m_viewMap.containsKey(CmsElementView.DEFAULT_ELEMENT_VIEW.getId())) {
273                m_defaultView = CmsElementView.DEFAULT_ELEMENT_VIEW.getId();
274            } else if (!m_viewMap.isEmpty()) {
275                m_defaultView = m_viewMap.values().iterator().next().getElementViewId();
276            } else {
277                m_defaultView = defaultValue;
278                LOG.error(
279                    "Initial view not available and no suitable replacement view found: user="
280                        + getCmsObject().getRequestContext().getCurrentUser().getName()
281                        + " view="
282                        + defaultValue
283                        + " path="
284                        + checkRes.getRootPath());
285            }
286
287        }
288    }
289
290    /** Additional info key for storing the "edit small elements" setting on the user. */
291    public static final String ADDINFO_EDIT_SMALL_ELEMENTS = "EDIT_SMALL_ELEMENTS";
292
293    /** Session attribute name used to store the selected clipboard tab. */
294    public static final String ATTR_CLIPBOARD_TAB = "clipboardtab";
295
296    /** The model group pages path fragment. */
297    public static final String MODEL_GROUP_PATH_FRAGMENT = "/.content/.modelgroups/";
298
299    /** The source container page id settings key. */
300    public static final String SOURCE_CONTAINERPAGE_ID_SETTING = "source_containerpage_id";
301
302    /** Static reference to the log. */
303    static final Log LOG = CmsLog.getLog(CmsContainerpageService.class);
304
305    /** Serial version UID. */
306    private static final long serialVersionUID = -6188370638303594280L;
307
308    /** The configuration data of the current container page context. */
309    private CmsADEConfigData m_configData;
310
311    /** The session cache. */
312    private CmsADESessionCache m_sessionCache;
313
314    /** The workplace settings. */
315    private CmsWorkplaceSettings m_workplaceSettings;
316
317    /**
318     * Generates the model resource data list.<p>
319     *
320     * @param cms the cms context
321     * @param resourceType the resource type name
322     * @param modelResources the model resource
323     * @param contentLocale the content locale
324     *
325     * @return the model resources data
326     *
327     * @throws CmsException if something goes wrong reading the resource information
328     */
329    public static List<CmsModelResourceInfo> generateModelResourceList(
330        CmsObject cms,
331        String resourceType,
332        List<CmsResource> modelResources,
333        Locale contentLocale)
334    throws CmsException {
335
336        List<CmsModelResourceInfo> result = new ArrayList<CmsModelResourceInfo>();
337        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
338        CmsModelResourceInfo defaultInfo = new CmsModelResourceInfo(
339            Messages.get().getBundle(wpLocale).key(Messages.GUI_TITLE_DEFAULT_RESOURCE_CONTENT_0),
340            Messages.get().getBundle(wpLocale).key(Messages.GUI_DESCRIPTION_DEFAULT_RESOURCE_CONTENT_0),
341            null);
342        defaultInfo.setResourceType(resourceType);
343        result.add(defaultInfo);
344        for (CmsResource model : modelResources) {
345            CmsGallerySearchResult searchInfo = CmsGallerySearch.searchById(cms, model.getStructureId(), contentLocale);
346            CmsModelResourceInfo modelInfo = new CmsModelResourceInfo(
347                searchInfo.getTitle(),
348                searchInfo.getDescription(),
349                null);
350            modelInfo.addAdditionalInfo(
351                Messages.get().getBundle(wpLocale).key(Messages.GUI_LABEL_PATH_0),
352                cms.getSitePath(model));
353            modelInfo.setResourceType(resourceType);
354            modelInfo.setStructureId(model.getStructureId());
355            result.add(modelInfo);
356        }
357        return result;
358    }
359
360    /**
361     * Returns serialized container data.<p>
362     *
363     * @param container the container
364     *
365     * @return the serialized data
366     *
367     * @throws Exception if serialization fails
368     */
369    public static String getSerializedContainerInfo(CmsContainer container) throws Exception {
370
371        return CmsGwtActionElement.serialize(I_CmsContainerpageService.class.getMethod("getContainerInfo"), container);
372    }
373
374    /**
375     * Returns the serialized element data.<p>
376     *
377     * @param cms the cms context
378     * @param request the servlet request
379     * @param response the servlet response
380     * @param elementBean the element to serialize
381     * @param page the container page
382     *
383     * @return the serialized element data
384     *
385     * @throws Exception if something goes wrong
386     */
387    public static String getSerializedElementInfo(
388        CmsObject cms,
389        HttpServletRequest request,
390        HttpServletResponse response,
391        CmsContainerElementBean elementBean,
392        CmsContainerPageBean page)
393    throws Exception {
394
395        CmsContainerElement result = new CmsContainerElement();
396        CmsElementUtil util = new CmsElementUtil(
397            cms,
398            cms.getRequestContext().getUri(),
399            page,
400            null,
401            request,
402            response,
403            false,
404            cms.getRequestContext().getLocale());
405        util.setElementInfo(elementBean, result);
406        return CmsGwtActionElement.serialize(I_CmsContainerpageService.class.getMethod("getElementInfo"), result);
407    }
408
409    /**
410     * Checks whether the current page is a model group page.<p>
411     *
412     * @param cms the CMS context
413     * @param containerPage the current page
414     *
415     * @return <code>true</code> if the current page is a model group page
416     */
417    public static boolean isEditingModelGroups(CmsObject cms, CmsResource containerPage) {
418
419        return (OpenCms.getResourceManager().getResourceType(containerPage).getTypeName().equals(
420            CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME)
421            && OpenCms.getRoleManager().hasRole(cms, CmsRole.DEVELOPER));
422    }
423
424    /**
425     * Fetches the container page data.<p>
426     *
427     * @param request the current request
428     *
429     * @return the container page data
430     *
431     * @throws CmsRpcException if something goes wrong
432     */
433    public static CmsCntPageData prefetch(HttpServletRequest request) throws CmsRpcException {
434
435        CmsContainerpageService srv = new CmsContainerpageService();
436        srv.setCms(CmsFlexController.getCmsObject(request));
437        srv.setRequest(request);
438        CmsCntPageData result = null;
439        try {
440            result = srv.prefetch();
441        } finally {
442            srv.clearThreadStorage();
443        }
444        return result;
445    }
446
447    /**
448     * Unlocks a page or set of pages if they are locked by the current user.
449     *
450     * <p>This is not called via the normal GWT-RPC mechanism, but with the browser's sendBeacon function.
451     *
452     * @param cms the CMS context
453     * @param request the current request
454     * @param response the current response
455     * @throws Exception if something goes wrong
456     */
457    public static void unlockPage(CmsObject cms, HttpServletRequest request, HttpServletResponse response)
458    throws Exception {
459
460        // don't bother doing anything unless we have a session and are offline
461
462        if (request.getSession(false) == null) {
463            LOG.debug("no session found");
464            return;
465        }
466        if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
467            LOG.debug("can't unlock page in online project");
468            return;
469        }
470        byte[] byteData = CmsFileUtil.readFully(request.getInputStream(), false);
471
472        String encoding = request.getCharacterEncoding();
473        if (encoding == null) {
474            encoding = "UTF-8";
475        }
476        String strData = new String(byteData, encoding);
477        LOG.debug("Unlock request received: " + strData);
478
479        AutoBean<I_CmsUnlockData> data = AutoBeanCodex.decode(
480            AutoBeanFactorySource.create(I_CmsUnlockDataFactory.class),
481            I_CmsUnlockData.class,
482            strData);
483
484        I_CmsUnlockData unlockData = data.as();
485        List<CmsUUID> ids = new ArrayList<>();
486        if (CmsUUID.isValidUUID(unlockData.getPageId())) {
487            ids.add(new CmsUUID(unlockData.getPageId()));
488        }
489        CmsUUID detailId = null;
490        if (CmsUUID.isValidUUID(unlockData.getDetailId())) {
491            detailId = new CmsUUID(unlockData.getDetailId());
492            try {
493                CmsResource detailResource = cms.readResource(detailId, CmsResourceFilter.ALL);
494                Optional<CmsResource> detailOnlyPage = CmsDetailOnlyContainerUtil.getDetailOnlyPage(
495                    cms,
496                    detailResource,
497                    unlockData.getLocale());
498                if (detailOnlyPage.isPresent()) {
499                    ids.add(detailOnlyPage.get().getStructureId());
500                }
501            } catch (CmsVfsResourceNotFoundException e) {
502                LOG.info(e.getLocalizedMessage(), e);
503            } catch (Exception e) {
504                LOG.error(e.getLocalizedMessage(), e);
505            }
506        }
507
508        for (CmsUUID id : ids) {
509            try {
510                CmsResource page = cms.readResource(id, CmsResourceFilter.IGNORE_EXPIRATION);
511                CmsLock lock = cms.getLock(page);
512                if (!lock.isUnlocked() && lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
513                    LOG.debug("Unlocking " + page.getRootPath());
514                    cms.unlockResource(page);
515                } else {
516                    LOG.debug("Can't unlock " + page.getRootPath() + " because it's not locked for the current user.");
517                }
518            } catch (Exception e) {
519                LOG.error(e.getLocalizedMessage(), e);
520            }
521        }
522    }
523
524    /**
525     * Returns the server id part of the given client id.<p>
526     *
527     * @param id the id
528     *
529     * @return the server id
530     */
531    private static String getServerIdString(String id) {
532
533        if (id.contains(CmsADEManager.CLIENT_ID_SEPERATOR)) {
534            id = id.substring(0, id.indexOf(CmsADEManager.CLIENT_ID_SEPERATOR));
535        }
536        return id;
537    }
538
539    /**
540     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#addToFavoriteList(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, java.lang.String)
541     */
542    public void addToFavoriteList(CmsContainerPageRpcContext context, String clientId) throws CmsRpcException {
543
544        try {
545            ensureSession();
546            List<CmsContainerElementBean> list = OpenCms.getADEManager().getFavoriteList(getCmsObject());
547            CmsResource containerPage = getCmsObject().readResource(
548                context.getPageStructureId(),
549                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
550            updateFavoriteRecentList(containerPage, clientId, list);
551            OpenCms.getADEManager().saveFavoriteList(getCmsObject(), list);
552        } catch (Throwable e) {
553            error(e);
554        }
555    }
556
557    /**
558     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#addToRecentList(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, java.lang.String)
559     */
560    public void addToRecentList(CmsContainerPageRpcContext context, String clientId) throws CmsRpcException {
561
562        try {
563            ensureSession();
564            List<CmsContainerElementBean> list = OpenCms.getADEManager().getRecentList(getCmsObject());
565            CmsResource containerPage = getCmsObject().readResource(
566                context.getPageStructureId(),
567                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
568            updateFavoriteRecentList(containerPage, clientId, list);
569            OpenCms.getADEManager().saveRecentList(getCmsObject(), list);
570        } catch (Throwable e) {
571            error(e);
572        }
573    }
574
575    /**
576     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#checkContainerpageOrElementsChanged(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID, java.lang.String)
577     */
578    public boolean checkContainerpageOrElementsChanged(
579        CmsUUID structureId,
580        CmsUUID detailContentId,
581        String contentLocale)
582    throws CmsRpcException {
583
584        try {
585            List<CmsUUID> additionalIds = new ArrayList<CmsUUID>();
586            additionalIds.add(structureId);
587            boolean detailOnlyChanged = false;
588            if (detailContentId != null) {
589                additionalIds.add(detailContentId);
590                try {
591
592                    CmsObject cms = getCmsObject();
593                    CmsResource detailContentRes = cms.readResource(detailContentId, CmsResourceFilter.ALL);
594                    OpenCms.getLocaleManager();
595                    CmsResource page = cms.readResource(structureId, CmsResourceFilter.ignoreExpirationOffline(cms));
596                    Optional<CmsResource> detailOnlyRes = CmsDetailOnlyContainerUtil.getDetailOnlyResource(
597                        cms,
598                        contentLocale,
599                        detailContentRes,
600                        page);
601                    if (detailOnlyRes.isPresent()) {
602                        detailOnlyChanged = CmsDefaultResourceStatusProvider.getContainerpageRelationTargets(
603                            getCmsObject(),
604                            detailOnlyRes.get().getStructureId(),
605                            Arrays.asList(detailOnlyRes.get().getStructureId()),
606                            true).isChanged();
607                    }
608                } catch (CmsException e) {
609                    LOG.error(e.getLocalizedMessage(), e);
610                }
611            }
612            return detailOnlyChanged
613                || CmsDefaultResourceStatusProvider.getContainerpageRelationTargets(
614                    getCmsObject(),
615                    structureId,
616                    additionalIds,
617                    true /*stop looking if we find a changed resource.*/).isChanged();
618        } catch (Throwable e) {
619            error(e);
620            return false; // will never be reached
621        }
622
623    }
624
625    /**
626     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#checkCreateNewElement(org.opencms.util.CmsUUID, java.lang.String, java.lang.String, org.opencms.ade.containerpage.shared.CmsContainer, java.lang.String)
627     */
628    public CmsCreateElementData checkCreateNewElement(
629        CmsUUID pageStructureId,
630        String clientId,
631        String resourceType,
632        CmsContainer container,
633        String locale)
634    throws CmsRpcException {
635
636        CmsObject cms = getCmsObject();
637        CmsCreateElementData result = new CmsCreateElementData();
638        try {
639            CmsResource currentPage = cms.readResource(pageStructureId, CmsResourceFilter.ignoreExpirationOffline(cms));
640
641            List<CmsResource> modelResources = CmsResourceTypeXmlContent.getModelFiles(
642                getCmsObject(),
643                CmsResource.getFolderPath(cms.getSitePath(currentPage)),
644                resourceType);
645            if (modelResources.isEmpty()) {
646                CmsContainerElementBean bean = getCachedElement(clientId, currentPage.getRootPath());
647                I_CmsFormatterBean formatter = CmsElementUtil.getFormatterForContainer(
648                    cms,
649                    bean,
650                    container,
651                    getConfigData(currentPage.getRootPath()),
652                    getSessionCache());
653                CmsUUID modelResId = null;
654                if (formatter instanceof CmsMacroFormatterBean) {
655                    modelResId = ((CmsMacroFormatterBean)formatter).getDefaultContentStructureId();
656                }
657                result.setCreatedElement(createNewElement(pageStructureId, clientId, resourceType, modelResId, locale));
658            } else {
659                result.setModelResources(
660                    generateModelResourceList(
661                        getCmsObject(),
662                        resourceType,
663                        modelResources,
664                        CmsLocaleManager.getLocale(locale)));
665            }
666        } catch (CmsException e) {
667            error(e);
668        }
669        return result;
670    }
671
672    /**
673     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#checkNewWidgetsAvailable(org.opencms.util.CmsUUID)
674     */
675    public boolean checkNewWidgetsAvailable(CmsUUID structureId) throws CmsRpcException {
676
677        try {
678            CmsObject cms = getCmsObject();
679            CmsResource resource = cms.readResource(structureId);
680            return CmsWorkplaceEditorManager.checkAcaciaEditorAvailable(cms, resource);
681        } catch (Throwable t) {
682            error(t);
683        }
684        return false;
685    }
686
687    /**
688     * Parses an element id.<p>
689     *
690     * @param id the element id
691     *
692     * @return the corresponding structure id
693     *
694     * @throws CmsIllegalArgumentException if the id has not the right format
695     */
696    public CmsUUID convertToServerId(String id) throws CmsIllegalArgumentException {
697
698        if (id == null) {
699            throw new CmsIllegalArgumentException(
700                org.opencms.xml.containerpage.Messages.get().container(
701                    org.opencms.xml.containerpage.Messages.ERR_INVALID_ID_1,
702                    id));
703        }
704        String serverId = getServerIdString(id);
705        try {
706            return new CmsUUID(serverId);
707        } catch (NumberFormatException e) {
708            throw new CmsIllegalArgumentException(
709                org.opencms.xml.containerpage.Messages.get().container(
710                    org.opencms.xml.containerpage.Messages.ERR_INVALID_ID_1,
711                    id),
712                e);
713        }
714    }
715
716    /**
717     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#copyElement(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID, java.lang.String)
718     */
719    public CmsUUID copyElement(CmsUUID pageId, CmsUUID originalElementId, String locale) throws CmsRpcException {
720
721        try {
722            CmsObject cms = OpenCms.initCmsObject(getCmsObject());
723            cms.getRequestContext().setLocale(CmsLocaleManager.getLocale(locale));
724            CmsResource page = cms.readResource(pageId, CmsResourceFilter.IGNORE_EXPIRATION);
725            CmsResource element = cms.readResource(originalElementId, CmsResourceFilter.IGNORE_EXPIRATION);
726            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(cms, page.getRootPath());
727            String typeName = OpenCms.getResourceManager().getResourceType(element.getTypeId()).getTypeName();
728            CmsResourceTypeConfig typeConfig = config.getResourceType(typeName);
729            if (typeConfig == null) {
730                LOG.error("copyElement: Type not configured in ADE configuration: " + typeName);
731                return originalElementId;
732            } else {
733                CmsResource newResource = typeConfig.createNewElement(
734                    cms,
735                    element,
736                    CmsResource.getParentFolder(page.getRootPath()));
737                return newResource.getStructureId();
738            }
739        } catch (Throwable e) {
740            error(e);
741            return null; // will never be reached
742        }
743    }
744
745    /**
746     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#createNewElement(org.opencms.util.CmsUUID, java.lang.String, java.lang.String, org.opencms.util.CmsUUID, java.lang.String)
747     */
748    public CmsContainerElement createNewElement(
749        CmsUUID pageStructureId,
750        String clientId,
751        String resourceType,
752        CmsUUID modelResourceStructureId,
753        String locale)
754    throws CmsRpcException {
755
756        CmsContainerElement element = null;
757        try {
758            ensureSession();
759            CmsObject cms = getCmsObject();
760            CmsResource pageResource = cms.readResource(
761                pageStructureId,
762                CmsResourceFilter.ignoreExpirationOffline(cms));
763            CmsADEConfigData configData = getConfigData(pageResource.getRootPath());
764            CmsResourceTypeConfig typeConfig = configData.getResourceType(resourceType);
765            CmsObject cloneCms = OpenCms.initCmsObject(cms);
766            cloneCms.getRequestContext().setLocale(CmsLocaleManager.getLocale(locale));
767
768            CmsResource modelResource = null;
769            if (modelResourceStructureId != null) {
770                modelResource = cms.readResource(modelResourceStructureId);
771            }
772            CmsResource newResource = typeConfig.createNewElement(
773                cloneCms,
774                modelResource,
775                CmsResource.getParentFolder(pageResource.getRootPath()));
776            CmsContainerElementBean bean = getCachedElement(clientId, pageResource.getRootPath());
777            Map<String, String> settings = new HashMap<String, String>();
778
779            settings = bean.getIndividualSettings();
780
781            CmsContainerElementBean newBean = new CmsContainerElementBean(
782                newResource.getStructureId(),
783                null,
784                settings,
785                typeConfig.isCopyInModels());
786            String newClientId = newBean.editorHash();
787            getSessionCache().setCacheContainerElement(newClientId, newBean);
788            element = new CmsContainerElement();
789            element.setNewEditorDisabled(!CmsWorkplaceEditorManager.checkAcaciaEditorAvailable(cms, newResource));
790            element.setClientId(newClientId);
791            element.setSitePath(cms.getSitePath(newResource));
792            element.setResourceType(resourceType);
793            element.setIconClasses(
794                CmsIconUtil.getIconClasses(CmsIconUtil.getDisplayType(cms, newResource), null, false));
795            element.setCreateNew(newBean.isCreateNew());
796        } catch (CmsException e) {
797            error(e);
798        }
799        return element;
800    }
801
802    /**
803     * Reads the cached element-bean for the given client-side-id from cache.<p>
804     *
805     * @param clientId the client-side-id
806     * @param pageRootPath the container page root path
807     *
808     * @return the cached container element bean
809     *
810     * @throws CmsException in case reading the element resource fails
811     */
812    public CmsContainerElementBean getCachedElement(String clientId, String pageRootPath) throws CmsException {
813
814        String id = clientId;
815        CmsContainerElementBean element = null;
816        element = getSessionCache().getCacheContainerElement(id);
817        if (element != null) {
818            return element;
819        }
820        if (id.contains(CmsADEManager.CLIENT_ID_SEPERATOR)) {
821            throw new CmsException(Messages.get().container(Messages.ERR_MISSING_CACHED_ELEMENT_0));
822        }
823        // this is necessary if the element has not been cached yet
824        CmsResource resource = getCmsObject().readResource(convertToServerId(id), CmsResourceFilter.IGNORE_EXPIRATION);
825        CmsADEConfigData configData = getConfigData(pageRootPath);
826        CmsResourceTypeConfig typeConfig = configData.getResourceType(
827            OpenCms.getResourceManager().getResourceType(resource).getTypeName());
828        element = new CmsContainerElementBean(
829            convertToServerId(id),
830            null,
831            null,
832            (typeConfig != null) && typeConfig.isCopyInModels());
833        getSessionCache().setCacheContainerElement(element.editorHash(), element);
834        return element;
835    }
836
837    /**
838     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getContainerInfo()
839     */
840    public CmsContainer getContainerInfo() {
841
842        throw new UnsupportedOperationException("This method is used for serialization only.");
843    }
844
845    /**
846     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getDeleteOptions(java.lang.String, org.opencms.util.CmsUUID, java.lang.String)
847     */
848    public CmsDialogOptionsAndInfo getDeleteOptions(String clientId, CmsUUID pageId, String requestParams)
849    throws CmsRpcException {
850
851        try {
852            CmsResource pageResource = getCmsObject().readResource(
853                pageId,
854                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
855            CmsContainerElementBean element = getCachedElement(clientId, pageResource.getRootPath());
856            element.initResource(getCmsObject());
857            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
858            if (type instanceof CmsResourceTypeXmlContent) {
859                I_CmsEditHandler handler = ((CmsResourceTypeXmlContent)type).getEditHandler(getCmsObject());
860                Map<String, String[]> params = CmsRequestUtil.createParameterMap(
861                    CmsEncoder.decode(requestParams),
862                    true,
863                    CmsEncoder.ENCODING_UTF_8);
864                CmsDialogOptions options = handler.getDeleteOptions(getCmsObject(), element, pageId, params);
865                if (options != null) {
866                    return new CmsDialogOptionsAndInfo(
867                        options,
868                        CmsVfsService.getPageInfo(getCmsObject(), element.getResource()));
869                }
870            }
871        } catch (CmsException e) {
872            error(e);
873        }
874        return null;
875    }
876
877    /**
878     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getEditOptions(java.lang.String, org.opencms.util.CmsUUID, java.lang.String, boolean)
879     */
880    public CmsDialogOptionsAndInfo getEditOptions(
881        String clientId,
882        CmsUUID pageId,
883        String requestParams,
884        boolean isListElement)
885    throws CmsRpcException {
886
887        try {
888            CmsResource pageResource = getCmsObject().readResource(
889                pageId,
890                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
891            CmsContainerElementBean element = getCachedElement(clientId, pageResource.getRootPath());
892            element.initResource(getCmsObject());
893            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
894            if (type instanceof CmsResourceTypeXmlContent) {
895                I_CmsEditHandler handler = ((CmsResourceTypeXmlContent)type).getEditHandler(getCmsObject());
896                Map<String, String[]> params = CmsRequestUtil.createParameterMap(
897                    CmsEncoder.decode(requestParams),
898                    true,
899                    CmsEncoder.ENCODING_UTF_8);
900                CmsDialogOptions options = handler.getEditOptions(
901                    getCmsObject(),
902                    element,
903                    pageId,
904                    params,
905                    isListElement);
906                if (options != null) {
907                    return new CmsDialogOptionsAndInfo(
908                        options,
909                        CmsVfsService.getPageInfo(getCmsObject(), element.getResource()));
910                }
911            }
912        } catch (CmsException e) {
913            error(e);
914        }
915        return null;
916    }
917
918    /**
919     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getElementInfo()
920     */
921    public CmsContainerElement getElementInfo() {
922
923        throw new UnsupportedOperationException("This method is used for serialization only.");
924    }
925
926    /**
927     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getElementsData(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, org.opencms.util.CmsUUID, java.lang.String, java.util.Collection, java.util.Collection, boolean, java.lang.String, java.lang.String)
928     */
929    public Map<String, CmsContainerElementData> getElementsData(
930        CmsContainerPageRpcContext context,
931        CmsUUID detailContentId,
932        String reqParams,
933        Collection<String> clientIds,
934        Collection<CmsContainer> containers,
935        boolean allwaysCopy,
936        String dndSource,
937        String locale)
938    throws CmsRpcException {
939
940        Map<String, CmsContainerElementData> result = null;
941        try {
942            ensureSession();
943            CmsResource pageResource = getCmsObject().readResource(
944                context.getPageStructureId(),
945                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
946            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
947                getCmsObject(),
948                pageResource.getRootPath());
949            initRequestFromRpcContext(context);
950            String containerpageUri = getCmsObject().getSitePath(pageResource);
951            result = getElements(
952                config,
953                pageResource,
954                clientIds,
955                containerpageUri,
956                detailContentId,
957                containers,
958                allwaysCopy,
959                dndSource,
960                CmsStringUtil.isNotEmptyOrWhitespaceOnly(dndSource),
961                CmsLocaleManager.getLocale(locale));
962        } catch (Throwable e) {
963            error(e);
964        }
965        return result;
966    }
967
968    /**
969     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getElementSettingsConfig(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, java.lang.String, java.lang.String, java.util.Collection, java.lang.String)
970     */
971    public CmsElementSettingsConfig getElementSettingsConfig(
972        CmsContainerPageRpcContext context,
973        String clientId,
974        String containerId,
975        Collection<CmsContainer> containers,
976        String locale)
977    throws CmsRpcException {
978
979        try {
980            ensureSession();
981            CmsObject cms = getCmsObject();
982            CmsResource pageResource = cms.readResource(
983                context.getPageStructureId(),
984                CmsResourceFilter.ignoreExpirationOffline(cms));
985            initRequestFromRpcContext(context);
986            String containerpageUri = cms.getSitePath(pageResource);
987
988            CmsContainerPageBean pageBean = generateContainerPageForContainers(
989                containers,
990                cms.getRequestContext().addSiteRoot(containerpageUri));
991
992            CmsElementUtil elemUtil = new CmsElementUtil(
993                cms,
994                containerpageUri,
995                pageBean,
996                null,
997                getRequest(),
998                getResponse(),
999                false,
1000                CmsLocaleManager.getLocale(locale));
1001            CmsContainerElementBean element = getCachedElement(
1002                clientId,
1003                cms.getRequestContext().addSiteRoot(containerpageUri));
1004            if (element.getInstanceId() == null) {
1005                element = element.clone();
1006                getSessionCache().setCacheContainerElement(element.editorHash(), element);
1007            }
1008            element.initResource(cms);
1009            return elemUtil.getElementSettingsConfig(pageResource, element, containerId, containers);
1010        } catch (Throwable e) {
1011            error(e);
1012        }
1013        return null;
1014    }
1015
1016    /**
1017     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getElementsLockedForPublishing(java.util.Set)
1018     */
1019    public Set<CmsUUID> getElementsLockedForPublishing(Set<CmsUUID> idsToCheck) throws CmsRpcException {
1020
1021        try {
1022            CmsObject cms = getCmsObject();
1023            Set<CmsUUID> result = new HashSet<>();
1024            for (CmsUUID id : idsToCheck) {
1025                try {
1026                    CmsResource resource = cms.readResource(id, CmsResourceFilter.ALL);
1027                    CmsLock lock = cms.getLock(resource);
1028                    if (!lock.getSystemLock().isUnlocked()
1029                        && lock.getUserId().equals(cms.getRequestContext().getCurrentUser().getId())) {
1030                        result.add(resource.getStructureId());
1031                    }
1032                } catch (CmsVfsResourceNotFoundException e) {
1033                    LOG.debug(e.getLocalizedMessage(), e);
1034                }
1035            }
1036            return result;
1037        } catch (Exception e) {
1038            error(e);
1039            return null;
1040        }
1041    }
1042
1043    /**
1044     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getElementWithSettings(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, org.opencms.util.CmsUUID, java.lang.String, java.lang.String, java.util.Map, java.util.Collection, java.lang.String)
1045     */
1046    public CmsContainerElementData getElementWithSettings(
1047        CmsContainerPageRpcContext context,
1048        CmsUUID detailContentId,
1049        String uriParams,
1050        String clientId,
1051        Map<String, String> settings,
1052        Collection<CmsContainer> containers,
1053        String locale)
1054    throws CmsRpcException {
1055
1056        CmsContainerElementData element = null;
1057        try {
1058            ensureSession();
1059            CmsObject cms = getCmsObject();
1060            CmsResource pageResource = cms.readResource(
1061                context.getPageStructureId(),
1062                CmsResourceFilter.ignoreExpirationOffline(cms));
1063            initRequestFromRpcContext(context);
1064            String containerpageUri = cms.getSitePath(pageResource);
1065            Locale contentLocale = CmsLocaleManager.getLocale(locale);
1066            CmsElementUtil elemUtil = new CmsElementUtil(
1067                cms,
1068                containerpageUri,
1069                generateContainerPageForContainers(containers, pageResource.getRootPath()),
1070                detailContentId,
1071                getRequest(),
1072                getResponse(),
1073                false,
1074                contentLocale);
1075
1076            CmsContainerElementBean elementBean = getCachedElement(clientId, pageResource.getRootPath());
1077            elementBean.initResource(cms);
1078            storeFormatterSelection(elementBean, settings);
1079            // make sure to keep the element instance id
1080            if (!settings.containsKey(CmsContainerElement.ELEMENT_INSTANCE_ID)
1081                && elementBean.getIndividualSettings().containsKey(CmsContainerElement.ELEMENT_INSTANCE_ID)) {
1082                settings.put(
1083                    CmsContainerElement.ELEMENT_INSTANCE_ID,
1084                    elementBean.getIndividualSettings().get(CmsContainerElement.ELEMENT_INSTANCE_ID));
1085            }
1086
1087            elementBean = CmsContainerElementBean.cloneWithSettings(
1088                elementBean,
1089                convertSettingValues(elementBean.getResource(), settings, contentLocale));
1090            getSessionCache().setCacheContainerElement(elementBean.editorHash(), elementBean);
1091            element = elemUtil.getElementData(pageResource, elementBean, containers);
1092        } catch (Throwable e) {
1093            error(e);
1094        }
1095        return element;
1096    }
1097
1098    /**
1099     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getFavoriteList(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID, java.util.Collection, java.lang.String)
1100     */
1101    public List<CmsContainerElementData> getFavoriteList(
1102        CmsUUID pageStructureId,
1103        CmsUUID detailContentId,
1104        Collection<CmsContainer> containers,
1105        String locale)
1106    throws CmsRpcException {
1107
1108        List<CmsContainerElementData> result = null;
1109        try {
1110            ensureSession();
1111            CmsResource containerpage = getCmsObject().readResource(
1112                pageStructureId,
1113                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1114            String containerpageUri = getCmsObject().getSitePath(containerpage);
1115            result = getListElementsData(
1116                OpenCms.getADEManager().getFavoriteList(getCmsObject()),
1117                containerpageUri,
1118                detailContentId,
1119                containers,
1120                CmsLocaleManager.getLocale(locale));
1121        } catch (Throwable e) {
1122            error(e);
1123        }
1124        return result;
1125    }
1126
1127    /**
1128     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getGalleryDataForPage(java.util.List, org.opencms.util.CmsUUID, java.lang.String, java.lang.String)
1129     */
1130    public CmsContainerPageGalleryData getGalleryDataForPage(
1131        final List<CmsContainer> containers,
1132        CmsUUID elementView,
1133        String uri,
1134        String locale)
1135    throws CmsRpcException {
1136
1137        CmsGalleryDataBean data = null;
1138        try {
1139
1140            CmsObject cms = getCmsObject();
1141
1142            CmsAddDialogTypeHelper typeHelper = new CmsAddDialogTypeHelper(CmsResourceTypeConfig.AddMenuType.ade);
1143            List<CmsResourceTypeBean> resTypeBeans = typeHelper.getResourceTypes(
1144                cms,
1145                cms.getRequestContext().addSiteRoot(uri),
1146                uri,
1147                OpenCms.getADEManager().getElementViews(cms).get(elementView),
1148                new I_CmsResourceTypeEnabledCheck() {
1149
1150                    public boolean checkEnabled(
1151                        CmsObject paramCms,
1152                        CmsADEConfigData config,
1153                        I_CmsResourceType resType) {
1154
1155                        boolean isModelGroup = CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME.equals(
1156                            resType.getTypeName());
1157                        return isModelGroup || config.hasFormatters(paramCms, resType, containers);
1158                    }
1159                });
1160            CmsGalleryService srv = new CmsGalleryService();
1161            srv.setCms(cms);
1162            srv.setRequest(getRequest());
1163            data = srv.getInitialSettingsForContainerPage(resTypeBeans, uri, locale);
1164            CmsContainerPageGalleryData result = new CmsContainerPageGalleryData();
1165
1166            CmsADESessionCache cache = CmsADESessionCache.getCache(getRequest(), cms);
1167            CmsGallerySearchBean search = cache.getLastPageEditorGallerySearch();
1168            String subsite = OpenCms.getADEManager().getSubSiteRoot(cms, cms.addSiteRoot(uri));
1169            String searchStoreKey = elementView + "|" + subsite + "|" + locale;
1170            data.getContextParameters().put("searchStoreKey", searchStoreKey);
1171            if (search != null) {
1172                if (searchStoreKey.equals(
1173                    search.getOriginalGalleryData().getContextParameters().get("searchStoreKey"))) {
1174                    if (hasCompatibleSearchData(search.getOriginalGalleryData(), data, search)) {
1175
1176                        CmsVfsEntryBean preloadData = null;
1177                        if (search.getFolders() != null) {
1178                            preloadData = CmsGalleryService.generateVfsPreloadData(
1179                                getCmsObject(),
1180                                CmsGalleryService.getVfsTreeState(getRequest(), data.getTreeToken()),
1181                                search.getFolders());
1182                        }
1183
1184                        // only restore last result list if the search was performed in a 'similar' context
1185                        search.setTabId(GalleryTabId.cms_tab_results.toString());
1186                        search.setPage(1);
1187                        search.setLastPage(0);
1188                        data.setStartTab(GalleryTabId.cms_tab_results);
1189                        search = srv.getSearch(search);
1190                        data.setVfsPreloadData(preloadData);
1191                        data.setIncludeExpiredDefault(search.isIncludeExpired());
1192                        result.setGallerySearch(search);
1193                    }
1194                }
1195            }
1196            result.setGalleryData(data);
1197            return result;
1198
1199        } catch (Exception e) {
1200            error(e);
1201            return null;
1202        }
1203    }
1204
1205    /**
1206     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getNewElementData(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, org.opencms.util.CmsUUID, java.lang.String, java.lang.String, java.util.Collection, java.lang.String)
1207     */
1208    public CmsContainerElementData getNewElementData(
1209        CmsContainerPageRpcContext context,
1210        CmsUUID detailContentId,
1211        String reqParams,
1212        String resourceType,
1213        Collection<CmsContainer> containers,
1214        String localeName)
1215    throws CmsRpcException {
1216
1217        CmsContainerElementData result = null;
1218        try {
1219            ensureSession();
1220            CmsResource pageResource = getCmsObject().readResource(
1221                context.getPageStructureId(),
1222                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1223            initRequestFromRpcContext(context);
1224            String containerpageUri = getCmsObject().getSitePath(pageResource);
1225            Locale locale = CmsLocaleManager.getLocale(localeName);
1226            result = getNewElement(
1227                getServerIdString(resourceType),
1228                containerpageUri,
1229                detailContentId,
1230                containers,
1231                locale);
1232        } catch (Throwable e) {
1233            error(e);
1234        }
1235        return result;
1236    }
1237
1238    /**
1239     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getNewOptions(java.lang.String, org.opencms.util.CmsUUID, java.lang.String)
1240     */
1241    public CmsDialogOptionsAndInfo getNewOptions(String clientId, CmsUUID pageStructureId, String requestParams)
1242    throws CmsRpcException {
1243
1244        try {
1245            CmsResource pageResource = getCmsObject().readResource(
1246                pageStructureId,
1247                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1248            CmsContainerElementBean element = getCachedElement(clientId, pageResource.getRootPath());
1249            element.initResource(getCmsObject());
1250            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
1251            if (type instanceof CmsResourceTypeXmlContent) {
1252                I_CmsEditHandler handler = ((CmsResourceTypeXmlContent)type).getEditHandler(getCmsObject());
1253                Map<String, String[]> params = CmsRequestUtil.createParameterMap(
1254                    CmsEncoder.decode(requestParams),
1255                    true,
1256                    CmsEncoder.ENCODING_UTF_8);
1257                CmsDialogOptions options = handler.getNewOptions(getCmsObject(), element, pageStructureId, params);
1258                if (options != null) {
1259                    return new CmsDialogOptionsAndInfo(
1260                        options,
1261                        CmsVfsService.getPageInfo(getCmsObject(), element.getResource()));
1262                }
1263            }
1264        } catch (CmsException e) {
1265            error(e);
1266        }
1267        return null;
1268
1269    }
1270
1271    /**
1272     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getRecentList(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID, java.util.Collection, java.lang.String)
1273     */
1274    public List<CmsContainerElementData> getRecentList(
1275        CmsUUID pageStructureId,
1276        CmsUUID detailContentId,
1277        Collection<CmsContainer> containers,
1278        String locale)
1279    throws CmsRpcException {
1280
1281        List<CmsContainerElementData> result = null;
1282        try {
1283            ensureSession();
1284            CmsResource containerpage = getCmsObject().readResource(
1285                pageStructureId,
1286                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1287            String containerpageUri = getCmsObject().getSitePath(containerpage);
1288            result = getListElementsData(
1289                OpenCms.getADEManager().getRecentList(getCmsObject()),
1290                containerpageUri,
1291                detailContentId,
1292                containers,
1293                CmsLocaleManager.getLocale(locale));
1294        } catch (Throwable e) {
1295            error(e);
1296        }
1297        return result;
1298    }
1299
1300    /**
1301     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#getRemovedElementStatus(java.lang.String, org.opencms.util.CmsUUID)
1302     */
1303    public CmsRemovedElementStatus getRemovedElementStatus(String id, CmsUUID containerpageId) throws CmsRpcException {
1304
1305        if ((id == null) || !id.matches(CmsUUID.UUID_REGEX + ".*$")) {
1306            return new CmsRemovedElementStatus(null, null, false, null);
1307        }
1308        try {
1309            CmsUUID structureId = convertToServerId(id);
1310            return internalGetRemovedElementStatus(structureId, containerpageId);
1311        } catch (CmsException e) {
1312            error(e);
1313            return null;
1314        }
1315    }
1316
1317    /**
1318     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#handleDelete(java.lang.String, java.lang.String, org.opencms.util.CmsUUID, java.lang.String)
1319     */
1320    public void handleDelete(String clientId, String deleteOption, CmsUUID pageId, String requestParams)
1321    throws CmsRpcException {
1322
1323        try {
1324            CmsResource pageResource = getCmsObject().readResource(
1325                pageId,
1326                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1327            CmsContainerElementBean element = getCachedElement(clientId, pageResource.getRootPath());
1328            element.initResource(getCmsObject());
1329            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
1330            if (type instanceof CmsResourceTypeXmlContent) {
1331                I_CmsEditHandler handler = ((CmsResourceTypeXmlContent)type).getEditHandler(getCmsObject());
1332                Map<String, String[]> params = CmsRequestUtil.createParameterMap(
1333                    CmsEncoder.decode(requestParams),
1334                    true,
1335                    CmsEncoder.ENCODING_UTF_8);
1336                handler.handleDelete(getCmsObject(), element, deleteOption, pageId, params);
1337            }
1338        } catch (CmsException e) {
1339            error(e);
1340        }
1341    }
1342
1343    /**
1344     * Internal helper method to get the status of a removed element.<p>
1345     *
1346     * @param structureId the structure id of the removed element
1347     * @param containerpageId the id of the page to exclude from the relation check, or null if no page should be excluded
1348     *
1349     * @return the status of the removed element
1350     *
1351     * @throws CmsException in case reading the resource fails
1352     */
1353    public CmsRemovedElementStatus internalGetRemovedElementStatus(CmsUUID structureId, CmsUUID containerpageId)
1354    throws CmsException {
1355
1356        CmsObject cms = getCmsObject();
1357        CmsResource elementResource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
1358        boolean hasWritePermissions = cms.hasPermissions(
1359            elementResource,
1360            CmsPermissionSet.ACCESS_WRITE,
1361            false,
1362            CmsResourceFilter.ALL);
1363        boolean isSystemResource = elementResource.getRootPath().startsWith(CmsResource.VFS_FOLDER_SYSTEM + "/");
1364        CmsRelationFilter relationFilter = CmsRelationFilter.relationsToStructureId(structureId);
1365        List<CmsRelation> relationsToElement = cms.readRelations(relationFilter);
1366        Iterator<CmsRelation> iter = relationsToElement.iterator();
1367
1368        // ignore XML_STRONG (i.e. container element) relations from the container page, this must be checked on the client side.
1369        while (iter.hasNext()) {
1370            CmsRelation relation = iter.next();
1371            if ((containerpageId != null)
1372                && containerpageId.equals(relation.getSourceId())
1373                && relation.getType().equals(CmsRelationType.XML_STRONG)) {
1374                iter.remove();
1375            }
1376        }
1377        ElementDeleteMode elementDeleteMode = null;
1378        CmsResource pageResource = cms.readResource(containerpageId, CmsResourceFilter.IGNORE_EXPIRATION);
1379        CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfiguration(cms, pageResource.getRootPath());
1380        CmsResourceTypeConfig typeConfig = adeConfig.getResourceType(
1381            OpenCms.getResourceManager().getResourceType(elementResource).getTypeName());
1382
1383        if (typeConfig != null) {
1384            elementDeleteMode = typeConfig.getElementDeleteMode();
1385        }
1386
1387        boolean hasNoRelations = relationsToElement.isEmpty();
1388        boolean deletionCandidate = hasNoRelations && hasWritePermissions && !isSystemResource;
1389        CmsListInfoBean elementInfo = CmsVfsService.getPageInfo(cms, elementResource);
1390        return new CmsRemovedElementStatus(structureId, elementInfo, deletionCandidate, elementDeleteMode);
1391    }
1392
1393    /**
1394     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#loadClipboardTab()
1395     */
1396    public int loadClipboardTab() {
1397
1398        Integer clipboardTab = (Integer)(getRequest().getSession().getAttribute(ATTR_CLIPBOARD_TAB));
1399        if (clipboardTab == null) {
1400            clipboardTab = Integer.valueOf(0);
1401        }
1402        return clipboardTab.intValue();
1403    }
1404
1405    /**
1406     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#prefetch()
1407     */
1408    public CmsCntPageData prefetch() throws CmsRpcException {
1409
1410        CmsCntPageData data = null;
1411        CmsObject cms = getCmsObject();
1412        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
1413        HttpServletRequest request = getRequest();
1414        try {
1415            CmsTemplateContextInfo info = OpenCms.getTemplateContextManager().getContextInfoBean(cms, request);
1416            CmsResource containerPage = getContainerpage(cms);
1417            Set<String> detailTypes = getDetailTypes(cms, containerPage);
1418            boolean isEditingModelGroup = isEditingModelGroups(cms, containerPage);
1419            boolean isModelPage = isModelPage(cms, containerPage);
1420            if (isModelPage) {
1421                // the model edit confirm dialog should only be shown once per session, disable it after first model editing
1422                getRequest().getSession().setAttribute(
1423                    CmsVfsSitemapService.ATTR_SHOW_MODEL_EDIT_CONFIRM,
1424                    Boolean.FALSE);
1425            }
1426
1427            TemplateBean templateBean = (TemplateBean)getRequest().getAttribute(
1428                CmsTemplateContextManager.ATTR_TEMPLATE_BEAN);
1429            CmsADESessionCache sessionCache = CmsADESessionCache.getCache(getRequest(), cms);
1430            sessionCache.setTemplateBean(containerPage.getRootPath(), templateBean);
1431            long lastModified = containerPage.getDateLastModified();
1432            String editorUri = OpenCms.getWorkplaceManager().getEditorHandler().getEditorUri(
1433                cms,
1434                CmsResourceTypeXmlContent.getStaticTypeName(),
1435                "User agent",
1436                false);
1437            boolean useClassicEditor = (editorUri == null) || !editorUri.contains("acacia");
1438            CmsResource detailResource = CmsDetailPageResourceHandler.getDetailResource(request);
1439            String noEditReason;
1440            String detailContainerPage = null;
1441            CmsQuickLaunchLocationCache locationCache = CmsQuickLaunchLocationCache.getLocationCache(
1442                request.getSession());
1443            CmsUUID detailContainerPageId = null;
1444            if (detailResource != null) {
1445                locationCache.setPageEditorResource(cms.getRequestContext().getSiteRoot(), detailResource);
1446                CmsObject rootCms = OpenCms.initCmsObject(cms);
1447                rootCms.getRequestContext().setSiteRoot("");
1448                String detailResourcePath = detailResource.getRootPath();
1449                String locale = cms.getRequestContext().getLocale().toString();
1450                detailContainerPage = CmsDetailOnlyContainerUtil.getDetailOnlyPageName(
1451                    cms,
1452                    containerPage,
1453                    detailResourcePath,
1454                    locale);
1455
1456                if (rootCms.existsResource(detailContainerPage, CmsResourceFilter.IGNORE_EXPIRATION)) {
1457                    detailContainerPageId = rootCms.readResource(
1458                        detailContainerPage,
1459                        CmsResourceFilter.IGNORE_EXPIRATION).getStructureId();
1460                    noEditReason = getNoEditReason(
1461                        rootCms,
1462                        rootCms.readResource(detailContainerPage, CmsResourceFilter.IGNORE_EXPIRATION));
1463                } else {
1464                    String permissionFolder = CmsResource.getFolderPath(detailContainerPage);
1465                    while (!rootCms.existsResource(permissionFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
1466                        permissionFolder = CmsResource.getParentFolder(permissionFolder);
1467                    }
1468                    noEditReason = getNoEditReason(
1469                        rootCms,
1470                        rootCms.readResource(permissionFolder, CmsResourceFilter.IGNORE_EXPIRATION));
1471                }
1472            } else {
1473                if (!isModelPage && !isEditingModelGroup) {
1474                    locationCache.setPageEditorResource(cms.getRequestContext().getSiteRoot(), containerPage);
1475                }
1476                noEditReason = getNoEditReason(cms, containerPage);
1477            }
1478
1479            String sitemapPath = "";
1480            boolean sitemapManager = OpenCms.getRoleManager().hasRole(cms, CmsRole.EDITOR);
1481            if (sitemapManager) {
1482                sitemapPath = CmsADEManager.PATH_SITEMAP_EDITOR_JSP;
1483            }
1484            CmsCntPageData.ElementReuseMode reuseMode = ElementReuseMode.reuse;
1485            String reuseModeString = getWorkplaceSettings().getUserSettings().getAdditionalPreference(
1486                "elementReuseMode",
1487                true);
1488
1489            try {
1490                reuseMode = ElementReuseMode.valueOf(reuseModeString);
1491            } catch (Exception e) {
1492                LOG.info("Invalid reuse mode : " + reuseModeString, e);
1493            }
1494            InitialElementViewProvider viewHelper = new InitialElementViewProvider();
1495            viewHelper.init(getSessionCache().getElementView(), containerPage);
1496            CmsLocaleGroup group = cms.getLocaleGroupService().readLocaleGroup(containerPage);
1497            Locale mainLocale = null;
1498
1499            if (group.isRealGroup() && !cms.getRequestContext().getLocale().equals(group.getMainLocale())) {
1500                mainLocale = group.getMainLocale();
1501            }
1502            CmsSiteManagerImpl siteManager = OpenCms.getSiteManager();
1503            String ownRoot = siteManager.getSiteRoot(containerPage.getRootPath());
1504            Map<String, CmsLocaleLinkBean> localeLinkBeans = null;
1505            if (group.isRealGroup()) {
1506                localeLinkBeans = Maps.newHashMap();
1507                Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
1508                for (Map.Entry<Locale, CmsResource> entry : group.getResourcesByLocale().entrySet()) {
1509                    String otherRoot = siteManager.getSiteRoot(entry.getValue().getRootPath());
1510                    if ((otherRoot != null) && otherRoot.equals(ownRoot)) {
1511                        String theLink = OpenCms.getLinkManager().substituteLinkForUnknownTarget(
1512                            cms,
1513                            cms.getRequestContext().removeSiteRoot(entry.getValue().getRootPath()));
1514                        localeLinkBeans.put(entry.getKey().getDisplayLanguage(locale), CmsLocaleLinkBean.link(theLink));
1515                    } else {
1516                        localeLinkBeans.put(
1517                            entry.getKey().getDisplayLanguage(locale),
1518                            CmsLocaleLinkBean.error(
1519                                Messages.get().getBundle(locale).key(Messages.GUI_SHOWLOCALE_WRONG_SITE_0)));
1520                    }
1521                }
1522            }
1523
1524            String onlineLink = null;
1525            CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(cms.getRequestContext().getSiteRoot());
1526            if ((site != null) && !OpenCms.getSiteManager().getWorkplaceServer().equals(site.getUrl())) {
1527                if (detailResource != null) {
1528                    onlineLink = OpenCms.getLinkManager().getOnlineLink(
1529                        cms,
1530                        cms.getSitePath(containerPage),
1531                        cms.getSitePath(detailResource),
1532                        false);
1533                } else {
1534                    onlineLink = OpenCms.getLinkManager().getOnlineLink(cms, cms.getSitePath(containerPage));
1535                }
1536            }
1537
1538            String modelGroupElementId = null;
1539            if (isEditingModelGroup) {
1540                CmsProperty modelElementProp = cms.readPropertyObject(
1541                    containerPage,
1542                    CmsPropertyDefinition.PROPERTY_TEMPLATE_ELEMENTS,
1543                    false);
1544                if (!modelElementProp.isNullProperty() && CmsUUID.isValidUUID(modelElementProp.getValue())) {
1545                    modelGroupElementId = modelElementProp.getValue();
1546                }
1547            }
1548            String title = null;
1549            if (isModelPage || isEditingModelGroup) {
1550                title = Messages.get().getBundle(wpLocale).key(Messages.GUI_TITLE_MODEL_0);
1551
1552            }
1553            ElementDeleteMode deleteMode = OpenCms.getWorkplaceManager().getElementDeleteMode();
1554            if (deleteMode == null) {
1555                deleteMode = ElementDeleteMode.askDelete;
1556            }
1557            CmsListInfoBean pageInfo = CmsVfsService.getPageInfo(cms, containerPage);
1558            data = new CmsCntPageData(
1559                onlineLink,
1560                noEditReason,
1561                CmsRequestUtil.encodeParams(request),
1562                sitemapPath,
1563                sitemapManager,
1564                detailResource != null ? detailResource.getStructureId() : null,
1565                detailContainerPage,
1566                detailContainerPageId,
1567                detailTypes,
1568                lastModified,
1569                getLockInfo(containerPage),
1570                pageInfo,
1571                cms.getRequestContext().getLocale().toString(),
1572                useClassicEditor,
1573                info,
1574                isEditSmallElements(request, cms),
1575                Lists.newArrayList(viewHelper.getViewMap().values()),
1576                viewHelper.getDefaultView(),
1577                reuseMode,
1578                deleteMode,
1579                isModelPage,
1580                isEditingModelGroup,
1581                modelGroupElementId,
1582                mainLocale != null ? mainLocale.toString() : null,
1583                localeLinkBeans,
1584                title,
1585                System.currentTimeMillis());
1586            boolean allowSettingsInEditor = true;
1587            CmsModule baseModule = OpenCms.getModuleManager().getModule("org.opencms.base");
1588            if (baseModule != null) {
1589                String param = baseModule.getParameter("allowSettingsInEditor");
1590                allowSettingsInEditor = CmsStringUtil.isEmptyOrWhitespaceOnly(param)
1591                    || Boolean.valueOf(param).booleanValue();
1592            }
1593            data.setAllowSettingsInEditor(allowSettingsInEditor);
1594        } catch (Throwable e) {
1595            error(e);
1596        }
1597        return data;
1598    }
1599
1600    /**
1601     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#prepareForEdit(java.lang.String, java.lang.String, org.opencms.util.CmsUUID, java.lang.String)
1602     */
1603    public CmsUUID prepareForEdit(String clientId, String editOption, CmsUUID pageId, String requestParams)
1604    throws CmsRpcException {
1605
1606        try {
1607            CmsResource pageResource = getCmsObject().readResource(
1608                pageId,
1609                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1610            CmsContainerElementBean element = getCachedElement(clientId, pageResource.getRootPath());
1611            element.initResource(getCmsObject());
1612            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
1613            if (type instanceof CmsResourceTypeXmlContent) {
1614                I_CmsEditHandler handler = ((CmsResourceTypeXmlContent)type).getEditHandler(getCmsObject());
1615                Map<String, String[]> params = CmsRequestUtil.createParameterMap(
1616                    CmsEncoder.decode(requestParams),
1617                    true,
1618                    CmsEncoder.ENCODING_UTF_8);
1619                return handler.prepareForEdit(getCmsObject(), element, editOption, pageId, params);
1620            }
1621        } catch (CmsException e) {
1622            error(e);
1623        }
1624        return null;
1625    }
1626
1627    /**
1628     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#replaceElement(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, org.opencms.util.CmsUUID, java.lang.String, java.lang.String, java.lang.String, java.util.Collection, java.lang.String)
1629     */
1630    public CmsContainerElementData replaceElement(
1631        CmsContainerPageRpcContext context,
1632        CmsUUID detailContentId,
1633        String reqParams,
1634        String clientId,
1635        String replaceId,
1636        Collection<CmsContainer> containers,
1637        String locale)
1638    throws CmsRpcException {
1639
1640        CmsContainerElementData element = null;
1641        try {
1642            ensureSession();
1643            CmsObject cms = getCmsObject();
1644            CmsResource pageResource = cms.readResource(
1645                context.getPageStructureId(),
1646                CmsResourceFilter.ignoreExpirationOffline(cms));
1647            initRequestFromRpcContext(context);
1648            String containerpageUri = cms.getSitePath(pageResource);
1649            Locale contentLocale = CmsLocaleManager.getLocale(locale);
1650            CmsElementUtil elemUtil = new CmsElementUtil(
1651                cms,
1652                containerpageUri,
1653                generateContainerPageForContainers(containers, pageResource.getRootPath()),
1654                detailContentId,
1655                getRequest(),
1656                getResponse(),
1657                false,
1658                contentLocale);
1659
1660            CmsContainerElementBean elementBean = getCachedElement(clientId, pageResource.getRootPath());
1661            Map<String, String> settings = new HashMap<String, String>(elementBean.getIndividualSettings());
1662            settings.remove(CmsContainerElement.ELEMENT_INSTANCE_ID);
1663            CmsContainerElementBean replaceBean = new CmsContainerElementBean(
1664                new CmsUUID(replaceId),
1665                elementBean.getFormatterId(),
1666                settings,
1667                elementBean.isCreateNew());
1668            getSessionCache().setCacheContainerElement(replaceBean.editorHash(), replaceBean);
1669            element = elemUtil.getElementData(pageResource, replaceBean, containers);
1670        } catch (Throwable e) {
1671            error(e);
1672        }
1673        return element;
1674    }
1675
1676    /**
1677     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveClipboardTab(int)
1678     */
1679    public void saveClipboardTab(int tabIndex) {
1680
1681        getRequest().getSession().setAttribute(ATTR_CLIPBOARD_TAB, new Integer(tabIndex));
1682    }
1683
1684    /**
1685     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveContainerpage(org.opencms.util.CmsUUID, java.util.List)
1686     */
1687    public long saveContainerpage(CmsUUID pageStructureId, List<CmsContainer> containers) throws CmsRpcException {
1688
1689        CmsObject cms = getCmsObject();
1690        try {
1691            ensureSession();
1692            CmsResource containerpage = cms.readResource(
1693                pageStructureId,
1694                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1695            ensureLock(containerpage);
1696            String containerpageUri = cms.getSitePath(containerpage);
1697            saveContainers(cms, containerpage, containerpageUri, containers);
1698        } catch (Throwable e) {
1699            error(e);
1700        }
1701        return System.currentTimeMillis();
1702    }
1703
1704    /**
1705     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveDetailContainers(org.opencms.util.CmsUUID, java.lang.String, java.util.List)
1706     */
1707    public long saveDetailContainers(CmsUUID detailId, String detailContainerResource, List<CmsContainer> containers)
1708    throws CmsRpcException {
1709
1710        CmsObject cms = getCmsObject();
1711        try {
1712            ensureSession();
1713            CmsObject rootCms = OpenCms.initCmsObject(cms);
1714            rootCms.getRequestContext().setSiteRoot("");
1715            CmsResource containerpage;
1716            containerpage = CmsDetailOnlyContainerUtil.readOrCreateDetailOnlyPage(
1717                rootCms,
1718                detailId,
1719                detailContainerResource);
1720            saveContainers(rootCms, containerpage, detailContainerResource, containers);
1721        } catch (Throwable e) {
1722            error(e);
1723        }
1724        return System.currentTimeMillis();
1725    }
1726
1727    /**
1728     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveElementSettings(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, org.opencms.util.CmsUUID, java.lang.String, java.lang.String, java.util.Map, java.util.List, java.lang.String)
1729     */
1730    public CmsContainerElementData saveElementSettings(
1731        CmsContainerPageRpcContext context,
1732        CmsUUID detailContentId,
1733        String reqParams,
1734        String clientId,
1735        Map<String, String> settings,
1736        List<CmsContainer> containers,
1737        String locale)
1738    throws CmsRpcException {
1739
1740        CmsContainerElementData element = null;
1741        try {
1742            ensureSession();
1743            CmsObject cms = getCmsObject();
1744            CmsResource pageResource = cms.readResource(
1745                context.getPageStructureId(),
1746                CmsResourceFilter.ignoreExpirationOffline(cms));
1747            initRequestFromRpcContext(context);
1748            Locale contentLocale = CmsLocaleManager.getLocale(locale);
1749            CmsContainerElementBean elementBean = getCachedElement(clientId, pageResource.getRootPath());
1750            elementBean.initResource(cms);
1751            storeFormatterSelection(elementBean, settings);
1752            // make sure to keep the element instance id
1753            if (!settings.containsKey(CmsContainerElement.ELEMENT_INSTANCE_ID)
1754                && elementBean.getIndividualSettings().containsKey(CmsContainerElement.ELEMENT_INSTANCE_ID)) {
1755                settings.put(
1756                    CmsContainerElement.ELEMENT_INSTANCE_ID,
1757                    elementBean.getIndividualSettings().get(CmsContainerElement.ELEMENT_INSTANCE_ID));
1758            }
1759            if (!isEditingModelGroups(cms, pageResource)) {
1760                // in case of model group state set to 'noGroup', the group will be dissolved and former group id forgotten
1761                if (!(settings.containsKey(CmsContainerElement.MODEL_GROUP_STATE)
1762                    && (ModelGroupState.noGroup == ModelGroupState.evaluate(
1763                        settings.get(CmsContainerElement.MODEL_GROUP_STATE))))) {
1764                    if (elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_ID)) {
1765                        // make sure to keep the model group id
1766                        settings.put(
1767                            CmsContainerElement.MODEL_GROUP_ID,
1768                            elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_ID));
1769                    }
1770                    if (elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_STATE)) {
1771                        settings.put(
1772                            CmsContainerElement.MODEL_GROUP_STATE,
1773                            elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_STATE));
1774                    }
1775                }
1776            }
1777            elementBean = CmsContainerElementBean.cloneWithSettings(
1778                elementBean,
1779                convertSettingValues(elementBean.getResource(), settings, contentLocale));
1780            getSessionCache().setCacheContainerElement(elementBean.editorHash(), elementBean);
1781
1782            // update client id within container data
1783            for (CmsContainer container : containers) {
1784                for (CmsContainerElement child : container.getElements()) {
1785                    if (child.getClientId().equals(clientId)) {
1786                        child.setClientId(elementBean.editorHash());
1787                    }
1788                }
1789            }
1790            if (detailContentId == null) {
1791                saveContainers(cms, pageResource, cms.getSitePath(pageResource), containers);
1792            } else {
1793                List<CmsContainer> detailContainers = new ArrayList<CmsContainer>();
1794                for (CmsContainer container : containers) {
1795                    if (container.isDetailOnly()) {
1796                        detailContainers.add(container);
1797                    }
1798                }
1799                CmsObject rootCms = OpenCms.initCmsObject(cms);
1800                rootCms.getRequestContext().setSiteRoot("");
1801                CmsResource detailResource = rootCms.readResource(detailContentId, CmsResourceFilter.IGNORE_EXPIRATION);
1802                String detailRootPath = detailResource.getRootPath();
1803                CmsResource detailContainerPage = rootCms.readResource(
1804                    CmsDetailOnlyContainerUtil.getDetailOnlyPageName(cms, pageResource, detailRootPath, locale));
1805
1806                ensureLock(detailContainerPage);
1807                saveContainers(rootCms, detailContainerPage, detailContainerPage.getRootPath(), detailContainers);
1808            }
1809            String containerpageUri = cms.getSitePath(pageResource);
1810            CmsElementUtil elemUtil = new CmsElementUtil(
1811                cms,
1812                containerpageUri,
1813                generateContainerPageForContainers(containers, pageResource.getRootPath()),
1814                detailContentId,
1815                getRequest(),
1816                getResponse(),
1817                false,
1818                contentLocale);
1819            element = elemUtil.getElementData(pageResource, elementBean, containers);
1820        } catch (Throwable e) {
1821            error(e);
1822        }
1823        return element;
1824    }
1825
1826    /**
1827     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveFavoriteList(java.util.List, java.lang.String)
1828     */
1829    public void saveFavoriteList(List<String> clientIds, String uri) throws CmsRpcException {
1830
1831        try {
1832            ensureSession();
1833            OpenCms.getADEManager().saveFavoriteList(
1834                getCmsObject(),
1835                getCachedElements(clientIds, getCmsObject().getRequestContext().addSiteRoot(uri)));
1836        } catch (Throwable e) {
1837            error(e);
1838        }
1839    }
1840
1841    /**
1842     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveGroupContainer(org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext, org.opencms.util.CmsUUID, java.lang.String, org.opencms.ade.containerpage.shared.CmsGroupContainer, java.util.Collection, java.lang.String)
1843     */
1844    public CmsGroupContainerSaveResult saveGroupContainer(
1845        CmsContainerPageRpcContext context,
1846
1847        CmsUUID detailContentId,
1848        String reqParams,
1849        CmsGroupContainer groupContainer,
1850        Collection<CmsContainer> containers,
1851        String locale)
1852    throws CmsRpcException {
1853
1854        CmsObject cms = getCmsObject();
1855        List<CmsRemovedElementStatus> removedElements = null;
1856        try {
1857            CmsPair<CmsContainerElement, List<CmsRemovedElementStatus>> saveResult = internalSaveGroupContainer(
1858                cms,
1859                context.getPageStructureId(),
1860                groupContainer);
1861            removedElements = saveResult.getSecond();
1862        } catch (Throwable e) {
1863            error(e);
1864        }
1865        Collection<String> ids = new ArrayList<String>();
1866        ids.add(groupContainer.getClientId());
1867        // update offline indices
1868        OpenCms.getSearchManager().updateOfflineIndexes();
1869        return new CmsGroupContainerSaveResult(
1870            getElementsData(context, detailContentId, reqParams, ids, containers, false, null, locale),
1871            removedElements);
1872    }
1873
1874    /**
1875     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveInheritanceContainer(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID, org.opencms.ade.containerpage.shared.CmsInheritanceContainer, java.util.Collection, java.lang.String)
1876     */
1877    public Map<String, CmsContainerElementData> saveInheritanceContainer(
1878        CmsUUID pageStructureId,
1879        CmsUUID detailContentId,
1880        CmsInheritanceContainer inheritanceContainer,
1881        Collection<CmsContainer> containers,
1882        String locale)
1883    throws CmsRpcException {
1884
1885        try {
1886            CmsObject cms = getCmsObject();
1887            CmsADEConfigData rpcConfig = OpenCms.getADEManager().lookupConfiguration(
1888                cms,
1889                cms.getRequestContext().getRootUri());
1890            CmsResource containerPage = cms.readResource(
1891                pageStructureId,
1892                CmsResourceFilter.ignoreExpirationOffline(getCmsObject()));
1893            String sitePath = cms.getSitePath(containerPage);
1894            Locale requestedLocale = CmsLocaleManager.getLocale(locale);
1895            CmsResource referenceResource = null;
1896            if (inheritanceContainer.isNew()) {
1897                CmsADEConfigData config = getConfigData(containerPage.getRootPath());
1898                CmsResourceTypeConfig typeConfig = config.getResourceType(
1899                    CmsResourceTypeXmlContainerPage.INHERIT_CONTAINER_TYPE_NAME);
1900                referenceResource = typeConfig.createNewElement(cms, containerPage.getRootPath());
1901                inheritanceContainer.setClientId(referenceResource.getStructureId().toString());
1902            }
1903            if (referenceResource == null) {
1904                CmsUUID id = convertToServerId(inheritanceContainer.getClientId());
1905                referenceResource = cms.readResource(id, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1906            }
1907            ensureLock(referenceResource);
1908            saveInheritanceGroup(referenceResource, inheritanceContainer);
1909            tryUnlock(referenceResource);
1910            List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>();
1911            for (CmsContainerElement clientElement : inheritanceContainer.getElements()) {
1912                CmsContainerElementBean elementBean = getCachedElement(
1913                    clientElement.getClientId(),
1914                    containerPage.getRootPath());
1915                elementBean = CmsContainerElementBean.cloneWithSettings(
1916                    elementBean,
1917                    elementBean.getIndividualSettings());
1918                CmsInheritanceInfo inheritanceInfo = clientElement.getInheritanceInfo();
1919                // if a local elements misses the key it was newly added
1920                if (inheritanceInfo.isNew() && CmsStringUtil.isEmptyOrWhitespaceOnly(inheritanceInfo.getKey())) {
1921                    // generating new key
1922                    inheritanceInfo.setKey(CmsResource.getFolderPath(sitePath) + new CmsUUID().toString());
1923                }
1924                elementBean.setInheritanceInfo(inheritanceInfo);
1925                elements.add(elementBean);
1926            }
1927            cms.getRequestContext().setLocale(requestedLocale);
1928            if (inheritanceContainer.getElementsChanged()) {
1929                OpenCms.getADEManager().saveInheritedContainer(
1930                    cms,
1931                    containerPage,
1932                    inheritanceContainer.getName(),
1933                    true,
1934                    elements);
1935            }
1936            return getElements(
1937                rpcConfig,
1938                containerPage,
1939                new ArrayList<String>(Collections.singletonList(inheritanceContainer.getClientId())),
1940                sitePath,
1941                detailContentId,
1942                containers,
1943                false,
1944                null,
1945                false,
1946                requestedLocale);
1947        } catch (Exception e) {
1948            error(e);
1949        }
1950        return null;
1951    }
1952
1953    /**
1954     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#saveRecentList(java.util.List, java.lang.String)
1955     */
1956    public void saveRecentList(List<String> clientIds, String uri) throws CmsRpcException {
1957
1958        try {
1959            ensureSession();
1960            OpenCms.getADEManager().saveRecentList(
1961                getCmsObject(),
1962                getCachedElements(clientIds, getCmsObject().getRequestContext().addSiteRoot(uri)));
1963        } catch (Throwable e) {
1964            error(e);
1965        }
1966    }
1967
1968    /**
1969     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#setEditSmallElements(boolean)
1970     */
1971    public void setEditSmallElements(boolean editSmallElements) throws CmsRpcException {
1972
1973        try {
1974            CmsObject cms = getCmsObject();
1975            CmsUser user = cms.getRequestContext().getCurrentUser();
1976            user.getAdditionalInfo().put(ADDINFO_EDIT_SMALL_ELEMENTS, "" + editSmallElements);
1977            cms.writeUser(user);
1978        } catch (Throwable t) {
1979            error(t);
1980        }
1981    }
1982
1983    /**
1984     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#setElementView(org.opencms.util.CmsUUID)
1985     */
1986    public void setElementView(CmsUUID elementView) {
1987
1988        getSessionCache().setElementView(elementView);
1989    }
1990
1991    /**
1992     * @see org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService#setLastPage(org.opencms.util.CmsUUID, org.opencms.util.CmsUUID)
1993     */
1994    public void setLastPage(CmsUUID pageId, CmsUUID detailId) throws CmsRpcException {
1995
1996        try {
1997            HttpServletRequest req = getRequest();
1998            CmsObject cms = getCmsObject();
1999            CmsADESessionCache cache = CmsADESessionCache.getCache(req, cms);
2000            cache.setLastPage(cms, pageId, detailId);
2001        } catch (Exception e) {
2002            error(e);
2003        }
2004
2005    }
2006
2007    /**
2008     * Sets the session cache.<p>
2009     *
2010     * @param cache the session cache
2011     */
2012    public void setSessionCache(CmsADESessionCache cache) {
2013
2014        m_sessionCache = cache;
2015    }
2016
2017    /**
2018     * Gets the settings which should be updated for an element in the DND case.<p>
2019     *
2020     * @param config the sitemap configuration
2021     * @param originalSettings the original settings
2022     * @param formatterConfig the formatter configuration for the element
2023     * @param containers the containers
2024     * @param dndContainer the id of the DND origin container
2025     *
2026     * @return the map of settings to update
2027     */
2028    Map<String, String> getSettingsToChangeForDnd(
2029        CmsADEConfigData config,
2030        Map<String, String> originalSettings,
2031        CmsFormatterConfiguration formatterConfig,
2032        Collection<CmsContainer> containers,
2033        String dndContainer) {
2034
2035        Map<String, String> result = Maps.newHashMap();
2036        if (dndContainer == null) {
2037            return result;
2038        }
2039        String key = CmsFormatterConfig.getSettingsKeyForContainer(dndContainer);
2040        String formatterId = originalSettings.get(key);
2041        if (formatterId == null) {
2042            return result;
2043        }
2044        for (CmsContainer container : containers) {
2045            if (container.getName().equals(dndContainer)) {
2046                continue;
2047            }
2048
2049            Map<String, I_CmsFormatterBean> formatterSelection = formatterConfig.getFormatterSelection(
2050                container.getType(),
2051                container.getWidth());
2052            I_CmsFormatterBean currentBean = config.findFormatter(formatterId);
2053            if (currentBean != null) {
2054                if (CmsFormatterConfiguration.matchFormatter(currentBean, container.getType(), container.getWidth())) {
2055                    String newKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName());
2056                    result.put(newKey, currentBean.getKeyOrId());
2057                }
2058            } else if (formatterSelection.containsKey(formatterId)) {
2059                // for backwards compatibility with schema-configured formatters
2060                String newKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName());
2061                result.put(newKey, formatterId);
2062            }
2063        }
2064        return result;
2065    }
2066
2067    /**
2068     * Creates a new container element bean from an existing one, but changes some of the individual settings in the copy.<p>
2069     *
2070     * @param element the original container element
2071     * @param settingsToOverride the map of settings to change
2072     *
2073     * @return the new container element bean with the changed settings
2074     */
2075    CmsContainerElementBean overrideSettings(CmsContainerElementBean element, Map<String, String> settingsToOverride) {
2076
2077        Map<String, String> settings = Maps.newHashMap(element.getIndividualSettings());
2078        settings.putAll(settingsToOverride);
2079        CmsContainerElementBean result = new CmsContainerElementBean(
2080            element.getId(),
2081            element.getFormatterId(),
2082            settings,
2083            element.isCreateNew());
2084        return result;
2085    }
2086
2087    /**
2088     * Adds the formatter to the recently used formatter list.<p>
2089     *
2090     * @param elementBean the element bean
2091     * @param settings the changed settings
2092     */
2093    void storeFormatterSelection(CmsContainerElementBean elementBean, Map<String, String> settings) {
2094
2095        Entry<String, String> previousFormatterEntry = null;
2096        for (Entry<String, String> entry : elementBean.getIndividualSettings().entrySet()) {
2097            if (entry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) {
2098                previousFormatterEntry = entry;
2099                break;
2100            }
2101        }
2102        Entry<String, String> formatterEntry = null;
2103        for (Entry<String, String> entry : settings.entrySet()) {
2104            if (entry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) {
2105                formatterEntry = entry;
2106                break;
2107            }
2108        }
2109        if ((formatterEntry != null)
2110            && ((previousFormatterEntry == null)
2111                || !formatterEntry.getKey().equals(previousFormatterEntry.getKey())
2112                || !formatterEntry.getValue().equals(previousFormatterEntry.getValue()))) {
2113            String idString = formatterEntry.getValue();
2114            if (CmsUUID.isValidUUID(idString)) { // TODO: Make this work for schema formatters
2115                // the formatter setting has changed
2116                I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(elementBean.getResource());
2117                getSessionCache().addRecentFormatter(resType.getTypeName(), new CmsUUID(idString));
2118            }
2119        }
2120    }
2121
2122    /**
2123     * Converts the given setting values according to the setting configuration of the given resource.<p>
2124     *
2125     * @param resource the resource
2126     * @param settings the settings to convert
2127     * @param locale the locale used for accessing the element settings
2128     *
2129     * @return the converted settings
2130     * @throws CmsException if something goes wrong
2131     */
2132    private Map<String, String> convertSettingValues(CmsResource resource, Map<String, String> settings, Locale locale)
2133    throws CmsException {
2134
2135        CmsObject cms = getCmsObject();
2136        Locale origLocale = cms.getRequestContext().getLocale();
2137        try {
2138            cms.getRequestContext().setLocale(locale);
2139            Map<String, CmsXmlContentProperty> settingsConf = OpenCms.getADEManager().getElementSettings(cms, resource);
2140            Map<String, String> changedSettings = new HashMap<String, String>();
2141            if (settings != null) {
2142                for (Map.Entry<String, String> entry : settings.entrySet()) {
2143                    String settingName = entry.getKey();
2144                    String settingType = "string";
2145                    if (settingsConf.get(settingName) != null) {
2146                        settingType = settingsConf.get(settingName).getType();
2147                    }
2148                    changedSettings.put(
2149                        settingName,
2150                        CmsXmlContentPropertyHelper.getPropValueIds(getCmsObject(), settingType, entry.getValue()));
2151                }
2152            }
2153            return changedSettings;
2154        } finally {
2155            cms.getRequestContext().setLocale(origLocale);
2156        }
2157    }
2158
2159    /**
2160     * Generates the XML container page bean for the given containers.<p>
2161     *
2162     * @param containers the containers
2163     * @param containerpageRootPath the container page root path
2164     *
2165     * @return the container page bean
2166     * @throws CmsException in case generating the page data fails
2167     */
2168    private CmsContainerPageBean generateContainerPageForContainers(
2169        Collection<CmsContainer> containers,
2170        String containerpageRootPath)
2171    throws CmsException {
2172
2173        List<CmsContainerBean> containerBeans = new ArrayList<CmsContainerBean>();
2174        for (CmsContainer container : containers) {
2175            CmsContainerBean containerBean = getContainerBeanToSave(container, containerpageRootPath);
2176            containerBeans.add(containerBean);
2177        }
2178        CmsContainerPageBean page = new CmsContainerPageBean(containerBeans);
2179        return page;
2180    }
2181
2182    /**
2183     * Returns a list of container elements from a list with client id's.<p>
2184     *
2185     * @param clientIds list of client id's
2186     * @param pageRootPath the container page root path
2187     *
2188     * @return a list of element beans
2189     * @throws CmsException in case reading the element resource fails
2190     */
2191    private List<CmsContainerElementBean> getCachedElements(List<String> clientIds, String pageRootPath)
2192    throws CmsException {
2193
2194        List<CmsContainerElementBean> result = new ArrayList<CmsContainerElementBean>();
2195        for (String id : clientIds) {
2196            try {
2197                result.add(getCachedElement(id, pageRootPath));
2198            } catch (CmsIllegalArgumentException e) {
2199                log(e.getLocalizedMessage(), e);
2200            }
2201        }
2202        return result;
2203    }
2204
2205    /**
2206     * Returns the configuration data of the current container page context.<p>
2207     *
2208     * @param pageRootPath the container page root path
2209     *
2210     * @return the configuration data of the current container page context
2211     */
2212    private CmsADEConfigData getConfigData(String pageRootPath) {
2213
2214        if (m_configData == null) {
2215            m_configData = OpenCms.getADEManager().lookupConfiguration(getCmsObject(), pageRootPath);
2216        }
2217        return m_configData;
2218    }
2219
2220    /**
2221     * Helper method for converting a CmsContainer to a CmsContainerBean when saving a container page.<p>
2222     *
2223     * @param container the container for which the CmsContainerBean should be created
2224     * @param containerpageRootPath the container page root path
2225     *
2226     * @return a container bean
2227     *
2228     * @throws CmsException in case generating the container data fails
2229     */
2230    private CmsContainerBean getContainerBeanToSave(CmsContainer container, String containerpageRootPath)
2231    throws CmsException {
2232
2233        CmsObject cms = getCmsObject();
2234        List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>();
2235        for (CmsContainerElement elementData : container.getElements()) {
2236            if (!elementData.isNew()) {
2237                CmsContainerElementBean newElementBean = getContainerElementBeanToSave(
2238                    cms,
2239                    containerpageRootPath,
2240                    container,
2241                    elementData);
2242                if (newElementBean != null) {
2243                    elements.add(newElementBean);
2244                }
2245            }
2246        }
2247        CmsContainerBean result = CmsElementUtil.clientToServerContainer(container, elements);
2248        return result;
2249    }
2250
2251    /**
2252     * Converts container page element data to a bean which can be saved in a container page.<p>
2253     *
2254     * @param cms the current CMS context
2255     * @param containerpageRootPath the container page root path
2256     * @param container the container containing the element
2257     * @param elementData the data for the single element
2258     *
2259     * @return the container element bean
2260     *
2261     * @throws CmsException if something goes wrong
2262     */
2263    private CmsContainerElementBean getContainerElementBeanToSave(
2264        CmsObject cms,
2265        String containerpageRootPath,
2266        CmsContainer container,
2267        CmsContainerElement elementData)
2268    throws CmsException {
2269
2270        String elementClientId = elementData.getClientId();
2271        boolean hasUuidPrefix = (elementClientId != null) && elementClientId.matches(CmsUUID.UUID_REGEX + ".*$");
2272        boolean isCreateNew = elementData.isCreateNew();
2273        if (elementData.isNew() && !hasUuidPrefix) {
2274
2275            // Due to the changed save system without the save button, we need to make sure that new elements
2276            // are only created once. This must happen when the user first edits a new element. But we still
2277            // want to save changes to non-new elements on the page, so we skip new elements while saving.
2278            return null;
2279        }
2280        CmsContainerElementBean element = getCachedElement(elementData.getClientId(), containerpageRootPath);
2281
2282        CmsResource resource;
2283        if (element.getResource() == null) {
2284            element.initResource(cms);
2285            resource = element.getResource();
2286        } else {
2287            // make sure resource is readable, this is necessary for new content elements
2288            if (element.getId().isNullUUID()) {
2289                // in memory only element, can not be read nor saved
2290                return null;
2291            }
2292            resource = cms.readResource(element.getId(), CmsResourceFilter.IGNORE_EXPIRATION);
2293        }
2294
2295        // check if there is a valid formatter
2296        int containerWidth = container.getWidth();
2297        CmsADEConfigData config = getConfigData(containerpageRootPath);
2298        CmsFormatterConfiguration formatters = config.getFormatters(cms, resource);
2299        String containerType = null;
2300        containerType = container.getType();
2301        I_CmsFormatterBean formatter = null;
2302        String formatterConfigId = null;
2303        if ((element.getIndividualSettings() != null)
2304            && (element.getIndividualSettings().get(
2305                CmsFormatterConfig.getSettingsKeyForContainer(container.getName())) != null)) {
2306            formatterConfigId = element.getIndividualSettings().get(
2307                CmsFormatterConfig.getSettingsKeyForContainer(container.getName()));
2308            I_CmsFormatterBean dynamicFmt = config.findFormatter(formatterConfigId);
2309            if (dynamicFmt != null) {
2310                formatter = dynamicFmt;
2311            } else if (formatterConfigId.startsWith(CmsFormatterConfig.SCHEMA_FORMATTER_ID)
2312                && CmsUUID.isValidUUID(formatterConfigId.substring(CmsFormatterConfig.SCHEMA_FORMATTER_ID.length()))) {
2313                    formatter = formatters.getFormatterSelection(containerType, containerWidth).get(formatterConfigId);
2314                }
2315        }
2316        if (formatter == null) {
2317            formatter = CmsElementUtil.getFormatterForContainer(cms, element, container, config, getSessionCache());
2318            if (formatter != null) {
2319                formatterConfigId = formatter.isFromFormatterConfigFile()
2320                ? formatter.getId()
2321                : CmsFormatterConfig.SCHEMA_FORMATTER_ID + formatter.getJspStructureId().toString();
2322            }
2323        }
2324        CmsContainerElementBean newElementBean = null;
2325        if (formatter != null) {
2326            Map<String, String> settings = new HashMap<String, String>(element.getIndividualSettings());
2327            String formatterKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName());
2328            settings.put(formatterKey, formatterConfigId);
2329            // remove not used formatter settings
2330            Iterator<Entry<String, String>> entries = settings.entrySet().iterator();
2331            while (entries.hasNext()) {
2332                Entry<String, String> entry = entries.next();
2333                if (entry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)
2334                    && !entry.getKey().equals(formatterKey)) {
2335                    entries.remove();
2336                }
2337            }
2338
2339            newElementBean = new CmsContainerElementBean(
2340                element.getId(),
2341                formatter.getJspStructureId(),
2342                settings,
2343                isCreateNew);
2344        }
2345        return newElementBean;
2346    }
2347
2348    /**
2349     * Returns the requested container-page resource.<p>
2350     *
2351     * @param cms the current cms object
2352     *
2353     * @return the container-page resource
2354     *
2355     * @throws CmsException if the resource could not be read for any reason
2356     */
2357    private CmsResource getContainerpage(CmsObject cms) throws CmsException {
2358
2359        String currentUri = cms.getRequestContext().getUri();
2360        CmsResource containerPage = cms.readResource(currentUri, CmsResourceFilter.ignoreExpirationOffline(cms));
2361        if (!CmsResourceTypeXmlContainerPage.isContainerPage(containerPage)) {
2362            // container page is used as template
2363            String cntPagePath = cms.readPropertyObject(
2364                containerPage,
2365                CmsPropertyDefinition.PROPERTY_TEMPLATE_ELEMENTS,
2366                true).getValue("");
2367            try {
2368                containerPage = cms.readResource(cntPagePath, CmsResourceFilter.ignoreExpirationOffline(cms));
2369            } catch (CmsException e) {
2370                if (!LOG.isDebugEnabled()) {
2371                    LOG.warn(e.getLocalizedMessage());
2372                }
2373                LOG.debug(e.getLocalizedMessage(), e);
2374            }
2375        }
2376        return containerPage;
2377    }
2378
2379    /**
2380     * Gets the (potentially empty) set of types for which the container page is registered as a detail page.
2381     *
2382     * @param cms the CMS context
2383     * @param containerPage a container page resource
2384     *
2385     * @return the set of names of types for which the container page is a detail page
2386     */
2387    private Set<String> getDetailTypes(CmsObject cms, CmsResource containerPage) {
2388
2389        List<CmsDetailPageInfo> infos = OpenCms.getADEManager().getAllDetailPages(cms);
2390        Set<CmsUUID> ids = new HashSet<>();
2391        ids.add(containerPage.getStructureId());
2392        Set<String> result = new HashSet<>();
2393        if (containerPage.isFile()) {
2394            try {
2395                CmsResource folder = cms.readParentFolder(containerPage.getStructureId());
2396                if (folder != null) {
2397                    ids.add(folder.getStructureId());
2398                }
2399            } catch (CmsException e) {
2400                LOG.error(e.getLocalizedMessage(), e);
2401            }
2402        }
2403        for (CmsDetailPageInfo info : infos) {
2404            if (ids.contains(info.getId())) {
2405                result.add(info.getType());
2406            }
2407        }
2408
2409        return result;
2410
2411    }
2412
2413    /**
2414     * Returns the data of the given elements.<p>
2415     *
2416     * @param config the sitemap configuration
2417     * @param page the current container page
2418     * @param clientIds the list of IDs of the elements to retrieve the data for
2419     * @param uriParam the current URI
2420     * @param detailContentId the detail content structure id
2421     * @param containers the containers for which the element data should be fetched
2422     * @param allwaysCopy <code>true</code> in case reading data for a clipboard element used as a copy group
2423     * @param dndOriginContainer the container from which an element was dragged (null if this method is not called for DND)
2424     * @param isDragMode if the page is in drag mode
2425     * @param locale the locale to use
2426     *
2427     * @return the elements data
2428     *
2429     * @throws CmsException if something really bad happens
2430     */
2431    private Map<String, CmsContainerElementData> getElements(
2432        CmsADEConfigData config,
2433        CmsResource page,
2434        Collection<String> clientIds,
2435        String uriParam,
2436        CmsUUID detailContentId,
2437        Collection<CmsContainer> containers,
2438        boolean allwaysCopy,
2439        String dndOriginContainer,
2440        boolean isDragMode,
2441        Locale locale)
2442    throws CmsException {
2443
2444        CmsObject cms = getCmsObject();
2445        CmsContainerPageBean pageBean = generateContainerPageForContainers(
2446            containers,
2447            cms.getRequestContext().addSiteRoot(uriParam));
2448        Map<String, CmsContainerElementBean> idMapping = new HashMap<String, CmsContainerElementBean>();
2449        for (String elemId : clientIds) {
2450            if ((elemId == null)) {
2451                continue;
2452            }
2453            CmsContainerElementBean element = getCachedElement(elemId, cms.getRequestContext().addSiteRoot(uriParam));
2454            if (element.getInstanceId() == null) {
2455                element = element.clone();
2456                getSessionCache().setCacheContainerElement(element.editorHash(), element);
2457            }
2458            element.initResource(cms);
2459            idMapping.put(elemId, element);
2460        }
2461        List<String> foundGroups = new ArrayList<String>();
2462        if (CmsContainerElement.MENU_CONTAINER_ID.equals(dndOriginContainer)) {
2463            // this indicates the element is added to the page and not being repositioned, check for model group data
2464            CmsModelGroupHelper modelHelper = new CmsModelGroupHelper(
2465                cms,
2466                getConfigData(uriParam),
2467                getSessionCache(),
2468                isEditingModelGroups(cms, page));
2469            pageBean = modelHelper.prepareforModelGroupContent(idMapping, foundGroups, pageBean, allwaysCopy, locale);
2470        }
2471
2472        CmsElementUtil elemUtil = new CmsElementUtil(
2473            cms,
2474            uriParam,
2475            pageBean,
2476            detailContentId,
2477            getRequest(),
2478            getResponse(),
2479            isDragMode,
2480            locale);
2481        Map<String, CmsContainerElementData> result = new HashMap<String, CmsContainerElementData>();
2482        Set<String> ids = new HashSet<String>();
2483        for (Entry<String, CmsContainerElementBean> entry : idMapping.entrySet()) {
2484            CmsContainerElementBean element = entry.getValue();
2485            String dndId = null;
2486            if (ids.contains(element.editorHash())) {
2487                continue;
2488            }
2489            if ((dndOriginContainer != null) && !CmsContainerElement.MENU_CONTAINER_ID.equals(dndOriginContainer)) {
2490                CmsFormatterConfiguration formatterConfig = elemUtil.getFormatterConfiguration(element.getResource());
2491                Map<String, String> dndSettings = getSettingsToChangeForDnd(
2492                    config,
2493                    element.getIndividualSettings(),
2494                    formatterConfig,
2495                    containers,
2496                    dndOriginContainer);
2497                if (!dndSettings.isEmpty()) {
2498                    CmsContainerElementBean dndElementBean = overrideSettings(element, dndSettings);
2499                    getSessionCache().setCacheContainerElement(dndElementBean.editorHash(), dndElementBean);
2500                    dndId = dndElementBean.editorHash();
2501                    Map<String, CmsContainerElementData> dndResults = getElements(
2502                        config,
2503                        page,
2504                        Arrays.asList(dndId),
2505                        uriParam,
2506                        detailContentId,
2507                        containers,
2508                        false,
2509                        null,
2510                        isDragMode,
2511                        locale);
2512                    result.putAll(dndResults);
2513                }
2514            }
2515
2516            CmsContainerElementData elementData = elemUtil.getElementData(page, element, containers);
2517            if (elementData == null) {
2518                continue;
2519            }
2520            // make sure the element with it's current settings is cached
2521            getSessionCache().setCacheContainerElement(element.editorHash(), element);
2522            elementData.setDndId(dndId);
2523            result.put(entry.getKey(), elementData);
2524            if (elementData.isGroupContainer() || elementData.isInheritContainer()) {
2525                // this is a group-container
2526                CmsResource elementRes = cms.readResource(element.getId());
2527                List<CmsContainerElementBean> subElements = elementData.isGroupContainer()
2528                ? getGroupContainerElements(elementRes)
2529                : getInheritedElements(elementRes, locale, uriParam);
2530                // adding all sub-items to the elements data
2531                for (CmsContainerElementBean subElement : subElements) {
2532                    getSessionCache().setCacheContainerElement(subElement.editorHash(), subElement);
2533                    if (!ids.contains(subElement.editorHash())) {
2534                        CmsContainerElementData subItemData = elemUtil.getElementData(page, subElement, containers);
2535                        ids.add(subElement.editorHash());
2536                        result.put(subElement.editorHash(), subItemData);
2537                    }
2538                }
2539            }
2540            ids.add(element.editorHash());
2541        }
2542        for (CmsContainerElementData elementData : result.values()) {
2543            elementData.setGroup(foundGroups.contains(elementData.getClientId()));
2544        }
2545        return result;
2546    }
2547
2548    /**
2549     * Helper method for converting a CmsGroupContainer to a CmsGroupContainerBean when saving a group container.<p>
2550     *
2551     * @param groupContainer the group-container data
2552     * @param containerPage the container page
2553     * @param locale the locale to use
2554     *
2555     * @return the group-container bean
2556     */
2557    private CmsGroupContainerBean getGroupContainerBean(
2558        CmsGroupContainer groupContainer,
2559        CmsResource containerPage,
2560        String locale) {
2561
2562        CmsObject cms = getCmsObject();
2563        List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>();
2564        for (CmsContainerElement elementData : groupContainer.getElements()) {
2565            try {
2566                if (elementData.isNew()) {
2567                    elementData = createNewElement(
2568                        containerPage.getStructureId(),
2569                        elementData.getClientId(),
2570                        elementData.getResourceType(),
2571                        null,
2572                        locale);
2573                }
2574                CmsContainerElementBean element = getCachedElement(
2575                    elementData.getClientId(),
2576                    containerPage.getRootPath());
2577
2578                // make sure resource is readable,
2579                if (cms.existsResource(element.getId(), CmsResourceFilter.IGNORE_EXPIRATION)) {
2580                    elements.add(element);
2581                }
2582
2583            } catch (Exception e) {
2584                log(e.getLocalizedMessage(), e);
2585            }
2586        }
2587        return new CmsGroupContainerBean(
2588            groupContainer.getTitle(),
2589            groupContainer.getDescription(),
2590            elements,
2591            groupContainer.getTypes());
2592    }
2593
2594    /**
2595     * Returns the sub-elements of this group container resource.<p>
2596     *
2597     * @param resource the group container resource
2598     *
2599     * @return the sub-elements
2600     *
2601     * @throws CmsException if something goes wrong reading the resource
2602     */
2603    private List<CmsContainerElementBean> getGroupContainerElements(CmsResource resource) throws CmsException {
2604
2605        CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal(
2606            getCmsObject(),
2607            resource,
2608            getRequest());
2609        CmsGroupContainerBean groupContainer = xmlGroupContainer.getGroupContainer(getCmsObject());
2610        return groupContainer.getElements();
2611    }
2612
2613    /**
2614     * Gets the structure ids of group container elements from an unmarshalled group container for a single locale.<p>
2615     *
2616     * @param groupContainer the group container
2617     * @param locale the locale for which we want the element ids
2618     *
2619     * @return the group container's element ids for the given locale
2620     */
2621    private Set<CmsUUID> getGroupElementIds(CmsXmlGroupContainer groupContainer, Locale locale) {
2622
2623        Set<CmsUUID> idSet = new HashSet<CmsUUID>();
2624        CmsGroupContainerBean groupContainerBean = groupContainer.getGroupContainer(getCmsObject());
2625        if (groupContainerBean != null) {
2626            for (CmsContainerElementBean element : groupContainerBean.getElements()) {
2627                idSet.add(element.getId());
2628            }
2629        }
2630        return idSet;
2631
2632    }
2633
2634    /**
2635     * Returns the sub-elements of this inherit container resource.<p>
2636     *
2637     * @param resource the inherit container resource
2638     * @param locale the requested locale
2639     * @param uriParam the current URI
2640     *
2641     * @return the sub-elements
2642     *
2643     * @throws CmsException if something goes wrong reading the resource
2644     */
2645    private List<CmsContainerElementBean> getInheritedElements(CmsResource resource, Locale locale, String uriParam)
2646    throws CmsException {
2647
2648        CmsObject cms = getCmsObject();
2649        cms.getRequestContext().setLocale(locale);
2650        CmsInheritanceReferenceParser parser = new CmsInheritanceReferenceParser(cms);
2651        parser.parse(resource);
2652        CmsInheritanceReference ref = parser.getReference(locale);
2653        if (ref == null) {
2654            // new inheritance reference, return an empty list
2655            return Collections.emptyList();
2656        }
2657        String name = ref.getName();
2658        CmsADEManager adeManager = OpenCms.getADEManager();
2659        CmsInheritedContainerState result = adeManager.getInheritedContainerState(cms, cms.addSiteRoot(uriParam), name);
2660        return result.getElements(true);
2661    }
2662
2663    /**
2664     * Returns the data of the given elements.<p>
2665     *
2666     * @param listElements the list of element beans to retrieve the data for
2667     * @param containerpageUri the current URI
2668     * @param detailContentId the detail content structure id
2669     * @param containers the containers which exist on the container page
2670     * @param locale the locale to use
2671     *
2672     * @return the elements data
2673     *
2674     * @throws CmsException if something really bad happens
2675     */
2676    private List<CmsContainerElementData> getListElementsData(
2677        List<CmsContainerElementBean> listElements,
2678        String containerpageUri,
2679        CmsUUID detailContentId,
2680        Collection<CmsContainer> containers,
2681        Locale locale)
2682    throws CmsException {
2683
2684        CmsObject cms = getCmsObject();
2685        CmsElementUtil elemUtil = new CmsElementUtil(
2686            cms,
2687            containerpageUri,
2688            generateContainerPageForContainers(containers, cms.getRequestContext().addSiteRoot(containerpageUri)),
2689            detailContentId,
2690            getRequest(),
2691            getResponse(),
2692            true,
2693            locale);
2694        CmsADESessionCache cache = getSessionCache();
2695        List<CmsContainerElementData> result = new ArrayList<CmsContainerElementData>();
2696        for (CmsContainerElementBean element : listElements) {
2697            // checking if resource exists
2698            if (cms.existsResource(element.getId(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireFile())) {
2699                try {
2700                    CmsContainerElementBean clone = element.clone();
2701                    clone.ensureNewInstanceId();
2702                    cache.setCacheContainerElement(clone.editorHash(), clone);
2703                    CmsContainerElementData elementData = elemUtil.getElementData(
2704                        elemUtil.getPage(),
2705                        clone,
2706                        containers);
2707                    result.add(elementData);
2708                } catch (CmsVfsResourceNotFoundException e) {
2709                    // model group id not found, or other resources
2710                    LOG.info(e.getLocalizedMessage(), e);
2711                }
2712            }
2713        }
2714        return result;
2715    }
2716
2717    /**
2718     * Returns the lock information to the given resource.<p>
2719     *
2720     * @param resource the resource
2721     *
2722     * @return lock information, if the page is locked by another user
2723     *
2724     * @throws CmsException if something goes wrong reading the lock owner user
2725     */
2726    private String getLockInfo(CmsResource resource) throws CmsException {
2727
2728        CmsObject cms = getCmsObject();
2729        CmsResourceUtil resourceUtil = new CmsResourceUtil(cms, resource);
2730        CmsLock lock = resourceUtil.getLock();
2731        String lockInfo = null;
2732        if (!lock.isLockableBy(cms.getRequestContext().getCurrentUser())) {
2733            if (lock.getType() == CmsLockType.PUBLISH) {
2734                lockInfo = Messages.get().getBundle(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms)).key(
2735                    Messages.GUI_LOCKED_FOR_PUBLISH_0);
2736            } else {
2737                CmsUser lockOwner = cms.readUser(lock.getUserId());
2738                lockInfo = Messages.get().getBundle(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms)).key(
2739                    Messages.GUI_LOCKED_BY_1,
2740                    lockOwner.getFullName());
2741            }
2742        }
2743        return lockInfo;
2744    }
2745
2746    /**
2747     * Returns the element data for a new element not existing in the VFS yet.<p>
2748     *
2749     * @param resourceTypeName the resource type name
2750     * @param uriParam the request parameters
2751     * @param detailContentId the detail content structure id
2752     * @param containers the containers of the template
2753     * @param locale the current locale
2754     *
2755     * @return the element data
2756     *
2757     * @throws CmsException if something goes wrong
2758     */
2759    private CmsContainerElementData getNewElement(
2760        String resourceTypeName,
2761        String uriParam,
2762        CmsUUID detailContentId,
2763        Collection<CmsContainer> containers,
2764        Locale locale)
2765    throws CmsException {
2766
2767        CmsObject cms = getCmsObject();
2768        CmsElementUtil elemUtil = new CmsElementUtil(
2769            cms,
2770            uriParam,
2771            generateContainerPageForContainers(containers, cms.getRequestContext().addSiteRoot(uriParam)),
2772            detailContentId,
2773            getRequest(),
2774            getResponse(),
2775            true,
2776            locale);
2777        CmsADEConfigData configData = getConfigData(cms.getRequestContext().addSiteRoot(uriParam));
2778        CmsResourceTypeConfig typeConfig = configData.getResourceType(resourceTypeName);
2779        CmsContainerElementBean elementBean = CmsContainerElementBean.createElementForResourceType(
2780            cms,
2781            OpenCms.getResourceManager().getResourceType(resourceTypeName),
2782            "/",
2783            Collections.<String, String> emptyMap(),
2784            typeConfig.isCopyInModels(),
2785            locale);
2786        CmsContainerElementData data = elemUtil.getElementData(elemUtil.getPage(), elementBean, containers);
2787        data.setClientId(elementBean.editorHash());
2788        getSessionCache().setCacheContainerElement(resourceTypeName, elementBean);
2789        getSessionCache().setCacheContainerElement(elementBean.editorHash(), elementBean);
2790        return data;
2791    }
2792
2793    /**
2794     * Returns the no-edit reason for the given resource.<p>
2795     *
2796     * @param cms the current cms object
2797     * @param containerPage the resource
2798     *
2799     * @return the no-edit reason, empty if editing is allowed
2800     *
2801     * @throws CmsException is something goes wrong
2802     */
2803    private String getNoEditReason(CmsObject cms, CmsResource containerPage) throws CmsException {
2804
2805        return new CmsResourceUtil(cms, containerPage).getNoEditReason(
2806            OpenCms.getWorkplaceManager().getWorkplaceLocale(cms),
2807            !cms.getRequestContext().getCurrentProject().isOnlineProject());
2808    }
2809
2810    /**
2811     * Returns the session cache.<p>
2812     *
2813     * @return the session cache
2814     */
2815    private CmsADESessionCache getSessionCache() {
2816
2817        if (m_sessionCache == null) {
2818            m_sessionCache = CmsADESessionCache.getCache(getRequest(), getCmsObject());
2819        }
2820        return m_sessionCache;
2821    }
2822
2823    /**
2824     * Returns the workplace settings of the current user.<p>
2825     *
2826     * @return the workplace settings
2827     */
2828    private CmsWorkplaceSettings getWorkplaceSettings() {
2829
2830        if (m_workplaceSettings == null) {
2831            m_workplaceSettings = CmsWorkplace.getWorkplaceSettings(getCmsObject(), getRequest());
2832        }
2833        return m_workplaceSettings;
2834    }
2835
2836    /**
2837     * Checks if results for the stored gallery data can be restored for the new gallery data.<p>
2838     *
2839     * @param originalGalleryData the original gallery data
2840     * @param data the new gallery data
2841     * @param search the search bean
2842     *
2843     * @return true if the original and new gallery data are compatible, i.e. we can restore the search results
2844     */
2845    private boolean hasCompatibleSearchData(
2846        CmsGalleryDataBean originalGalleryData,
2847        CmsGalleryDataBean data,
2848        CmsGallerySearchBean search) {
2849
2850        Set<String> originalUsableTypes = Sets.newHashSet();
2851        Set<String> usableTypes = Sets.newHashSet();
2852        for (CmsResourceTypeBean type : originalGalleryData.getTypes()) {
2853            if (!type.isDeactivated()) {
2854                originalUsableTypes.add(type.getType());
2855            }
2856        }
2857        for (CmsResourceTypeBean type : data.getTypes()) {
2858            if (!type.isDeactivated()) {
2859                usableTypes.add(type.getType());
2860            }
2861        }
2862        if (!usableTypes.containsAll(originalUsableTypes)) {
2863            return false;
2864        }
2865        return true;
2866    }
2867
2868    /**
2869     * Initializes request attributes using data from the RPC context.<p>
2870     *
2871     * @param context the RPC context
2872     */
2873    private void initRequestFromRpcContext(CmsContainerPageRpcContext context) {
2874
2875        if (context.getTemplateContext() != null) {
2876            getRequest().setAttribute(
2877                CmsTemplateContextManager.ATTR_RPC_CONTEXT_OVERRIDE,
2878                context.getTemplateContext());
2879        }
2880    }
2881
2882    /**
2883     * Internal method for saving a group container.<p>
2884     *
2885     * @param cms the cms context
2886     * @param pageStructureId the container page structure id
2887     * @param groupContainer the group container to save
2888     *
2889     * @return the container element representing the group container
2890     *
2891     * @throws CmsException if something goes wrong
2892     * @throws CmsXmlException if the XML processing goes wrong
2893     */
2894    private CmsPair<CmsContainerElement, List<CmsRemovedElementStatus>> internalSaveGroupContainer(
2895        CmsObject cms,
2896        CmsUUID pageStructureId,
2897        CmsGroupContainer groupContainer)
2898    throws CmsException, CmsXmlException {
2899
2900        ensureSession();
2901        CmsResource pageResource = getCmsObject().readResource(pageStructureId, CmsResourceFilter.IGNORE_EXPIRATION);
2902        CmsResource groupContainerResource = null;
2903        if (groupContainer.isNew()) {
2904            CmsADEConfigData config = getConfigData(pageResource.getRootPath());
2905            CmsResourceTypeConfig typeConfig = config.getResourceType(
2906                CmsResourceTypeXmlContainerPage.GROUP_CONTAINER_TYPE_NAME);
2907            groupContainerResource = typeConfig.createNewElement(getCmsObject(), pageResource.getRootPath());
2908            String resourceName = cms.getSitePath(groupContainerResource);
2909            groupContainer.setSitePath(resourceName);
2910            groupContainer.setClientId(groupContainerResource.getStructureId().toString());
2911        }
2912        if (groupContainerResource == null) {
2913            CmsUUID id = convertToServerId(groupContainer.getClientId());
2914            groupContainerResource = cms.readResource(id, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
2915        }
2916        CmsGroupContainerBean groupContainerBean = getGroupContainerBean(
2917            groupContainer,
2918            pageResource,
2919            Locale.ENGLISH.toString());
2920
2921        cms.lockResourceTemporary(groupContainerResource);
2922        CmsFile groupContainerFile = cms.readFile(groupContainerResource);
2923        Locale locale = Locale.ENGLISH;
2924        CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal(cms, groupContainerFile);
2925        Set<CmsUUID> oldElementIds = getGroupElementIds(xmlGroupContainer, locale);
2926        xmlGroupContainer.clearLocales();
2927        xmlGroupContainer.save(cms, groupContainerBean, locale);
2928        cms.unlockResource(groupContainerResource);
2929        Set<CmsUUID> newElementIds = getGroupElementIds(xmlGroupContainer, locale);
2930        Set<CmsUUID> removedElementIds = Sets.difference(oldElementIds, newElementIds);
2931        List<CmsRemovedElementStatus> deletionCandidateStatuses = new ArrayList<CmsRemovedElementStatus>();
2932        for (CmsUUID removedId : removedElementIds) {
2933            CmsRemovedElementStatus status = internalGetRemovedElementStatus(removedId, null);
2934            if (status.isDeletionCandidate()) {
2935                deletionCandidateStatuses.add(status);
2936            }
2937        }
2938        CmsContainerElement element = new CmsContainerElement();
2939        element.setClientId(groupContainerFile.getStructureId().toString());
2940        element.setSitePath(cms.getSitePath(groupContainerFile));
2941        element.setResourceType(CmsResourceTypeXmlContainerPage.GROUP_CONTAINER_TYPE_NAME);
2942        return CmsPair.create(element, deletionCandidateStatuses);
2943    }
2944
2945    /**
2946     * Checks if small elements in a container page should be initially editable.<p>
2947     *
2948     * @param request the current request
2949     * @param cms the current CMS context
2950     * @return true if small elements should be initially editable
2951     */
2952    private boolean isEditSmallElements(HttpServletRequest request, CmsObject cms) {
2953
2954        CmsUser user = cms.getRequestContext().getCurrentUser();
2955        String editSmallElementsStr = (String)(user.getAdditionalInfo().get(ADDINFO_EDIT_SMALL_ELEMENTS));
2956        if (editSmallElementsStr == null) {
2957            return true;
2958        } else {
2959            return Boolean.valueOf(editSmallElementsStr).booleanValue();
2960        }
2961    }
2962
2963    /**
2964     * Checks if a page is a model page.<p>
2965     *
2966     * @param cms the CMS context to use
2967     * @param containerPage the page to check
2968     *
2969     * @return true if the resource is a model page
2970     */
2971    private boolean isModelPage(CmsObject cms, CmsResource containerPage) {
2972
2973        CmsADEConfigData config = getConfigData(containerPage.getRootPath());
2974        for (CmsModelPageConfig modelConfig : config.getModelPages()) {
2975            if (modelConfig.getResource().getStructureId().equals(containerPage.getStructureId())) {
2976                return true;
2977            }
2978        }
2979        return false;
2980    }
2981
2982    /**
2983     * Saves the given containers to the container page resource.<p>
2984     *
2985     * @param cms the cms context
2986     * @param containerpage the container page resource
2987     * @param containerpageUri the container page site path
2988     * @param containers the container to save
2989     *
2990     * @throws CmsException if something goes wrong writing the file
2991     */
2992    private void saveContainers(
2993        CmsObject cms,
2994        CmsResource containerpage,
2995        String containerpageUri,
2996        List<CmsContainer> containers)
2997    throws CmsException {
2998
2999        CmsContainerPageBean page = generateContainerPageForContainers(containers, containerpage.getRootPath());
3000
3001        CmsModelGroupHelper modelHelper = new CmsModelGroupHelper(
3002            getCmsObject(),
3003            getConfigData(containerpage.getRootPath()),
3004            getSessionCache(),
3005            isEditingModelGroups(cms, containerpage));
3006        if (isEditingModelGroups(cms, containerpage)) {
3007            page = modelHelper.saveModelGroups(page, containerpage);
3008        } else {
3009            page = modelHelper.removeModelGroupContainers(page);
3010        }
3011        CmsXmlContainerPage xmlCnt = CmsXmlContainerPageFactory.unmarshal(
3012            cms,
3013            cms.readFile(containerpageUri, CmsResourceFilter.ignoreExpirationOffline(cms)));
3014        xmlCnt.save(cms, page);
3015    }
3016
3017    /**
3018     * Saves the inheritance group.<p>
3019     *
3020     * @param resource the inheritance group resource
3021     * @param inheritanceContainer the inherited group container data
3022     *
3023     * @throws CmsException if something goes wrong
3024     */
3025    private void saveInheritanceGroup(CmsResource resource, CmsInheritanceContainer inheritanceContainer)
3026    throws CmsException {
3027
3028        CmsObject cms = getCmsObject();
3029        CmsFile file = cms.readFile(resource);
3030        CmsXmlContent document = CmsXmlContentFactory.unmarshal(cms, file);
3031
3032        for (Locale docLocale : document.getLocales()) {
3033            document.removeLocale(docLocale);
3034        }
3035        Locale locale = Locale.ENGLISH;
3036        document.addLocale(cms, locale);
3037        document.getValue("Title", locale).setStringValue(cms, inheritanceContainer.getTitle());
3038        document.getValue("Description", locale).setStringValue(cms, inheritanceContainer.getDescription());
3039        document.getValue("ConfigName", locale).setStringValue(cms, inheritanceContainer.getName());
3040        byte[] content = document.marshal();
3041        file.setContents(content);
3042        cms.writeFile(file);
3043    }
3044
3045    /**
3046     * Update favorite or recent list with the given element.<p>
3047     *
3048     * @param containerPage the edited container page
3049     * @param clientId the elements client id
3050     * @param list the list to update
3051     *
3052     * @return the updated list
3053     *
3054     * @throws CmsException in case reading the element resource fails
3055     */
3056    private List<CmsContainerElementBean> updateFavoriteRecentList(
3057        CmsResource containerPage,
3058        String clientId,
3059        List<CmsContainerElementBean> list)
3060    throws CmsException {
3061
3062        try {
3063            CmsContainerElementBean element = getCachedElement(clientId, containerPage.getRootPath());
3064            Map<String, String> settings = new HashMap<String, String>(element.getIndividualSettings());
3065            String formatterID = null;
3066            Iterator<Entry<String, String>> entries = settings.entrySet().iterator();
3067            while (entries.hasNext()) {
3068                Entry<String, String> entry = entries.next();
3069                if (entry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) {
3070                    formatterID = entry.getValue();
3071                    entries.remove();
3072                }
3073            }
3074            settings.put(CmsFormatterConfig.FORMATTER_SETTINGS_KEY, formatterID);
3075            settings.put(SOURCE_CONTAINERPAGE_ID_SETTING, containerPage.getStructureId().toString());
3076            element = CmsContainerElementBean.cloneWithSettings(element, settings);
3077            Iterator<CmsContainerElementBean> listIt = list.iterator();
3078            while (listIt.hasNext()) {
3079                CmsContainerElementBean listElem = listIt.next();
3080                if (element.getInstanceId().equals(listElem.getInstanceId())) {
3081                    listIt.remove();
3082                }
3083            }
3084            list.add(0, element);
3085            return list;
3086        } catch (CmsVfsResourceNotFoundException e) {
3087            LOG.warn(e.getLocalizedMessage(), e);
3088            return list;
3089        }
3090    }
3091
3092}