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