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.Locale;
044
045import org.dom4j.Attribute;
046import org.dom4j.Element;
047
048/**
049 * Describes the XML content type "OpenCmsVfsFile".<p>
050 *
051 * This type allows links to internal VFS resources only.<p>
052 *
053 * @since 7.0.0
054 */
055public class CmsXmlVfsFileValue extends A_CmsXmlContentValue {
056
057    /** Value to mark that no link is defined, "none". */
058    public static final String NO_LINK = "none";
059
060    /** The name of this type as used in the XML schema. */
061    public static final String TYPE_NAME = "OpenCmsVfsFile";
062
063    /** The vfs link type constant. */
064    public static final String TYPE_VFS_LINK = "vfsLink";
065
066    /** The schema definition String is located in a text for easier editing. */
067    private static String m_schemaDefinition;
068
069    /** The String value of the element node. */
070    private String m_stringValue;
071
072    /**
073     * Creates a new, empty schema type descriptor of type "OpenCmsVfsFile".<p>
074     */
075    public CmsXmlVfsFileValue() {
076
077        // empty constructor is required for class registration
078    }
079
080    /**
081     * Creates a new XML content value of type "OpenCmsVfsFile".<p>
082     *
083     * @param document the XML content instance this value belongs to
084     * @param element the XML element that contains this value
085     * @param locale the locale this value is created for
086     * @param type the type instance to create the value for
087     */
088    public CmsXmlVfsFileValue(I_CmsXmlDocument document, Element element, Locale locale, I_CmsXmlSchemaType type) {
089
090        super(document, element, locale, type);
091    }
092
093    /**
094     * Creates a new schema type descriptor for the type "OpenCmsVfsFile".<p>
095     *
096     * @param name the name of the XML node containing the value according to the XML schema
097     * @param minOccurs minimum number of occurrences of this type according to the XML schema
098     * @param maxOccurs maximum number of occurrences of this type according to the XML schema
099     */
100    public CmsXmlVfsFileValue(String name, String minOccurs, String maxOccurs) {
101
102        super(name, minOccurs, maxOccurs);
103    }
104
105    /**
106     * Fills the given element with a {@link CmsXmlVfsFileValue} for the given data.<p>
107     *
108     * @param element the element to fill
109     * @param id the id to use
110     * @param rootPath the path to use
111     * @param type the relation type to use
112     */
113    public static void fillEntry(Element element, CmsUUID id, String rootPath, CmsRelationType type) {
114
115        CmsLink link = new CmsLink(CmsXmlVfsFileValue.TYPE_VFS_LINK, type, id, rootPath, true);
116        // get xml node
117        Element linkElement = element.element(CmsXmlPage.NODE_LINK);
118        if (linkElement == null) {
119            // create xml node if needed
120            linkElement = element.addElement(CmsXmlPage.NODE_LINK);
121        }
122        // update xml node
123        CmsLinkUpdateUtil.updateXmlForVfsFile(link, linkElement);
124    }
125
126    /**
127     * @see org.opencms.xml.types.A_CmsXmlContentValue#createValue(I_CmsXmlDocument, org.dom4j.Element, Locale)
128     */
129    public I_CmsXmlContentValue createValue(I_CmsXmlDocument document, Element element, Locale locale) {
130
131        return new CmsXmlVfsFileValue(document, element, locale, this);
132    }
133
134    /**
135     * @see org.opencms.xml.types.I_CmsXmlSchemaType#generateXml(org.opencms.file.CmsObject, org.opencms.xml.I_CmsXmlDocument, org.dom4j.Element, java.util.Locale)
136     */
137    @Override
138    public Element generateXml(CmsObject cms, I_CmsXmlDocument document, Element root, Locale locale) {
139
140        Element element = root.addElement(getName());
141
142        // get the default value from the content handler
143        String defaultValue = document.getHandler().getDefault(cms, this, locale);
144        if (defaultValue != null) {
145            I_CmsXmlContentValue value = createValue(document, element, locale);
146            value.setStringValue(cms, defaultValue);
147        }
148        return element;
149    }
150
151    /**
152     * Returns the link object represented by this XML content value.<p>
153     *
154     * @param cms the cms context, can be <code>null</code> but in this case no link check is performed
155     *
156     * @return the link object represented by this XML content value (will return null for an empty link)
157     */
158    public CmsLink getLink(CmsObject cms) {
159
160        Element linkElement = m_element.element(CmsXmlPage.NODE_LINK);
161        if (linkElement == null) {
162            String textValue = m_element.getText();
163            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(textValue)) {
164                if (CmsUUID.isValidUUID(textValue)) {
165                    setIdValue(cms, new CmsUUID(textValue));
166                } else {
167                    setStringValue(cms, textValue);
168                }
169            }
170            linkElement = m_element.element(CmsXmlPage.NODE_LINK);
171            if (linkElement == null) {
172                return null;
173            }
174        }
175        CmsLinkUpdateUtil.updateType(linkElement, getRelationType(getPath()));
176        CmsLink link = new CmsLink(linkElement);
177        link.checkConsistency(cms);
178        if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) {
179            return null;
180        }
181        return link;
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/XmlVfsFileValue.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 CmsXmlVfsFileValue(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 file value
247     */
248    public void setIdValue(CmsObject cms, CmsUUID id) {
249
250        CmsRelationType type = getRelationType(getPath());
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        if (CmsUUID.isValidUUID(value)) {
272            setIdValue(cms, new CmsUUID(value));
273            return;
274        }
275        String path = value;
276        if (cms != null) {
277            String siteRoot = OpenCms.getSiteManager().getSiteRoot(value);
278            String oldSite = cms.getRequestContext().getSiteRoot();
279            try {
280                if (siteRoot != null) {
281                    // only switch the site if needed
282                    cms.getRequestContext().setSiteRoot(siteRoot);
283                    // remove the site root, because the link manager call will append it anyway
284                    path = cms.getRequestContext().removeSiteRoot(value);
285                    if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
286                        path = "/";
287                    }
288                }
289                // remove parameters, if not the link manager call might fail
290                String query = "";
291                int pos = path.indexOf(CmsRequestUtil.URL_DELIMITER);
292                int anchorPos = path.indexOf('#');
293                if ((pos == -1) || ((anchorPos > -1) && (pos > anchorPos))) {
294                    pos = anchorPos;
295                }
296                if (pos > -1) {
297                    query = path.substring(pos);
298                    path = path.substring(0, pos);
299                }
300                // get the root path
301                path = OpenCms.getLinkManager().getRootPath(cms, path);
302                if (path == null) {
303                    path = value;
304                } else {
305                    // append parameters again
306                    path += query;
307                }
308            } finally {
309                if (siteRoot != null) {
310                    cms.getRequestContext().setSiteRoot(oldSite);
311                }
312            }
313        }
314        if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
315            return;
316        }
317        CmsRelationType type = getRelationType(getPath());
318        CmsLink link = new CmsLink(TYPE_VFS_LINK, type, path, true);
319        // link management check
320        link.checkConsistency(cms);
321        // update xml node
322        CmsLinkUpdateUtil.updateXmlForVfsFile(link, m_element.addElement(CmsXmlPage.NODE_LINK));
323    }
324
325    /**
326     * Creates the String value for this vfs file value element.<p>
327     *
328     * @param cms the cms context
329     *
330     * @return the String value for this vfs file value element
331     */
332    private String createStringValue(CmsObject cms) {
333
334        Attribute enabled = m_element.attribute(CmsXmlPage.ATTRIBUTE_ENABLED);
335
336        String content = "";
337        if ((enabled == null) || Boolean.valueOf(enabled.getText()).booleanValue()) {
338            CmsLink link = getLink(cms);
339            if (link != null) {
340                content = link.getUri();
341                if (cms != null) {
342                    content = cms.getRequestContext().removeSiteRoot(link.getUri());
343                }
344            }
345        }
346        return content;
347    }
348}