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
133            Map<String, CmsXmlContentProperty> propertyConfig = OpenCms.getADEManager().lookupConfiguration(
134                cms,
135                rootPath).getPropertyConfigurationAsMap();
136            Map<String, CmsXmlContentProperty> defaultProperties = getDefaultPropertiesForType(m_type);
137            Map<String, CmsXmlContentProperty> mergedConfig = new LinkedHashMap<String, CmsXmlContentProperty>();
138            mergedConfig.putAll(defaultProperties);
139            mergedConfig.putAll(propertyConfig);
140            propertyConfig = mergedConfig;
141
142            // Resolve macros in the property configuration
143            propertyConfig = CmsXmlContentPropertyHelper.resolveMacrosInProperties(
144                propertyConfig,
145                CmsMacroResolver.newWorkplaceLocaleResolver(cms));
146            CmsPropertyEditorHelper.updateWysiwygConfig(propertyConfig, cms, null);
147
148            result.setPropertyDefinitions(new LinkedHashMap<String, CmsXmlContentProperty>(propertyConfig));
149            try {
150                cms.getRequestContext().setSiteRoot("");
151                String parentPath = CmsResource.getParentFolder(rootPath);
152                CmsResource parent = cms.readResource(parentPath, CmsResourceFilter.IGNORE_EXPIRATION);
153                List<CmsProperty> parentProperties = cms.readPropertyObjects(parent, true);
154                List<CmsProperty> ownProperties = typeDefaultProperties;
155                result.setOwnProperties(convertProperties(ownProperties));
156                result.setInheritedProperties(convertProperties(parentProperties));
157                result.setPageInfo(getPageInfo(sitePath));
158                List<CmsPropertyDefinition> propDefs = cms.readAllPropertyDefinitions();
159                List<String> propNames = new ArrayList<String>();
160                for (CmsPropertyDefinition propDef : propDefs) {
161                    propNames.add(propDef.getName());
162                }
163                CmsTemplateFinder templateFinder = new CmsTemplateFinder(cms);
164                result.setTemplates(templateFinder.getTemplates());
165                result.setAllProperties(propNames);
166                result.setStructureId(id);
167                result.setSitePath(sitePath);
168                return result;
169            } finally {
170                cms.getRequestContext().setSiteRoot(originalSiteRoot);
171            }
172        }
173
174        /**
175         * Gets the page info bean.<p>
176         *
177         * @param sitePath the site path
178         * @return the page info bean
179         */
180        private CmsListInfoBean getPageInfo(String sitePath) {
181
182            CmsListInfoBean listInfo = new CmsListInfoBean();
183            listInfo.setResourceState(CmsResource.STATE_NEW);
184            listInfo.setTitle(CmsResource.getName(sitePath));
185            listInfo.setSubTitle(sitePath);
186
187            String key = OpenCms.getWorkplaceManager().getExplorerTypeSetting(m_type).getKey();
188            Locale currentLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
189            CmsMessages messages = OpenCms.getWorkplaceManager().getMessages(currentLocale);
190            String resTypeNiceName = messages.key(key);
191            listInfo.addAdditionalInfo(
192                messages.key(org.opencms.workplace.commons.Messages.GUI_LABEL_TYPE_0),
193                resTypeNiceName);
194            listInfo.setBigIconClasses(CmsIconUtil.getIconClasses(m_type, sitePath, false));
195            listInfo.setResourceType(m_type);
196            return listInfo;
197        }
198    }
199
200    /** The logger instance for this class. */
201    private static final Log LOG = CmsLog.getLog(CmsNewResourceBuilder.class);
202
203    /** The CMS context. */
204    CmsObject m_cms;
205
206    /** The resource type name. */
207    String m_type;
208
209    /** The list of registered callbacks. */
210    private List<I_Callback> m_callbacks = Lists.newArrayList();
211
212    /** The created resource (null until this helper has finished creating the resource). */
213    private CmsResource m_createdResource;
214
215    /** True if explorer name generation is enabled. */
216    private boolean m_explorerNameGeneration;
217
218    /** The model resource. */
219    private CmsResource m_modelResource;
220
221    /** The path with name pattern at which the resource should be created. */
222    private String m_pathWithPattern;
223
224    /** The property changes to save (may be null). */
225    private CmsPropertyChangeSet m_propChanges;
226
227    /**
228     * Creates a new instance.<p>
229     *
230     * @param cms the CMS context
231     * @throws CmsException if something goes wrong
232     */
233    public CmsNewResourceBuilder(CmsObject cms)
234    throws CmsException {
235
236        m_cms = OpenCms.initCmsObject(cms);
237    }
238
239    /**
240     * Adds a callback to be notified when the resource is created.<p>
241     *
242     * @param callback the callback
243     */
244    public void addCallback(I_Callback callback) {
245
246        m_callbacks.add(callback);
247    }
248
249    /**
250     * Triggers the resource creation.<p>
251     *
252     * @return the created resource
253     *
254     * @throws CmsException if something goes wrong
255     */
256    public CmsResource createResource() throws CmsException {
257
258        String path = OpenCms.getResourceManager().getNameGenerator().getNewFileName(
259            m_cms,
260            m_pathWithPattern,
261            5,
262            m_explorerNameGeneration);
263        Locale contentLocale = OpenCms.getLocaleManager().getDefaultLocale(m_cms, CmsResource.getFolderPath(path));
264        CmsRequestContext context = m_cms.getRequestContext();
265        if (m_modelResource != null) {
266            context.setAttribute(CmsRequestContext.ATTRIBUTE_MODEL, m_modelResource.getRootPath());
267        }
268        context.setAttribute(CmsRequestContext.ATTRIBUTE_NEW_RESOURCE_LOCALE, contentLocale);
269        CmsResource res = m_cms.createResource(
270            path,
271            OpenCms.getResourceManager().getResourceType(m_type),
272            null,
273            new ArrayList<CmsProperty>());
274        if (m_propChanges != null) {
275            CmsPropertyEditorHelper helper = new CmsPropertyEditorHelper(m_cms);
276            helper.overrideStructureId(res.getStructureId());
277            helper.saveProperties(m_propChanges);
278        }
279        // Path or other metadata may have changed
280        m_createdResource = m_cms.readResource(res.getStructureId(), CmsResourceFilter.IGNORE_EXPIRATION);
281        try {
282            m_cms.unlockResource(m_createdResource);
283        } catch (CmsException e) {
284            LOG.info(e.getLocalizedMessage(), e);
285        }
286        for (I_Callback callback : m_callbacks) {
287            callback.onResourceCreated(this);
288        }
289        return m_createdResource;
290    }
291
292    /**
293     * Gets the created resource.<p>
294     *
295     * This will null before the resource creation process.
296     *
297     * @return the created resource
298     */
299    public CmsResource getCreatedResource() {
300
301        return m_createdResource;
302    }
303
304    /**
305     * Loads the property data with which the property dialog for the new resource should be initialized.<p>
306     *
307     * @return the properties bean
308     */
309    public CmsPropertiesBean getPropertyData() {
310
311        CmsPropertyEditorHelper helper = new PropertyEditorHelper(m_cms);
312        try {
313            CmsPropertiesBean data = helper.loadPropertyData(CmsUUID.getNullUUID());
314            return data;
315        } catch (Exception e) {
316            throw new RuntimeException(e);
317        }
318    }
319
320    /**
321     * Creates a resource, but doesn't throw any exceptions.<p>
322     *
323     * Exceptions will be passed to the onError method of registered callbacks.<p>
324     *
325     * @return the created resource
326     */
327    public CmsResource safeCreateResource() {
328
329        try {
330            return createResource();
331        } catch (Exception e) {
332            for (I_Callback callback : m_callbacks) {
333                callback.onError(e);
334            }
335            return null;
336        }
337    }
338
339    /**
340     * Sets the Explorer name generation mode.<p>
341     *
342     * @param explorerNameGenerationMode the explorer name generation mode
343     */
344    public void setExplorerNameGeneration(boolean explorerNameGenerationMode) {
345
346        m_explorerNameGeneration = explorerNameGenerationMode;
347    }
348
349    /**
350     * Sets the locale.<p>
351     *
352     * @param locale the locale
353     */
354    public void setLocale(Locale locale) {
355
356        m_cms.getRequestContext().setLocale(locale);
357
358    }
359
360    /**
361     * Sets the model resource.<p>
362     *
363     * @param modelResource the model resource
364     */
365    public void setModel(CmsResource modelResource) {
366
367        m_modelResource = modelResource;
368    }
369
370    /**
371     * Sets the creation path containing a number pattern.<p>
372     *
373     * @param destination the creation path
374     */
375    public void setPatternPath(String destination) {
376
377        m_pathWithPattern = destination;
378    }
379
380    /**
381     * Sets the property changes.<p>
382     *
383     * @param propertyChanges the property changes
384     */
385    public void setPropertyChanges(CmsPropertyChangeSet propertyChanges) {
386
387        m_propChanges = propertyChanges;
388    }
389
390    /**
391     * Sets the site root of the CMS context.<p>
392     *
393     * @param siteRoot the site root
394     */
395    public void setSiteRoot(String siteRoot) {
396
397        m_cms.getRequestContext().setSiteRoot(siteRoot);
398    }
399
400    /**
401     * Sets the resource type name.<p>
402     *
403     * @param type the resource type name
404     */
405    public void setType(String type) {
406
407        m_type = type;
408    }
409}