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.configuration.CmsConfigurationException;
031import org.opencms.db.CmsSecurityManager;
032import org.opencms.file.CmsFile;
033import org.opencms.file.CmsObject;
034import org.opencms.file.CmsProperty;
035import org.opencms.file.CmsResource;
036import org.opencms.file.CmsResourceFilter;
037import org.opencms.i18n.CmsLocaleManager;
038import org.opencms.loader.CmsXmlPageLoader;
039import org.opencms.main.CmsException;
040import org.opencms.main.CmsLog;
041import org.opencms.relations.CmsLink;
042import org.opencms.security.CmsPermissionSet;
043import org.opencms.staticexport.CmsLinkTable;
044import org.opencms.util.CmsHtmlConverter;
045import org.opencms.xml.CmsXmlEntityResolver;
046import org.opencms.xml.CmsXmlException;
047import org.opencms.xml.page.CmsXmlPage;
048import org.opencms.xml.page.CmsXmlPageFactory;
049
050import java.util.ArrayList;
051import java.util.Collections;
052import java.util.Iterator;
053import java.util.LinkedHashSet;
054import java.util.List;
055import java.util.Locale;
056import java.util.Set;
057
058import org.apache.commons.logging.Log;
059
060/**
061 * Resource type descriptor for the type "xmlpage".<p>
062 *
063 * @since 6.0.0
064 */
065public class CmsResourceTypeXmlPage extends A_CmsResourceTypeLinkParseable {
066
067    /** The default XML page body. */
068    private static final String DEFAULT_BODY = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
069        + "\n"
070        + "<pages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://www.opencms.org/dtd/6.0/xmlpage.xsd\">\n"
071        + "</pages>";
072
073    /** The log object for this class. */
074    private static final Log LOG = CmsLog.getLog(CmsResourceTypeXmlPage.class);
075
076    /** The static type id of this resource type. */
077    private static int m_staticTypeId;
078
079    /** The type id of this resource type. */
080    private static final int RESOURCE_TYPE_ID = 6;
081
082    /** The name of this resource type. */
083    private static final String RESOURCE_TYPE_NAME = "xmlpage";
084
085    /** The serial version id. */
086    private static final long serialVersionUID = 4218077530657122699L;
087
088    /**
089     * Default constructor, used to initialize member variables.<p>
090     */
091    public CmsResourceTypeXmlPage() {
092
093        super();
094        m_typeId = RESOURCE_TYPE_ID;
095        m_typeName = RESOURCE_TYPE_NAME;
096    }
097
098    /**
099     * Returns the static type id of this (default) resource type.<p>
100     *
101     * @return the static type id of this (default) resource type
102     */
103    public static int getStaticTypeId() {
104
105        return m_staticTypeId;
106    }
107
108    /**
109     * Returns the static type name of this (default) resource type.<p>
110     *
111     * @return the static type name of this (default) resource type
112     */
113    public static String getStaticTypeName() {
114
115        return RESOURCE_TYPE_NAME;
116    }
117
118    /**
119     * Returns <code>true</code> in case the given resource is an XML page.<p>
120     *
121     * Internally this checks if the type id for the given resource is
122     * identical type id of the XML page.<p>
123     *
124     * @param resource the resource to check
125     *
126     * @return <code>true</code> in case the given resource is an XML page
127     *
128     * @since 7.0.2
129     */
130    public static boolean isXmlPage(CmsResource resource) {
131
132        boolean result = false;
133        if (resource != null) {
134            result = resource.getTypeId() == m_staticTypeId;
135        }
136        return result;
137    }
138
139    /**
140     * @see org.opencms.file.types.A_CmsResourceType#createResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, java.lang.String, byte[], java.util.List)
141     */
142    @Override
143    public CmsResource createResource(
144        CmsObject cms,
145        CmsSecurityManager securityManager,
146        String resourcename,
147        byte[] content,
148        List<CmsProperty> properties)
149    throws CmsException {
150
151        if (content == null) {
152            try {
153                String encoding = CmsLocaleManager.getResourceEncoding(
154                    cms,
155                    cms.readResource(CmsResource.getParentFolder(resourcename)));
156                content = DEFAULT_BODY.getBytes(encoding);
157            } catch (Exception e) {
158                LOG.error(e.getLocalizedMessage(), e);
159            }
160        }
161        return super.createResource(cms, securityManager, resourcename, content, properties);
162    }
163
164    /**
165     * @see org.opencms.file.types.I_CmsResourceType#getCachePropertyDefault()
166     */
167    @Override
168    public String getCachePropertyDefault() {
169
170        return "element;locale;";
171    }
172
173    /**
174     * @see org.opencms.file.types.I_CmsResourceType#getLoaderId()
175     */
176    @Override
177    public int getLoaderId() {
178
179        return CmsXmlPageLoader.RESOURCE_LOADER_ID;
180    }
181
182    /**
183     * @see org.opencms.file.types.A_CmsResourceType#initConfiguration(java.lang.String, java.lang.String, String)
184     */
185    @Override
186    public void initConfiguration(String name, String id, String className) throws CmsConfigurationException {
187
188        super.initConfiguration(name, id, className);
189        m_staticTypeId = m_typeId;
190    }
191
192    /**
193     * @see org.opencms.relations.I_CmsLinkParseable#parseLinks(org.opencms.file.CmsObject, org.opencms.file.CmsFile)
194     */
195    public List<CmsLink> parseLinks(CmsObject cms, CmsFile file) {
196
197        // use a linked set to keep the link order
198        Set<CmsLink> links = new LinkedHashSet<CmsLink>();
199        try {
200            CmsXmlPage xmlPage = CmsXmlPageFactory.unmarshal(cms, file);
201            List<Locale> locales = xmlPage.getLocales();
202
203            // iterate over all languages
204            Iterator<Locale> i = locales.iterator();
205            while (i.hasNext()) {
206                Locale locale = i.next();
207                List<String> elementNames = xmlPage.getNames(locale);
208
209                // iterate over all body elements per language
210                Iterator<String> j = elementNames.iterator();
211                while (j.hasNext()) {
212                    String elementName = j.next();
213                    CmsLinkTable linkTable = xmlPage.getLinkTable(elementName, locale);
214
215                    // iterate over all links inside a body element
216                    Iterator<CmsLink> k = linkTable.iterator();
217                    while (k.hasNext()) {
218                        CmsLink link = k.next();
219                        if (link.isInternal()) {
220                            link.checkConsistency(cms);
221                            links.add(link);
222                        }
223                    }
224                }
225            }
226        } catch (CmsXmlException e) {
227            if (LOG.isErrorEnabled()) {
228                LOG.error(
229                    Messages.get().getBundle().key(Messages.ERR_PROCESS_HTML_CONTENT_1, cms.getSitePath(file)),
230                    e);
231            }
232
233            return Collections.emptyList();
234        }
235        return new ArrayList<CmsLink>(links);
236    }
237
238    /**
239     * @see org.opencms.file.types.I_CmsResourceType#writeFile(org.opencms.file.CmsObject, CmsSecurityManager, CmsFile)
240     */
241    @Override
242    public CmsFile writeFile(CmsObject cms, CmsSecurityManager securityManager, CmsFile resource) throws CmsException {
243
244        // check if the user has write access and if resource is locked
245        // done here so that all the XML operations are not performed if permissions not granted
246        securityManager.checkPermissions(
247            cms.getRequestContext(),
248            resource,
249            CmsPermissionSet.ACCESS_WRITE,
250            true,
251            CmsResourceFilter.ALL);
252
253        // empty file content is allowed
254        if (resource.getLength() > 0) {
255            // read the xml page, use the encoding set in the property
256            CmsXmlPage xmlPage = CmsXmlPageFactory.unmarshal(cms, resource, false);
257            // validate the xml structure before writing the file
258            // an exception will be thrown if the structure is invalid
259            xmlPage.validateXmlStructure(new CmsXmlEntityResolver(cms));
260            // read the content-conversion property
261            String contentConversion = CmsHtmlConverter.getConversionSettings(cms, resource);
262            xmlPage.setConversion(contentConversion);
263            // correct the HTML structure
264            resource = xmlPage.correctXmlStructure(cms);
265        }
266
267        // xml is valid if no exception occurred
268        return super.writeFile(cms, securityManager, resource);
269    }
270}