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.xml.types;
029
030import org.opencms.file.CmsObject;
031import org.opencms.main.CmsIllegalArgumentException;
032import org.opencms.main.CmsRuntimeException;
033import org.opencms.main.OpenCms;
034import org.opencms.relations.CmsLink;
035import org.opencms.relations.CmsLinkUpdateUtil;
036import org.opencms.relations.CmsRelationType;
037import org.opencms.util.CmsRequestUtil;
038import org.opencms.util.CmsStringUtil;
039import org.opencms.util.CmsUUID;
040import org.opencms.xml.I_CmsXmlDocument;
041import org.opencms.xml.page.CmsXmlPage;
042
043import java.util.ArrayList;
044import java.util.List;
045import java.util.Locale;
046
047import org.dom4j.Attribute;
048import org.dom4j.Element;
049
050/**
051 * Describes the XML content type "OpenCmsVfsFile".<p>
052 *
053 * This type allows links to internal VFS resources only.<p>
054 *
055 * @since 7.0.0
056 */
057public class CmsXmlCategoryValue extends A_CmsXmlContentValue {
058
059    /** Value to mark that no link is defined, "none". */
060    public static final String NO_LINK = "none";
061
062    /** The name of this type as used in the XML schema. */
063    public static final String TYPE_NAME = "OpenCmsCategory";
064
065    /** The vfs link type constant. */
066    public static final String TYPE_VFS_LINK = "vfsLink";
067
068    /** The schema definition String is located in a text for easier editing. */
069    private static String m_schemaDefinition;
070
071    /** The String value of the element node. */
072    private String m_stringValue;
073
074    /**
075     * Creates a new, empty schema type descriptor of type "OpenCmsCategoryValue".<p>
076     */
077    public CmsXmlCategoryValue() {
078
079        // empty constructor is required for class registration
080    }
081
082    /**
083     * Creates a new XML content value of type "OpenCmsCategoryValue".<p>
084     *
085     * @param document the XML content instance this value belongs to
086     * @param element the XML element that contains this value
087     * @param locale the locale this value is created for
088     * @param type the type instance to create the value for
089     */
090    public CmsXmlCategoryValue(I_CmsXmlDocument document, Element element, Locale locale, I_CmsXmlSchemaType type) {
091
092        super(document, element, locale, type);
093    }
094
095    /**
096     * Creates a new schema type descriptor for the type "OpenCmsCategoryValue".<p>
097     *
098     * @param name the name of the XML node containing the value according to the XML schema
099     * @param minOccurs minimum number of occurrences of this type according to the XML schema
100     * @param maxOccurs maximum number of occurrences of this type according to the XML schema
101     */
102    public CmsXmlCategoryValue(String name, String minOccurs, String maxOccurs) {
103
104        super(name, minOccurs, maxOccurs);
105    }
106
107    /**
108     * Fills the given element with a {@link CmsXmlCategoryValue} for the given data.<p>
109     *
110     * @param element the element to fill
111     * @param id the id to use
112     * @param rootPath the path to use
113     * @param type the relation type to use
114     */
115    public static void fillEntry(Element element, CmsUUID id, String rootPath, CmsRelationType type) {
116
117        CmsLink link = new CmsLink(CmsXmlCategoryValue.TYPE_VFS_LINK, type, id, rootPath, true);
118        // get xml node
119        Element linkElement = element.element(CmsXmlPage.NODE_LINK);
120        if (linkElement == null) {
121            // create xml node if needed
122            linkElement = element.addElement(CmsXmlPage.NODE_LINK);
123        }
124        // update xml node
125        CmsLinkUpdateUtil.updateXmlForVfsFile(link, linkElement);
126    }
127
128    /**
129     * @see org.opencms.xml.types.A_CmsXmlContentValue#createValue(I_CmsXmlDocument, org.dom4j.Element, Locale)
130     */
131    public I_CmsXmlContentValue createValue(I_CmsXmlDocument document, Element element, Locale locale) {
132
133        return new CmsXmlCategoryValue(document, element, locale, this);
134    }
135
136    /**
137     * @see org.opencms.xml.types.I_CmsXmlSchemaType#generateXml(org.opencms.file.CmsObject, org.opencms.xml.I_CmsXmlDocument, org.dom4j.Element, java.util.Locale)
138     */
139    @Override
140    public Element generateXml(CmsObject cms, I_CmsXmlDocument document, Element root, Locale locale) {
141
142        Element element = root.addElement(getName());
143
144        // get the default value from the content handler
145        String defaultValue = document.getHandler().getDefault(cms, this, locale);
146        if (defaultValue != null) {
147            I_CmsXmlContentValue value = createValue(document, element, locale);
148            value.setStringValue(cms, defaultValue);
149        }
150        return element;
151    }
152
153    /**
154     * Returns the link objects represented by this XML content value.<p>
155     *
156     * @param cms the cms context, can be <code>null</code> but in this case no link check is performed
157     *
158     * @return  a list of link objects represented by this XML content value
159     */
160    public List<CmsLink> getLinks(CmsObject cms) {
161
162        List<CmsLink> result = new ArrayList<CmsLink>();
163
164        @SuppressWarnings("unchecked")
165        List<Element> linkElements = m_element.elements(CmsXmlPage.NODE_LINK);
166        for (Element linkElement : linkElements) {
167            if (linkElement == null) {
168                String uri = m_element.getText();
169                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(uri)) {
170                    setStringValue(cms, uri);
171                }
172            }
173            CmsLinkUpdateUtil.updateType(linkElement, getRelationType(getPath()));
174            CmsLink link = new CmsLink(linkElement);
175            link.checkConsistency(cms);
176            if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) {
177                continue;
178            }
179            result.add(link);
180        }
181        return result;
182    }
183
184    /**
185     * @see org.opencms.xml.types.I_CmsXmlContentValue#getPlainText(org.opencms.file.CmsObject)
186     */
187    @Override
188    public String getPlainText(CmsObject cms) {
189
190        return getStringValue(cms);
191    }
192
193    /**
194     * @see org.opencms.xml.types.I_CmsXmlSchemaType#getSchemaDefinition()
195     */
196    public String getSchemaDefinition() {
197
198        // the schema definition is located in a separate file for easier editing
199        if (m_schemaDefinition == null) {
200            m_schemaDefinition = readSchemaDefinition("org/opencms/xml/types/XmlCategoryValue.xsd");
201        }
202        return m_schemaDefinition;
203    }
204
205    /**
206     * @see org.opencms.xml.types.I_CmsXmlContentValue#getStringValue(CmsObject)
207     */
208    public String getStringValue(CmsObject cms) throws CmsRuntimeException {
209
210        if (m_stringValue == null) {
211            m_stringValue = createStringValue(cms);
212        }
213        return m_stringValue;
214    }
215
216    /**
217     * @see org.opencms.xml.types.A_CmsXmlContentValue#getTypeName()
218     */
219    public String getTypeName() {
220
221        return TYPE_NAME;
222    }
223
224    /**
225     * @see org.opencms.xml.types.A_CmsXmlContentValue#isSearchable()
226     */
227    @Override
228    public boolean isSearchable() {
229
230        // there is no point in searching link values
231        return false;
232    }
233
234    /**
235     * @see org.opencms.xml.types.A_CmsXmlContentValue#newInstance(java.lang.String, java.lang.String, java.lang.String)
236     */
237    public I_CmsXmlSchemaType newInstance(String name, String minOccurs, String maxOccurs) {
238
239        return new CmsXmlCategoryValue(name, minOccurs, maxOccurs);
240    }
241
242    /**
243     * Sets the value as a structure id.<p>
244     *
245     * @param cms the current CMS context
246     * @param id the structure id which should be stored in the category value
247     */
248    public void setIdValue(CmsObject cms, CmsUUID id) {
249
250        CmsRelationType type = CmsRelationType.CATEGORY;
251        CmsLink link = new CmsLink(TYPE_VFS_LINK, type, id, "@", true);
252        // link management check
253        link.checkConsistency(cms);
254        // update xml node
255        CmsLinkUpdateUtil.updateXmlForVfsFile(link, m_element.addElement(CmsXmlPage.NODE_LINK));
256
257    }
258
259    /**
260     * @see org.opencms.xml.types.A_CmsXmlContentValue#setStringValue(org.opencms.file.CmsObject, java.lang.String)
261     */
262    public void setStringValue(CmsObject cms, String value) throws CmsIllegalArgumentException {
263
264        m_element.clearContent();
265        // ensure the String value is re-calculated next time it's needed
266        m_stringValue = null;
267        if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
268            // no valid value given
269            return;
270        }
271        String[] pathes = value.split(",");
272        for (String path : pathes) {
273            if (cms != null) {
274                String siteRoot = OpenCms.getSiteManager().getSiteRoot(path);
275                String oldSite = cms.getRequestContext().getSiteRoot();
276                try {
277                    if (siteRoot != null) {
278                        // only switch the site if needed
279                        cms.getRequestContext().setSiteRoot(siteRoot);
280                        // remove the site root, because the link manager call will append it anyway
281                        path = cms.getRequestContext().removeSiteRoot(path);
282                    }
283                    // remove parameters, if not the link manager call might fail
284                    String query = "";
285                    int pos = path.indexOf(CmsRequestUtil.URL_DELIMITER);
286                    int anchorPos = path.indexOf('#');
287                    if ((pos == -1) || ((anchorPos > -1) && (pos > anchorPos))) {
288                        pos = anchorPos;
289                    }
290                    if (pos > -1) {
291                        query = path.substring(pos);
292                        path = path.substring(0, pos);
293                    }
294                    // get the root path
295                    path = OpenCms.getLinkManager().getRootPath(cms, path);
296                    if (path != null) {
297                        // append parameters again
298                        path += query;
299                    }
300                } finally {
301                    if (siteRoot != null) {
302                        cms.getRequestContext().setSiteRoot(oldSite);
303                    }
304                }
305            }
306            if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
307                continue;
308            }
309            CmsRelationType type = getRelationType(getPath());
310            CmsLink link = new CmsLink(TYPE_VFS_LINK, type, path, true);
311            // link management check
312            link.checkConsistency(cms);
313            // update xml node
314            CmsLinkUpdateUtil.updateXmlForVfsFile(link, m_element.addElement(CmsXmlPage.NODE_LINK));
315        }
316    }
317
318    /**
319     * Creates the String value for this category value element.<p>
320     *
321     * @param cms the cms context
322     *
323     * @return the String value for this category value element
324     */
325    private String createStringValue(CmsObject cms) {
326
327        Attribute enabled = m_element.attribute(CmsXmlPage.ATTRIBUTE_ENABLED);
328
329        String content = "";
330        if ((enabled == null) || Boolean.valueOf(enabled.getText()).booleanValue()) {
331            List<CmsLink> links = getLinks(cms);
332            int i = 0;
333            for (CmsLink link : links) {
334                if (link != null) {
335                    String uri = "";
336                    uri += link.getUri();
337                    if (cms != null) {
338                        uri = cms.getRequestContext().removeSiteRoot(link.getUri());
339                    }
340                    if (i > 0) {
341                        content += ",";
342                    }
343                    content += uri;
344                    i++;
345                }
346            }
347        }
348        return content;
349    }
350}