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.ui.util;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsPropertyDefinition;
033import org.opencms.file.CmsRequestContext;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
037import org.opencms.file.types.I_CmsResourceType;
038import org.opencms.gwt.CmsIconUtil;
039import org.opencms.gwt.CmsPropertyEditorHelper;
040import org.opencms.gwt.CmsTemplateFinder;
041import org.opencms.gwt.shared.CmsListInfoBean;
042import org.opencms.gwt.shared.property.CmsPropertiesBean;
043import org.opencms.gwt.shared.property.CmsPropertyChangeSet;
044import org.opencms.i18n.CmsMessages;
045import org.opencms.main.CmsException;
046import org.opencms.main.CmsLog;
047import org.opencms.main.OpenCms;
048import org.opencms.util.CmsMacroResolver;
049import org.opencms.util.CmsUUID;
050import org.opencms.xml.content.CmsXmlContentProperty;
051import org.opencms.xml.content.CmsXmlContentPropertyHelper;
052
053import java.util.ArrayList;
054import java.util.LinkedHashMap;
055import java.util.List;
056import java.util.Locale;
057import java.util.Map;
058
059import org.apache.commons.logging.Log;
060
061import com.google.common.collect.Lists;
062
063/**
064 * Helper class for creating a new resource using the New dialog.<p>
065 */
066public class CmsNewResourceBuilder {
067
068    /**
069     * Interface for callbacks which should be notified when this helper has created a resource.<p>
070     */
071    public static interface I_Callback {
072
073        /**
074         * Error handler.<p>
075         *
076         * @param e the exception which was thrown
077         */
078        void onError(Exception e);
079
080        /**
081         * This should be called after the resource is fully created and its properties have been set.<p>
082         *
083         * @param builder the resource builder
084         */
085        void onResourceCreated(CmsNewResourceBuilder builder);
086    }
087
088    /**
089     * Property helper subclass which is responsible for loading the initial property data to display in the property
090     * dialog for a resource to be created in the New dialog.<p>
091     */
092    public class PropertyEditorHelper extends CmsPropertyEditorHelper {
093
094        /**
095         * Creates a new instance.<p>
096         *
097         * @param cms the CMS cntext
098         */
099        public PropertyEditorHelper(CmsObject cms) {
100
101            super(cms);
102        }
103
104        /**
105         * Loads the data needed for editing the properties of a resource.<p>
106         *
107         * @param id the structure id of the resource (ignored)
108         *
109         * @return the data needed for editing the properties
110         *
111         * @throws CmsException if something goes wrong
112         */
113        @SuppressWarnings("synthetic-access")
114        @Override
115        public CmsPropertiesBean loadPropertyData(CmsUUID id) throws CmsException {
116
117            CmsObject cms = m_cms;
118            String originalSiteRoot = cms.getRequestContext().getSiteRoot();
119            CmsPropertiesBean result = new CmsPropertiesBean();
120
121            result.setReadOnly(false);
122            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(m_type);
123            List<CmsProperty> typeDefaultProperties = type.getConfiguredDefaultProperties();
124            result.setFolder(type.isFolder());
125            result.setContainerPage(m_type.equals(CmsResourceTypeXmlContainerPage.getStaticTypeName()));
126            String sitePath = OpenCms.getResourceManager().getNameGenerator().getNewFileName(
127                m_cms,
128                m_pathWithPattern,
129                5,
130                m_explorerNameGeneration);
131            String rootPath = m_cms.getRequestContext().addSiteRoot(sitePath);
132            Map<String, CmsXmlContentProperty> propertyConfig;
133            Map<String, CmsXmlContentProperty> defaultProperties = getDefaultPropertiesForType(m_type);
134            Map<String, CmsXmlContentProperty> mergedConfig = OpenCms.getADEManager().lookupConfiguration(
135                cms,
136                rootPath).getPropertyConfiguration(defaultProperties);
137            propertyConfig = mergedConfig;
138
139            // Resolve macros in the property configuration
140            propertyConfig = CmsXmlContentPropertyHelper.resolveMacrosInProperties(
141                propertyConfig,
142                CmsMacroResolver.newWorkplaceLocaleResolver(cms));
143            CmsPropertyEditorHelper.updateWysiwygConfig(propertyConfig, cms, null);
144
145            result.setPropertyDefinitions(new LinkedHashMap<String, CmsXmlContentProperty>(propertyConfig));
146            try {
147                cms.getRequestContext().setSiteRoot("");
148                String parentPath = CmsResource.getParentFolder(rootPath);
149                CmsResource parent = cms.readResource(parentPath, CmsResourceFilter.IGNORE_EXPIRATION);
150                List<CmsProperty> parentProperties = cms.readPropertyObjects(parent, true);
151                List<CmsProperty> ownProperties = typeDefaultProperties;
152                result.setOwnProperties(convertProperties(ownProperties));
153                result.setInheritedProperties(convertProperties(parentProperties));
154                result.setPageInfo(getPageInfo(sitePath));
155                List<CmsPropertyDefinition> propDefs = cms.readAllPropertyDefinitions();
156                List<String> propNames = new ArrayList<String>();
157                for (CmsPropertyDefinition propDef : propDefs) {
158                    propNames.add(propDef.getName());
159                }
160                CmsTemplateFinder templateFinder = new CmsTemplateFinder(cms);
161                result.setTemplates(templateFinder.getTemplates());
162                result.setAllProperties(propNames);
163                result.setStructureId(id);
164                result.setSitePath(sitePath);
165                return result;
166            } finally {
167                cms.getRequestContext().setSiteRoot(originalSiteRoot);
168            }
169        }
170
171        /**
172         * Gets the page info bean.<p>
173         *
174         * @param sitePath the site path
175         * @return the page info bean
176         */
177        private CmsListInfoBean getPageInfo(String sitePath) {
178
179            CmsListInfoBean listInfo = new CmsListInfoBean();
180            listInfo.setResourceState(CmsResource.STATE_NEW);
181            listInfo.setTitle(CmsResource.getName(sitePath));
182            listInfo.setSubTitle(sitePath);
183
184            String key = OpenCms.getWorkplaceManager().getExplorerTypeSetting(m_type).getKey();
185            Locale currentLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
186            CmsMessages messages = OpenCms.getWorkplaceManager().getMessages(currentLocale);
187            String resTypeNiceName = messages.key(key);
188            listInfo.addAdditionalInfo(
189                messages.key(org.opencms.workplace.commons.Messages.GUI_LABEL_TYPE_0),
190                resTypeNiceName);
191            listInfo.setBigIconClasses(CmsIconUtil.getIconClasses(m_type, sitePath, false));
192            listInfo.setResourceType(m_type);
193            return listInfo;
194        }
195    }
196
197    /** The logger instance for this class. */
198    private static final Log LOG = CmsLog.getLog(CmsNewResourceBuilder.class);
199
200    /** The CMS context. */
201    CmsObject m_cms;
202
203    /** The resource type name. */
204    String m_type;
205
206    /** The list of registered callbacks. */
207    private List<I_Callback> m_callbacks = Lists.newArrayList();
208
209    /** The created resource (null until this helper has finished creating the resource). */
210    private CmsResource m_createdResource;
211
212    /** True if explorer name generation is enabled. */
213    private boolean m_explorerNameGeneration;
214
215    /** The model resource. */
216    private CmsResource m_modelResource;
217
218    /** The path with name pattern at which the resource should be created. */
219    private String m_pathWithPattern;
220
221    /** The property changes to save (may be null). */
222    private CmsPropertyChangeSet m_propChanges;
223
224    /**
225     * Creates a new instance.<p>
226     *
227     * @param cms the CMS context
228     * @throws CmsException if something goes wrong
229     */
230    public CmsNewResourceBuilder(CmsObject cms)
231    throws CmsException {
232
233        m_cms = OpenCms.initCmsObject(cms);
234    }
235
236    /**
237     * Adds a callback to be notified when the resource is created.<p>
238     *
239     * @param callback the callback
240     */
241    public void addCallback(I_Callback callback) {
242
243        m_callbacks.add(callback);
244    }
245
246    /**
247     * Triggers the resource creation.<p>
248     *
249     * @return the created resource
250     *
251     * @throws CmsException if something goes wrong
252     */
253    public CmsResource createResource() throws CmsException {
254
255        String path = OpenCms.getResourceManager().getNameGenerator().getNewFileName(
256            m_cms,
257            m_pathWithPattern,
258            5,
259            m_explorerNameGeneration);
260        Locale contentLocale = OpenCms.getLocaleManager().getDefaultLocale(m_cms, CmsResource.getFolderPath(path));
261        CmsRequestContext context = m_cms.getRequestContext();
262        if (m_modelResource != null) {
263            context.setAttribute(CmsRequestContext.ATTRIBUTE_MODEL, m_modelResource.getRootPath());
264        }
265        context.setAttribute(CmsRequestContext.ATTRIBUTE_NEW_RESOURCE_LOCALE, contentLocale);
266        CmsResource res = m_cms.createResource(
267            path,
268            OpenCms.getResourceManager().getResourceType(m_type),
269            null,
270            new ArrayList<CmsProperty>());
271        if (m_propChanges != null) {
272            CmsPropertyEditorHelper helper = new CmsPropertyEditorHelper(m_cms);
273            helper.overrideStructureId(res.getStructureId());
274            helper.saveProperties(m_propChanges);
275        }
276        // Path or other metadata may have changed
277        m_createdResource = m_cms.readResource(res.getStructureId(), CmsResourceFilter.IGNORE_EXPIRATION);
278        try {
279            m_cms.unlockResource(m_createdResource);
280        } catch (CmsException e) {
281            LOG.info(e.getLocalizedMessage(), e);
282        }
283        for (I_Callback callback : m_callbacks) {
284            callback.onResourceCreated(this);
285        }
286        return m_createdResource;
287    }
288
289    /**
290     * Gets the created resource.<p>
291     *
292     * This will null before the resource creation process.
293     *
294     * @return the created resource
295     */
296    public CmsResource getCreatedResource() {
297
298        return m_createdResource;
299    }
300
301    /**
302     * Loads the property data with which the property dialog for the new resource should be initialized.<p>
303     *
304     * @return the properties bean
305     */
306    public CmsPropertiesBean getPropertyData() {
307
308        CmsPropertyEditorHelper helper = new PropertyEditorHelper(m_cms);
309        try {
310            CmsPropertiesBean data = helper.loadPropertyData(CmsUUID.getNullUUID());
311            return data;
312        } catch (Exception e) {
313            throw new RuntimeException(e);
314        }
315    }
316
317    /**
318     * Creates a resource, but doesn't throw any exceptions.<p>
319     *
320     * Exceptions will be passed to the onError method of registered callbacks.<p>
321     *
322     * @return the created resource
323     */
324    public CmsResource safeCreateResource() {
325
326        try {
327            return createResource();
328        } catch (Exception e) {
329            for (I_Callback callback : m_callbacks) {
330                callback.onError(e);
331            }
332            return null;
333        }
334    }
335
336    /**
337     * Sets the Explorer name generation mode.<p>
338     *
339     * @param explorerNameGenerationMode the explorer name generation mode
340     */
341    public void setExplorerNameGeneration(boolean explorerNameGenerationMode) {
342
343        m_explorerNameGeneration = explorerNameGenerationMode;
344    }
345
346    /**
347     * Sets the locale.<p>
348     *
349     * @param locale the locale
350     */
351    public void setLocale(Locale locale) {
352
353        m_cms.getRequestContext().setLocale(locale);
354
355    }
356
357    /**
358     * Sets the model resource.<p>
359     *
360     * @param modelResource the model resource
361     */
362    public void setModel(CmsResource modelResource) {
363
364        m_modelResource = modelResource;
365    }
366
367    /**
368     * Sets the creation path containing a number pattern.<p>
369     *
370     * @param destination the creation path
371     */
372    public void setPatternPath(String destination) {
373
374        m_pathWithPattern = destination;
375    }
376
377    /**
378     * Sets the property changes.<p>
379     *
380     * @param propertyChanges the property changes
381     */
382    public void setPropertyChanges(CmsPropertyChangeSet propertyChanges) {
383
384        m_propChanges = propertyChanges;
385    }
386
387    /**
388     * Sets the site root of the CMS context.<p>
389     *
390     * @param siteRoot the site root
391     */
392    public void setSiteRoot(String siteRoot) {
393
394        m_cms.getRequestContext().setSiteRoot(siteRoot);
395    }
396
397    /**
398     * Sets the resource type name.<p>
399     *
400     * @param type the resource type name
401     */
402    public void setType(String type) {
403
404        m_type = type;
405    }
406}