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 GmbH & Co. KG, 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.file.types;
029
030import org.opencms.ade.containerpage.shared.CmsContainerElement;
031import org.opencms.configuration.CmsConfigurationException;
032import org.opencms.db.CmsSecurityManager;
033import org.opencms.file.CmsFile;
034import org.opencms.file.CmsObject;
035import org.opencms.file.CmsProperty;
036import org.opencms.file.CmsPropertyDefinition;
037import org.opencms.file.CmsRequestContext;
038import org.opencms.file.CmsResource;
039import org.opencms.file.CmsResourceFilter;
040import org.opencms.loader.CmsLoaderException;
041import org.opencms.loader.CmsXmlContainerPageLoader;
042import org.opencms.main.CmsException;
043import org.opencms.main.CmsLog;
044import org.opencms.main.OpenCms;
045import org.opencms.relations.CmsLink;
046import org.opencms.relations.I_CmsLinkParseable;
047import org.opencms.security.CmsPermissionSet;
048import org.opencms.xml.CmsXmlContentDefinition;
049import org.opencms.xml.containerpage.CmsXmlContainerPage;
050import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
051import org.opencms.xml.types.CmsXmlVfsFileValue;
052import org.opencms.xml.types.I_CmsXmlContentValue;
053
054import java.util.ArrayList;
055import java.util.Collections;
056import java.util.HashSet;
057import java.util.Iterator;
058import java.util.List;
059import java.util.Locale;
060import java.util.Set;
061
062import org.apache.commons.logging.Log;
063
064/**
065 * Resource type descriptor for the type "containerpage".<p>
066 *
067 * It is just a xml content with a fixed schema.<p>
068 *
069 * @since 7.6
070 */
071public class CmsResourceTypeXmlContainerPage extends CmsResourceTypeXmlContent {
072
073    /** The configuration resource type name. */
074    public static final String CONFIGURATION_TYPE_NAME = "sitemap_config";
075
076    /** The group container resource type name. */
077    public static final String GROUP_CONTAINER_TYPE_NAME = "groupcontainer";
078
079    /** The inherit configuration resource type name. */
080    public static final String INHERIT_CONTAINER_CONFIG_TYPE_NAME = "inheritance_config";
081
082    /** The resource type name for inherited container references.  */
083    public static final String INHERIT_CONTAINER_TYPE_NAME = "inheritance_group";
084
085    /** The model group resource type name. */
086    public static final String MODEL_GROUP_TYPE_NAME = "modelgroup";
087
088    /** The name of this resource type. */
089    public static final String RESOURCE_TYPE_NAME = "containerpage";
090
091    /** A variable containing the actual configured type id of container pages. */
092    private static int containerPageTypeId;
093
094    /** The log object for this class. */
095    private static final Log LOG = CmsLog.getLog(CmsResourceTypeXmlContainerPage.class);
096
097    /** Fixed schema for container pages. */
098    private static final String SCHEMA = "/system/modules/org.opencms.ade.containerpage/schemas/container_page.xsd";
099
100    /** The serial version id. */
101    private static final long serialVersionUID = -6211941269510267155L;
102
103    /**
104     * Default constructor that sets the fixed schema for container pages.<p>
105     */
106    public CmsResourceTypeXmlContainerPage() {
107
108        super();
109        m_typeName = RESOURCE_TYPE_NAME;
110        addConfigurationParameter(CONFIGURATION_SCHEMA, SCHEMA);
111    }
112
113    /**
114     * Returns the container-page type id.<p>
115     *
116     * @return the container-page type id
117     *
118     * @throws CmsLoaderException if the type is not configured
119     */
120    @SuppressWarnings("deprecation")
121    public static int getContainerPageTypeId() throws CmsLoaderException {
122
123        if (containerPageTypeId == 0) {
124            I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(getStaticTypeName());
125            if (resType != null) {
126                containerPageTypeId = resType.getTypeId();
127            }
128        }
129        return containerPageTypeId;
130    }
131
132    /**
133     * Returns the container-page type id, but returns -1 instead of throwing an exception when an error happens.<p>
134     *
135     * @return the container-page type id
136     */
137    public static int getContainerPageTypeIdSafely() {
138
139        try {
140            return getContainerPageTypeId();
141        } catch (CmsLoaderException e) {
142            if (LOG.isDebugEnabled()) {
143                LOG.debug(e.getLocalizedMessage(), e);
144            }
145            return -1;
146        }
147    }
148
149    /**
150     * Returns the static type name of this (default) resource type.<p>
151     *
152     * @return the static type name of this (default) resource type
153     */
154    public static String getStaticTypeName() {
155
156        return RESOURCE_TYPE_NAME;
157    }
158
159    /**
160     * Returns <code>true</code> in case the given resource is a container page.<p>
161     *
162     * Internally this checks if the type id for the given resource is
163     * identical type id of the container page.<p>
164     *
165     * @param resource the resource to check
166     *
167     * @return <code>true</code> in case the given resource is a container page
168     */
169    public static boolean isContainerPage(CmsResource resource) {
170
171        boolean result = false;
172        if (resource != null) {
173            result = (resource.getTypeId() == getContainerPageTypeIdSafely())
174                || (OpenCms.getResourceManager().getResourceType(resource) instanceof CmsResourceTypeXmlContainerPage);
175        }
176
177        return result;
178
179    }
180
181    /**
182     * Checks whether the given resource is a model reuse group.<p>
183     *
184     * @param cms the cms context
185     * @param resource the resource
186     *
187     * @return <code>true</code> in case the resource is a model reuse group
188     */
189    public static boolean isModelCopyGroup(CmsObject cms, CmsResource resource) {
190
191        boolean result = false;
192        if (isModelGroup(resource)) {
193            try {
194                CmsProperty tempElementsProp = cms.readPropertyObject(
195                    resource,
196                    CmsPropertyDefinition.PROPERTY_TEMPLATE_ELEMENTS,
197                    false);
198                if (!tempElementsProp.isNullProperty()
199                    && CmsContainerElement.USE_AS_COPY_MODEL.equals(tempElementsProp.getValue())) {
200                    result = true;
201                }
202            } catch (CmsException e) {
203                LOG.warn(e.getMessage(), e);
204            }
205
206        }
207        return result;
208    }
209
210    /**
211     * Checks whether the given resource is a model group.<p>
212     *
213     * @param resource the resource
214     *
215     * @return <code>true</code> in case the resource is a model group
216     */
217    public static boolean isModelGroup(CmsResource resource) {
218
219        return OpenCms.getResourceManager().getResourceType(resource).getTypeName().equals(MODEL_GROUP_TYPE_NAME);
220    }
221
222    /**
223     * @see org.opencms.file.types.CmsResourceTypeXmlContent#createResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, java.lang.String, byte[], java.util.List)
224     */
225    @Override
226    public CmsResource createResource(
227        CmsObject cms,
228        CmsSecurityManager securityManager,
229        String resourcename,
230        byte[] content,
231        List<CmsProperty> properties)
232    throws CmsException {
233
234        boolean hasModelUri = false;
235        CmsXmlContainerPage newContent = null;
236        if ((getSchema() != null) && ((content == null) || (content.length == 0))) {
237            // unmarshal the content definition for the new resource
238            CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, getSchema());
239
240            // read the default locale for the new resource
241            Locale locale = OpenCms.getLocaleManager().getDefaultLocales(
242                cms,
243                CmsResource.getParentFolder(resourcename)).get(0);
244
245            String modelUri = (String)cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_MODEL);
246
247            // must set URI of OpenCms user context to parent folder of created resource,
248            // in order to allow reading of properties for default values
249            CmsObject newCms = OpenCms.initCmsObject(cms);
250            newCms.getRequestContext().setUri(CmsResource.getParentFolder(resourcename));
251            if (modelUri != null) {
252                // create the new content from the model file
253                newContent = CmsXmlContainerPageFactory.createDocument(newCms, locale, modelUri);
254                hasModelUri = true;
255            } else {
256                // create the new content from the content definition
257                newContent = CmsXmlContainerPageFactory.createDocument(
258                    newCms,
259                    locale,
260                    OpenCms.getSystemInfo().getDefaultEncoding(),
261                    contentDefinition);
262            }
263            // get the bytes from the created content
264            content = newContent.marshal();
265        }
266
267        // now create the resource using the super class
268        CmsResource resource = super.createResource(cms, securityManager, resourcename, content, properties);
269
270        // a model file was used, call the content handler for post-processing
271        if (hasModelUri) {
272            newContent = CmsXmlContainerPageFactory.unmarshal(cms, resource);
273            resource = newContent.getHandler().prepareForWrite(cms, newContent, newContent.getFile());
274        }
275
276        return resource;
277    }
278
279    /**
280     * @see org.opencms.file.types.CmsResourceTypeXmlContent#getLoaderId()
281     */
282    @Override
283    public int getLoaderId() {
284
285        return CmsXmlContainerPageLoader.CONTAINER_PAGE_RESOURCE_LOADER_ID;
286    }
287
288    /**
289     * @see org.opencms.file.types.A_CmsResourceType#initConfiguration(java.lang.String, java.lang.String, String)
290     */
291    @Override
292    public void initConfiguration(String name, String id, String className) throws CmsConfigurationException {
293
294        if (!RESOURCE_TYPE_NAME.equals(name) && !MODEL_GROUP_TYPE_NAME.equals(name)) {
295            // default resource type MUST have default name
296            throw new CmsConfigurationException(
297                Messages.get().container(
298                    Messages.ERR_INVALID_RESTYPE_CONFIG_NAME_3,
299                    this.getClass().getName(),
300                    RESOURCE_TYPE_NAME,
301                    name));
302        }
303        super.initConfiguration(name, id, className);
304    }
305
306    /**
307     * @see org.opencms.relations.I_CmsLinkParseable#parseLinks(org.opencms.file.CmsObject, org.opencms.file.CmsFile)
308     */
309    @Override
310    public List<CmsLink> parseLinks(CmsObject cms, CmsFile file) {
311
312        if (file.getLength() == 0) {
313            return Collections.emptyList();
314        }
315        CmsXmlContainerPage xmlContent;
316        long requestTime = cms.getRequestContext().getRequestTime();
317        try {
318            // prevent the check rules to remove the broken links
319            cms.getRequestContext().setRequestTime(CmsResource.DATE_RELEASED_EXPIRED_IGNORE);
320            xmlContent = CmsXmlContainerPageFactory.unmarshal(cms, file);
321        } catch (CmsException e) {
322            if (LOG.isErrorEnabled()) {
323                LOG.error(
324                    org.opencms.db.Messages.get().getBundle().key(
325                        org.opencms.db.Messages.ERR_READ_RESOURCE_1,
326                        cms.getSitePath(file)),
327                    e);
328            }
329            return Collections.emptyList();
330        } finally {
331            cms.getRequestContext().setRequestTime(requestTime);
332        }
333
334        Set<CmsLink> links = new HashSet<CmsLink>();
335
336        // add XSD link
337        CmsLink xsdLink = getXsdLink(cms, xmlContent);
338        if (xsdLink != null) {
339            links.add(xsdLink);
340        }
341
342        // iterate over all languages
343        List<Locale> locales = xmlContent.getLocales();
344        Iterator<Locale> i = locales.iterator();
345        while (i.hasNext()) {
346            Locale locale = i.next();
347            List<I_CmsXmlContentValue> values = xmlContent.getValues(locale);
348
349            // iterate over all body elements per language
350            Iterator<I_CmsXmlContentValue> j = values.iterator();
351            while (j.hasNext()) {
352                I_CmsXmlContentValue value = j.next();
353                if (!(value instanceof CmsXmlVfsFileValue)) {
354                    // filter only relations relevant fields
355                    // container pages do not have XmlHtml nor VarFiles
356                    continue;
357                }
358                CmsXmlVfsFileValue refValue = (CmsXmlVfsFileValue)value;
359                CmsLink link = refValue.getLink(cms);
360                if (link != null) {
361                    links.add(link);
362                }
363            }
364        }
365        return new ArrayList<CmsLink>(links);
366    }
367
368    /**
369     * @see org.opencms.file.types.CmsResourceTypeXmlContent#writeFile(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsFile)
370     */
371    @Override
372    public CmsFile writeFile(CmsObject cms, CmsSecurityManager securityManager, CmsFile resource) throws CmsException {
373
374        // check if the user has write access and if resource is locked
375        // done here so that all the XML operations are not performed if permissions not granted
376        securityManager.checkPermissions(
377            cms.getRequestContext(),
378            resource,
379            CmsPermissionSet.ACCESS_WRITE,
380            true,
381            CmsResourceFilter.ALL);
382        // read the XML content, use the encoding set in the property
383        CmsXmlContainerPage xmlContent = CmsXmlContainerPageFactory.unmarshal(cms, resource, false, true);
384        // call the content handler for post-processing
385        resource = xmlContent.getHandler().prepareForWrite(cms, xmlContent, resource);
386
387        // now write the file
388        CmsFile file = securityManager.writeFile(cms.getRequestContext(), resource);
389        I_CmsResourceType type = getResourceType(file);
390        // update the relations after writing!!
391        List<CmsLink> links = null;
392        if (type instanceof I_CmsLinkParseable) { // this check is needed because of type change
393            // if the new type is link parseable
394            links = ((I_CmsLinkParseable)type).parseLinks(cms, file);
395        }
396        // this has to be always executed, even if not link parseable to remove old links
397        securityManager.updateRelationsForResource(cms.getRequestContext(), file, links);
398        return file;
399    }
400}