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.json.JSONException; 032import org.opencms.main.CmsException; 033import org.opencms.main.CmsIllegalArgumentException; 034import org.opencms.main.CmsLog; 035import org.opencms.main.CmsRuntimeException; 036import org.opencms.main.OpenCms; 037import org.opencms.relations.CmsLink; 038import org.opencms.relations.CmsLinkUpdateUtil; 039import org.opencms.relations.CmsRelationType; 040import org.opencms.util.CmsRequestUtil; 041import org.opencms.util.CmsStringUtil; 042import org.opencms.xml.I_CmsXmlDocument; 043import org.opencms.xml.page.CmsXmlPage; 044import org.opencms.xml.xml2json.I_CmsJsonFormattableValue; 045import org.opencms.xml.xml2json.renderer.CmsJsonRendererXmlContent; 046 047import java.util.Locale; 048 049import org.apache.commons.logging.Log; 050 051import org.dom4j.Attribute; 052import org.dom4j.Element; 053 054/** 055 * Describes the XML content type "OpenCmsVarLink".<p> 056 * 057 * This type allows a link to either an internal VFS resource, or to an external website.<p> 058 * 059 * @since 7.0.0 060 */ 061public class CmsXmlVarLinkValue extends A_CmsXmlContentValue implements I_CmsJsonFormattableValue { 062 063 /** Logger instance for this class. */ 064 private static final Log LOG = CmsLog.getLog(CmsXmlVarLinkValue.class); 065 066 /** Value to mark that no link is defined, "none". */ 067 public static final String NO_LINK = "none"; 068 069 /** The name of this type as used in the XML schema. */ 070 public static final String TYPE_NAME = "OpenCmsVarLink"; 071 072 /** The var link type constant. */ 073 public static final String TYPE_VAR_LINK = "varLink"; 074 075 /** The schema definition String is located in a text for easier editing. */ 076 private static String m_schemaDefinition; 077 078 /** The link object represented by this object. */ 079 private CmsLink m_linkValue; 080 081 /** The String value of the element node. */ 082 private String m_stringValue; 083 084 /** 085 * Creates a new, empty schema type descriptor of type "OpenCmsVfsFile".<p> 086 */ 087 public CmsXmlVarLinkValue() { 088 089 // empty constructor is required for class registration 090 } 091 092 /** 093 * Creates a new XML content value of type "OpenCmsVfsFile".<p> 094 * 095 * @param document the XML content instance this value belongs to 096 * @param element the XML element that contains this value 097 * @param locale the locale this value is created for 098 * @param type the type instance to create the value for 099 */ 100 public CmsXmlVarLinkValue(I_CmsXmlDocument document, Element element, Locale locale, I_CmsXmlSchemaType type) { 101 102 super(document, element, locale, type); 103 } 104 105 /** 106 * Creates a new schema type descriptor for the type "OpenCmsVfsFile".<p> 107 * 108 * @param name the name of the XML node containing the value according to the XML schema 109 * @param minOccurs minimum number of occurrences of this type according to the XML schema 110 * @param maxOccurs maximum number of occurrences of this type according to the XML schema 111 */ 112 public CmsXmlVarLinkValue(String name, String minOccurs, String maxOccurs) { 113 114 super(name, minOccurs, maxOccurs); 115 } 116 117 /** 118 * Converts the value for a VarLink field to the form "root path + query string" if it's an internal link, and returns null if it's an external link. 119 * 120 * @param cms the CMS context 121 * @param value the value to convert 122 * @return the root path with the query if value is an internal link, and null otherwise 123 */ 124 public static String getInternalPathAndQuery(CmsObject cms, String value) { 125 126 String path = value; 127 if (cms != null) { 128 String siteRoot = OpenCms.getSiteManager().getSiteRoot(value); 129 String oldSite = cms.getRequestContext().getSiteRoot(); 130 try { 131 if (siteRoot != null) { 132 // only switch the site if needed 133 cms.getRequestContext().setSiteRoot(siteRoot); 134 // remove the site root, because the link manager call will append it anyway 135 path = cms.getRequestContext().removeSiteRoot(value); 136 } 137 // remove parameters, if not the link manager call might fail 138 String query = ""; 139 int pos = path.indexOf(CmsRequestUtil.URL_DELIMITER); 140 int anchorPos = path.indexOf('#'); 141 if ((pos == -1) || ((anchorPos > -1) && (pos > anchorPos))) { 142 pos = anchorPos; 143 } 144 if (pos > -1) { 145 query = path.substring(pos); 146 path = path.substring(0, pos); 147 } 148 // get the root path 149 path = OpenCms.getLinkManager().getRootPath(cms, path); 150 if (path != null) { 151 // append parameters again 152 path += query; 153 } 154 } finally { 155 if (siteRoot != null) { 156 cms.getRequestContext().setSiteRoot(oldSite); 157 } 158 } 159 } 160 return path; 161 } 162 163 /** 164 * @see org.opencms.xml.types.A_CmsXmlContentValue#createValue(I_CmsXmlDocument, org.dom4j.Element, Locale) 165 */ 166 public I_CmsXmlContentValue createValue(I_CmsXmlDocument document, Element element, Locale locale) { 167 168 return new CmsXmlVarLinkValue(document, element, locale, this); 169 } 170 171 /** 172 * @see org.opencms.xml.types.I_CmsXmlSchemaType#generateXml(org.opencms.file.CmsObject, org.opencms.xml.I_CmsXmlDocument, org.dom4j.Element, java.util.Locale) 173 */ 174 @Override 175 public Element generateXml(CmsObject cms, I_CmsXmlDocument document, Element root, Locale locale) { 176 177 Element element = root.addElement(getName()); 178 179 // get the default value from the content handler 180 String defaultValue = document.getHandler().getDefault(cms, this, locale); 181 if (defaultValue != null) { 182 I_CmsXmlContentValue value = createValue(document, element, locale); 183 value.setStringValue(cms, defaultValue); 184 } 185 return element; 186 } 187 188 /** 189 * Returns the link object represented by this XML content value.<p> 190 * 191 * @param cms the cms context, can be <code>null</code> but in this case no link check is performed, 192 * and the target is marked as "external" 193 * 194 * @return the link object represented by this XML content value 195 */ 196 public CmsLink getLink(CmsObject cms) { 197 198 if (m_linkValue == null) { 199 // need to to calculate link value twice 200 Element linkElement = m_element.element(CmsXmlPage.NODE_LINK); 201 if (linkElement == null) { 202 setStringValue(cms, m_element.getText()); 203 } else { 204 CmsLinkUpdateUtil.updateType(linkElement, getRelationType(getPath())); 205 CmsLink link = new CmsLink(linkElement); 206 if (link.isInternal()) { 207 // link management check 208 link.checkConsistency(cms); 209 } 210 if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) { 211 // this may just be an anchor link 212 m_linkValue = CmsLink.NULL_LINK; 213 } else { 214 m_linkValue = link; 215 } 216 } 217 } 218 if (m_linkValue == CmsLink.NULL_LINK) { 219 return null; 220 } 221 return m_linkValue; 222 } 223 224 /** 225 * @see org.opencms.xml.types.I_CmsXmlContentValue#getPlainText(org.opencms.file.CmsObject) 226 */ 227 @Override 228 public String getPlainText(CmsObject cms) { 229 230 return getStringValue(cms); 231 } 232 233 /** 234 * @see org.opencms.xml.types.I_CmsXmlSchemaType#getSchemaDefinition() 235 */ 236 public String getSchemaDefinition() { 237 238 // the schema definition is located in a separate file for easier editing 239 if (m_schemaDefinition == null) { 240 m_schemaDefinition = readSchemaDefinition("org/opencms/xml/types/XmlVarLinkValue.xsd"); 241 } 242 return m_schemaDefinition; 243 } 244 245 /** 246 * @see org.opencms.xml.types.I_CmsXmlContentValue#getStringValue(CmsObject) 247 */ 248 public String getStringValue(CmsObject cms) throws CmsRuntimeException { 249 250 if (m_stringValue == null) { 251 m_stringValue = createStringValue(cms); 252 } 253 return m_stringValue; 254 } 255 256 /** 257 * @see org.opencms.xml.types.A_CmsXmlContentValue#getTypeName() 258 */ 259 public String getTypeName() { 260 261 return TYPE_NAME; 262 } 263 264 /** 265 * @see org.opencms.xml.types.A_CmsXmlContentValue#isSearchable() 266 */ 267 @Override 268 public boolean isSearchable() { 269 270 // there is no point in searching link values 271 return false; 272 } 273 274 /** 275 * @see org.opencms.xml.types.A_CmsXmlContentValue#newInstance(java.lang.String, java.lang.String, java.lang.String) 276 */ 277 public I_CmsXmlSchemaType newInstance(String name, String minOccurs, String maxOccurs) { 278 279 return new CmsXmlVarLinkValue(name, minOccurs, maxOccurs); 280 } 281 282 /** 283 * @see org.opencms.xml.types.A_CmsXmlContentValue#setStringValue(org.opencms.file.CmsObject, java.lang.String) 284 */ 285 public void setStringValue(CmsObject cms, String value) throws CmsIllegalArgumentException { 286 287 // element is rebuild from given String value below 288 m_element.clearContent(); 289 // link value is re-calculated below 290 m_linkValue = null; 291 // ensure the String value is re-calculated next time it's needed 292 m_stringValue = null; 293 if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) { 294 // no valid value given 295 return; 296 } 297 298 String path = getInternalPathAndQuery(cms, value); 299 boolean internal = (path != null); 300 CmsRelationType type; 301 if (internal) { 302 type = getRelationType(getPath()); 303 } else { 304 // use original value for external links 305 path = value; 306 // external links are always "weak" 307 type = CmsRelationType.XML_WEAK; 308 } 309 CmsLink link = new CmsLink(TYPE_VAR_LINK, type, path, internal); 310 if (internal) { 311 // link management check for internal links 312 link.checkConsistency(cms); 313 } 314 // update xml node 315 CmsLinkUpdateUtil.updateXmlForHtmlValue(link, null, m_element.addElement(CmsXmlPage.NODE_LINK)); 316 // store the calculated link 317 m_linkValue = link; 318 } 319 320 /** 321 * @see org.opencms.xml.xml2json.I_CmsJsonFormattableValue#toJson(org.opencms.file.CmsObject) 322 */ 323 public Object toJson(CmsObject cms) { 324 325 try { 326 CmsXmlVarLinkValue linkValue = this; 327 // Use the CmsObject with current site for the link, so that the resulting link 328 // works in the context (i.e. domain) of the original JSON handler request, 329 // but CmsObject with site set to root site so we get the root path, which 330 // can then be used to construct further JSON handler URLs. 331 CmsLink linkObj = linkValue.getLink(cms); 332 String link = null; 333 if (linkObj != null) { 334 link = linkObj.getLink(cms); 335 } 336 CmsObject rootCms = OpenCms.initCmsObject(cms); 337 String path = linkValue.getStringValue(rootCms); 338 if (path.startsWith("http")) { 339 // external link 340 path = null; 341 } 342 return CmsJsonRendererXmlContent.linkAndPath(link, path, cms); 343 } catch (JSONException e) { 344 return null; 345 } catch (CmsException e) { 346 LOG.error(e.getLocalizedMessage(), e); 347 return null; 348 } 349 350 } 351 352 /** 353 * Creates the String value for this VarLink value element.<p> 354 * 355 * @param cms the current users OpenCms context 356 * 357 * @return the String value for this VarLink value element 358 */ 359 private String createStringValue(CmsObject cms) { 360 361 Attribute enabled = m_element.attribute(CmsXmlPage.ATTRIBUTE_ENABLED); 362 363 String content = ""; 364 if ((enabled == null) || Boolean.valueOf(enabled.getText()).booleanValue()) { 365 CmsLink link = getLink(cms); 366 if (link != null) { 367 content = link.getUri(); 368 if (link.isInternal() && (cms != null)) { 369 // remove site root for internal links 370 content = cms.getRequestContext().removeSiteRoot(link.getUri()); 371 } 372 } 373 } 374 return content; 375 } 376}