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.xml.I_CmsXmlDocument;
040import org.opencms.xml.page.CmsXmlPage;
041
042import java.util.Locale;
043
044import org.dom4j.Attribute;
045import org.dom4j.Element;
046
047/**
048 * Describes the XML content type "OpenCmsVarLink".<p>
049 *
050 * This type allows a link to either an internal VFS resource, or to an external website.<p>
051 *
052 * @since 7.0.0
053 */
054public class CmsXmlVarLinkValue extends A_CmsXmlContentValue {
055
056    /** Value to mark that no link is defined, "none". */
057    public static final String NO_LINK = "none";
058
059    /** The name of this type as used in the XML schema. */
060    public static final String TYPE_NAME = "OpenCmsVarLink";
061
062    /** The var link type constant. */
063    public static final String TYPE_VAR_LINK = "varLink";
064
065    /** The schema definition String is located in a text for easier editing. */
066    private static String m_schemaDefinition;
067
068    /** The link object represented by this object. */
069    private CmsLink m_linkValue;
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 "OpenCmsVfsFile".<p>
076     */
077    public CmsXmlVarLinkValue() {
078
079        // empty constructor is required for class registration
080    }
081
082    /**
083     * Creates a new XML content value of type "OpenCmsVfsFile".<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 CmsXmlVarLinkValue(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 "OpenCmsVfsFile".<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 CmsXmlVarLinkValue(String name, String minOccurs, String maxOccurs) {
103
104        super(name, minOccurs, maxOccurs);
105    }
106
107    /**
108     * @see org.opencms.xml.types.A_CmsXmlContentValue#createValue(I_CmsXmlDocument, org.dom4j.Element, Locale)
109     */
110    public I_CmsXmlContentValue createValue(I_CmsXmlDocument document, Element element, Locale locale) {
111
112        return new CmsXmlVarLinkValue(document, element, locale, this);
113    }
114
115    /**
116     * @see org.opencms.xml.types.I_CmsXmlSchemaType#generateXml(org.opencms.file.CmsObject, org.opencms.xml.I_CmsXmlDocument, org.dom4j.Element, java.util.Locale)
117     */
118    @Override
119    public Element generateXml(CmsObject cms, I_CmsXmlDocument document, Element root, Locale locale) {
120
121        Element element = root.addElement(getName());
122
123        // get the default value from the content handler
124        String defaultValue = document.getHandler().getDefault(cms, this, locale);
125        if (defaultValue != null) {
126            I_CmsXmlContentValue value = createValue(document, element, locale);
127            value.setStringValue(cms, defaultValue);
128        }
129        return element;
130    }
131
132    /**
133     * Returns the link object represented by this XML content value.<p>
134     *
135     * @param cms the cms context, can be <code>null</code> but in this case no link check is performed,
136     *      and the target is marked as "external"
137     *
138     * @return the link object represented by this XML content value
139     */
140    public CmsLink getLink(CmsObject cms) {
141
142        if (m_linkValue == null) {
143            // need to to calculate link value twice
144            Element linkElement = m_element.element(CmsXmlPage.NODE_LINK);
145            if (linkElement == null) {
146                setStringValue(cms, m_element.getText());
147            } else {
148                CmsLinkUpdateUtil.updateType(linkElement, getRelationType(getPath()));
149                CmsLink link = new CmsLink(linkElement);
150                if (link.isInternal()) {
151                    // link management check
152                    link.checkConsistency(cms);
153                }
154                if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) {
155                    // this may just be an anchor link
156                    m_linkValue = CmsLink.NULL_LINK;
157                } else {
158                    m_linkValue = link;
159                }
160            }
161        }
162        if (m_linkValue == CmsLink.NULL_LINK) {
163            return null;
164        }
165        return m_linkValue;
166    }
167
168    /**
169     * @see org.opencms.xml.types.I_CmsXmlContentValue#getPlainText(org.opencms.file.CmsObject)
170     */
171    @Override
172    public String getPlainText(CmsObject cms) {
173
174        return getStringValue(cms);
175    }
176
177    /**
178     * @see org.opencms.xml.types.I_CmsXmlSchemaType#getSchemaDefinition()
179     */
180    public String getSchemaDefinition() {
181
182        // the schema definition is located in a separate file for easier editing
183        if (m_schemaDefinition == null) {
184            m_schemaDefinition = readSchemaDefinition("org/opencms/xml/types/XmlVarLinkValue.xsd");
185        }
186        return m_schemaDefinition;
187    }
188
189    /**
190     * @see org.opencms.xml.types.I_CmsXmlContentValue#getStringValue(CmsObject)
191     */
192    public String getStringValue(CmsObject cms) throws CmsRuntimeException {
193
194        if (m_stringValue == null) {
195            m_stringValue = createStringValue(cms);
196        }
197        return m_stringValue;
198    }
199
200    /**
201     * @see org.opencms.xml.types.A_CmsXmlContentValue#getTypeName()
202     */
203    public String getTypeName() {
204
205        return TYPE_NAME;
206    }
207
208    /**
209     * @see org.opencms.xml.types.A_CmsXmlContentValue#isSearchable()
210     */
211    @Override
212    public boolean isSearchable() {
213
214        // there is no point in searching link values
215        return false;
216    }
217
218    /**
219     * @see org.opencms.xml.types.A_CmsXmlContentValue#newInstance(java.lang.String, java.lang.String, java.lang.String)
220     */
221    public I_CmsXmlSchemaType newInstance(String name, String minOccurs, String maxOccurs) {
222
223        return new CmsXmlVarLinkValue(name, minOccurs, maxOccurs);
224    }
225
226    /**
227     * @see org.opencms.xml.types.A_CmsXmlContentValue#setStringValue(org.opencms.file.CmsObject, java.lang.String)
228     */
229    public void setStringValue(CmsObject cms, String value) throws CmsIllegalArgumentException {
230
231        // element is rebuild from given String value below
232        m_element.clearContent();
233        // link value is re-calculated below
234        m_linkValue = null;
235        // ensure the String value is re-calculated next time it's needed
236        m_stringValue = null;
237        if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
238            // no valid value given
239            return;
240        }
241
242        String path = value;
243        if (cms != null) {
244            String siteRoot = OpenCms.getSiteManager().getSiteRoot(value);
245            String oldSite = cms.getRequestContext().getSiteRoot();
246            try {
247                if (siteRoot != null) {
248                    // only switch the site if needed
249                    cms.getRequestContext().setSiteRoot(siteRoot);
250                    // remove the site root, because the link manager call will append it anyway
251                    path = cms.getRequestContext().removeSiteRoot(value);
252                }
253                // remove parameters, if not the link manager call might fail
254                String query = "";
255                int pos = path.indexOf(CmsRequestUtil.URL_DELIMITER);
256                int anchorPos = path.indexOf('#');
257                if ((pos == -1) || ((anchorPos > -1) && (pos > anchorPos))) {
258                    pos = anchorPos;
259                }
260                if (pos > -1) {
261                    query = path.substring(pos);
262                    path = path.substring(0, pos);
263                }
264                // get the root path
265                path = OpenCms.getLinkManager().getRootPath(cms, path);
266                if (path != null) {
267                    // append parameters again
268                    path += query;
269                }
270            } finally {
271                if (siteRoot != null) {
272                    cms.getRequestContext().setSiteRoot(oldSite);
273                }
274            }
275        }
276        boolean internal = (path != null);
277        CmsRelationType type;
278        if (internal) {
279            type = getRelationType(getPath());
280        } else {
281            // use original value for external links
282            path = value;
283            // external links are always "weak"
284            type = CmsRelationType.XML_WEAK;
285        }
286        CmsLink link = new CmsLink(TYPE_VAR_LINK, type, path, internal);
287        if (internal) {
288            // link management check for internal links
289            link.checkConsistency(cms);
290        }
291        // update xml node
292        CmsLinkUpdateUtil.updateXmlForHtmlValue(link, null, m_element.addElement(CmsXmlPage.NODE_LINK));
293        // store the calculated link
294        m_linkValue = link;
295    }
296
297    /**
298     * Creates the String value for this VarLink value element.<p>
299     *
300     * @param cms the current users OpenCms context
301     *
302     * @return the String value for this VarLink value element
303     */
304    private String createStringValue(CmsObject cms) {
305
306        Attribute enabled = m_element.attribute(CmsXmlPage.ATTRIBUTE_ENABLED);
307
308        String content = "";
309        if ((enabled == null) || Boolean.valueOf(enabled.getText()).booleanValue()) {
310            CmsLink link = getLink(cms);
311            if (link != null) {
312                content = link.getUri();
313                if (link.isInternal() && (cms != null)) {
314                    // remove site root for internal links
315                    content = cms.getRequestContext().removeSiteRoot(link.getUri());
316                }
317            }
318        }
319        return content;
320    }
321}