All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.relations.CmsLink Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.relations;

import org.opencms.file.CmsObject;
import org.opencms.file.CmsRequestContext;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.wrapper.CmsObjectWrapper;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.CmsStaticResourceHandler;
import org.opencms.main.OpenCms;
import org.opencms.staticexport.CmsLinkProcessor;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.util.CmsUriSplitter;

import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;

import org.dom4j.Attribute;
import org.dom4j.Element;

/**
 * A single link entry in the link table.

* * @since 6.0.0 */ public class CmsLink { /** Name of the internal attribute of the link node. */ public static final String ATTRIBUTE_INTERNAL = "internal"; /** Name of the name attribute of the elements node. */ public static final String ATTRIBUTE_NAME = "name"; /** Name of the type attribute of the elements node. */ public static final String ATTRIBUTE_TYPE = "type"; /** Default link name. */ public static final String DEFAULT_NAME = "ref"; /** Default link type. */ public static final CmsRelationType DEFAULT_TYPE = CmsRelationType.XML_WEAK; /** A dummy uri. */ public static final String DUMMY_URI = "@@@"; /** Name of the anchor node. */ public static final String NODE_ANCHOR = "anchor"; /** Name of the query node. */ public static final String NODE_QUERY = "query"; /** Name of the target node. */ public static final String NODE_TARGET = "target"; /** Name of the UUID node. */ public static final String NODE_UUID = "uuid"; /** Constant for the NULL link. */ public static final CmsLink NULL_LINK = new CmsLink(); /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsLink.class); /** request context attribute to pass in a custom link renderer. */ public static final String CUSTOM_LINK_HANDLER = "CmsLink.customLinkHandler"; /** The anchor of the URI, if any. */ private String m_anchor; /** The XML element reference. */ private Element m_element; /** Indicates if the link is an internal link within the OpenCms VFS. */ private boolean m_internal; /** The internal name of the link. */ private String m_name; /** The parameters of the query, if any. */ private Map m_parameters; /** The query, if any. */ private String m_query; /** The site root of the (internal) link. */ private String m_siteRoot; /** The structure id of the linked resource. */ private CmsUUID m_structureId; /** The link target (destination). */ private String m_target; /** The type of the link. */ private CmsRelationType m_type; /** The raw uri. */ private String m_uri; /** The resource the link points to. */ private CmsResource m_resource; /** * Creates a new link from the given link info bean. * * @param linkInfo the link info bean */ public CmsLink(CmsLinkInfo linkInfo) { m_name = DEFAULT_NAME; m_type = linkInfo.getType(); m_internal = linkInfo.isInternal(); m_structureId = linkInfo.getStructureId(); m_target = linkInfo.getTarget(); m_anchor = linkInfo.getAnchor(); m_query = linkInfo.getQuery(); setUri(); } /** * Reconstructs a link object from the given XML node.

* * @param element the XML node containing the link information */ public CmsLink(Element element) { m_element = element; Attribute attrName = element.attribute(ATTRIBUTE_NAME); if (attrName != null) { m_name = attrName.getValue(); } else { m_name = DEFAULT_NAME; } Attribute attrType = element.attribute(ATTRIBUTE_TYPE); if (attrType != null) { m_type = CmsRelationType.valueOfXml(attrType.getValue()); } else { m_type = DEFAULT_TYPE; } Attribute attrInternal = element.attribute(ATTRIBUTE_INTERNAL); if (attrInternal != null) { m_internal = Boolean.valueOf(attrInternal.getValue()).booleanValue(); } else { m_internal = true; } Element uuid = element.element(NODE_UUID); Element target = element.element(NODE_TARGET); Element anchor = element.element(NODE_ANCHOR); Element query = element.element(NODE_QUERY); m_structureId = (uuid != null) ? new CmsUUID(uuid.getText()) : null; m_target = (target != null) ? target.getText() : null; m_anchor = (anchor != null) ? anchor.getText() : null; setQuery((query != null) ? query.getText() : null); // update the uri from the components setUri(); } /** * Creates a new link object without a reference to the xml page link element.

* * @param name the internal name of this link * @param type the type of this link * @param structureId the structure id of the link * @param uri the link uri * @param internal indicates if the link is internal within OpenCms */ public CmsLink(String name, CmsRelationType type, CmsUUID structureId, String uri, boolean internal) { m_element = null; m_name = name; m_type = type; m_internal = internal; m_structureId = structureId; m_uri = uri; // update component members from the uri setComponents(); } /** * Creates a new link object without a reference to the xml page link element.

* * @param name the internal name of this link * @param type the type of this link * @param uri the link uri * @param internal indicates if the link is internal within OpenCms */ public CmsLink(String name, CmsRelationType type, String uri, boolean internal) { this(name, type, null, uri, internal); } /** * Empty constructor for NULL constant.

*/ private CmsLink() { // empty constructor for NULL constant } /** * Checks and updates the structure id or the path of the target.

* * @param cms the cms context */ public void checkConsistency(CmsObject cms) { if (!m_internal || (cms == null)) { return; } // in case of static resource links use the null UUID if (CmsStaticResourceHandler.isStaticResourceUri(m_target)) { m_structureId = CmsUUID.getNullUUID(); return; } try { if (m_structureId == null) { // try by path throw new CmsException(Messages.get().container(Messages.LOG_BROKEN_LINK_NO_ID_0)); } // first look for the resource with the given structure id String rootPath = null; CmsResource res; try { res = cms.readResource(m_structureId, CmsResourceFilter.ALL); m_resource = res; rootPath = res.getRootPath(); if (!res.getRootPath().equals(m_target)) { // update path if needed if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_BROKEN_LINK_UPDATED_BY_ID_3, m_structureId, m_target, res.getRootPath())); } } } catch (CmsException e) { // not found throw new CmsVfsResourceNotFoundException( org.opencms.db.generic.Messages.get().container( org.opencms.db.generic.Messages.ERR_READ_RESOURCE_1, m_target), e); } if ((rootPath != null) && !rootPath.equals(m_target)) { // set the new target m_target = res.getRootPath(); setUri(); // update xml node CmsLinkUpdateUtil.updateXml(this, m_element, true); } } catch (CmsException e) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_BROKEN_LINK_BY_ID_2, m_target, m_structureId), e); } if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_target)) { // no correction is possible return; } // go on with the resource with the given path String siteRoot = cms.getRequestContext().getSiteRoot(); try { cms.getRequestContext().setSiteRoot(""); // now look for the resource with the given path CmsResource res = cms.readResource(m_target, CmsResourceFilter.ALL); m_resource = res; if (!res.getStructureId().equals(m_structureId)) { // update structure id if needed if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_BROKEN_LINK_UPDATED_BY_NAME_3, m_target, m_structureId, res.getStructureId())); } m_target = res.getRootPath(); // could change by a translation rule m_structureId = res.getStructureId(); CmsLinkUpdateUtil.updateXml(this, m_element, true); } } catch (CmsException e1) { // no correction was possible if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_BROKEN_LINK_BY_NAME_1, m_target), e1); } m_structureId = null; } finally { cms.getRequestContext().setSiteRoot(siteRoot); } } } /** * A link is considered equal if the link target and the link type is equal.

* * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof CmsLink) { CmsLink other = (CmsLink)obj; return (m_type == other.m_type) && CmsStringUtil.isEqual(m_target, other.m_target); } return false; } /** * Returns the anchor of this link.

* * @return the anchor or null if undefined */ public String getAnchor() { return m_anchor; } /** * Returns the xml node element representing this link object.

* * @return the xml node element representing this link object */ public Element getElement() { return m_element; } /** * Returns the processed link.

* * @param cms the current OpenCms user context, can be null * * @return the processed link */ public String getLink(CmsObject cms) { if (m_internal) { // if we have a local link, leave it unchanged // cms may be null for unit tests if ((cms == null) || (m_uri.length() == 0) || (m_uri.charAt(0) == '#')) { return m_uri; } I_CmsCustomLinkRenderer handler = (I_CmsCustomLinkRenderer)cms.getRequestContext().getAttribute( CmsLink.CUSTOM_LINK_HANDLER); if (handler != null) { String handlerResult = handler.getLink(cms, this); if (handlerResult != null) { return handlerResult; } } checkConsistency(cms); String target = m_target; String uri = computeUri(target, m_query, m_anchor); CmsObjectWrapper wrapper = (CmsObjectWrapper)cms.getRequestContext().getAttribute( CmsObjectWrapper.ATTRIBUTE_NAME); if (wrapper != null) { // if an object wrapper is used, rewrite the URI m_uri = wrapper.rewriteLink(m_uri); uri = wrapper.rewriteLink(uri); } if ((cms.getRequestContext().getSiteRoot().length() == 0) && (cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_EDITOR) == null)) { // Explanation why this check is required: // If the site root name length is 0, this means that a user has switched // the site root to the root site "/" in the Workplace. // In this case the workplace site must also be the active site. // If the editor is opened in the root site, because of this code the links are // always generated _with_ server name / port so that the source code looks identical to code // that would normally be created when running in a regular site. // If normal link processing would be used, the site information in the link // would be lost. return OpenCms.getLinkManager().substituteLink(cms, uri); } // get the site root for this URI / link // if there is no site root, we either have a /system link, or the site was deleted, // return the full URI prefixed with the opencms context String siteRoot = getSiteRoot(); if (siteRoot == null) { return OpenCms.getLinkManager().substituteLink(cms, uri); } if (cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_FULLLINKS) != null) { // full links should be generated even if we are in the same site return OpenCms.getLinkManager().getServerLink(cms, uri); } // return the link with the server prefix, if necessary return OpenCms.getLinkManager().substituteLink(cms, getSitePath(uri), siteRoot); } else { // don't touch external links return m_uri; } } /** * Returns the processed link.

* * @param cms the current OpenCms user context, can be null * @param processEditorLinks this parameter is not longer used * * @return the processed link * * @deprecated use {@link #getLink(CmsObject)} instead, * the process editor option is set using the OpenCms request context attributes */ @Deprecated public String getLink(CmsObject cms, boolean processEditorLinks) { return getLink(cms); } /** * Returns the macro name of this link.

* * @return the macro name name of this link */ public String getName() { return m_name; } /** * Returns the first parameter value for the given parameter name.

* * @param name the name of the parameter * @return the first value for this name or null */ public String getParameter(String name) { String[] p = getParameterMap().get(name); if (p != null) { return p[0]; } return null; } /** * Returns the map of parameters of this link.

* * @return the map of parameters */ public Map getParameterMap() { if (m_parameters == null) { m_parameters = CmsRequestUtil.createParameterMap(m_query); } return m_parameters; } /** * Returns the set of available parameter names for this link.

* * @return the parameter names */ public Set getParameterNames() { return getParameterMap().keySet(); } /** * Returns all parameter values for the given name.

* * @param name the name of the parameter * * @return all parameter values or null */ public String[] getParameterValues(String name) { return getParameterMap().get(name); } /** * Returns the query of this link.

* * @return the query or null if undefined */ public String getQuery() { return m_query; } /** * Returns the resource this link points to, if it is an internal link and has already been initialized via checkConsistency. * *

Returns null otherwise. * * @return the resource this link points to */ public CmsResource getResource() { return m_resource; } /** * Returns the vfs link of the target if it is internal.

* * @return the full link destination or null if the link is not internal * * @deprecated use {@link #getSitePath(CmsObject)} instead */ @Deprecated public String getSitePath() { return getSitePath(m_uri); } /** * Returns the path of the link target relative to the current site.

* * @param cms the CMS context * * @return the site path */ public String getSitePath(CmsObject cms) { return cms.getRequestContext().removeSiteRoot(m_uri); } /** * Return the site root if the target of this link is internal, or null otherwise.

* * @return the site root if the target of this link is internal, or null otherwise */ public String getSiteRoot() { if (m_internal && (m_siteRoot == null)) { m_siteRoot = OpenCms.getSiteManager().getSiteRoot(m_target); if (m_siteRoot == null) { m_siteRoot = ""; } } return m_siteRoot; } /** * The structure id of the linked resource.

* * @return structure id of the linked resource */ public CmsUUID getStructureId() { return m_structureId; } /** * Returns the target (destination) of this link.

* * @return the target the target (destination) of this link */ public String getTarget() { return m_target; } /** * Gets the target with the query appended, if there is one. * * @return the target with the query */ public String getTargetWithQuery() { return getTarget() + (getQuery() != null ? "?" + getQuery() : ""); } /** * Returns the type of this link.

* * @return the type of this link */ public CmsRelationType getType() { return m_type; } /** * Returns the raw uri of this link.

* * @return the uri */ public String getUri() { return m_uri; } /** * Returns the vfs link of the target if it is internal.

* * @return the full link destination or null if the link is not internal * * @deprecated Use {@link #getSitePath()} instead */ @Deprecated public String getVfsUri() { return getSitePath(); } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int result = m_type.hashCode(); if (m_target != null) { result += m_target.hashCode(); } return result; } /** * Returns if the link is internal.

* * @return true if the link is a local link */ public boolean isInternal() { return m_internal; } /** * Converts this link to a link info object. * * @return the link info object */ public CmsLinkInfo toLinkInfo() { return new CmsLinkInfo(m_structureId, m_target, m_query, m_anchor, m_type, m_internal); } /** * @see java.lang.Object#toString() */ @Override public String toString() { return m_uri; } /** * Updates the uri of this link with a new value.

* * Also updates the structure of the underlying XML page document this link belongs to.

* * Note that you can not update the "internal" or "type" values of the link, * so the new link must be of same type (A, IMG) and also remain either an internal or external link.

* * @param uri the uri to update this link with scheme://authority/path#anchor?query */ public void updateLink(String uri) { // set the uri m_uri = uri; // update the components setComponents(); // update the xml CmsLinkUpdateUtil.updateXml(this, m_element, true); } /** * Updates the uri of this link with a new target, anchor and query.

* * If anchor and/or query are null, this features are not used.

* * Note that you can not update the "internal" or "type" values of the link, * so the new link must be of same type (A, IMG) and also remain either an internal or external link.

* * Also updates the structure of the underlying XML page document this link belongs to.

* * @param target the target (destination) of this link * @param anchor the anchor or null if undefined * @param query the query or null if undefined */ public void updateLink(String target, String anchor, String query) { // set the components m_target = target; m_anchor = anchor; setQuery(query); // create the uri from the components setUri(); // update the xml CmsLinkUpdateUtil.updateXml(this, m_element, true); } /** * Helper method for getting the site path for a uri.

* * @param uri a VFS uri * @return the site path */ protected String getSitePath(String uri) { if (m_internal) { String siteRoot = getSiteRoot(); if (siteRoot != null) { return uri.substring(siteRoot.length()); } else { return uri; } } return null; } /** * Helper method for creating a uri from its components.

* * @param target the uri target * @param query the uri query component * @param anchor the uri anchor component * * @return the uri */ private String computeUri(String target, String query, String anchor) { StringBuffer uri = new StringBuffer(64); uri.append(target); if (query != null) { uri.append('?'); uri.append(query); } if (anchor != null) { uri.append('#'); uri.append(anchor); } return uri.toString(); } /** * Sets the component member variables (target, anchor, query) * by splitting the uri scheme://authority/path#anchor?query.

*/ private void setComponents() { CmsUriSplitter splitter = new CmsUriSplitter(m_uri, true); m_target = splitter.getPrefix(); m_anchor = CmsLinkProcessor.unescapeLink(splitter.getAnchor()); setQuery(splitter.getQuery()); } /** * Sets the query of the link.

* * @param query the query to set. */ private void setQuery(String query) { m_query = CmsLinkProcessor.unescapeLink(query); m_parameters = null; } /** * Joins the internal target, anchor and query components * to one uri string, setting the internal uri and parameters fields.

*/ private void setUri() { m_uri = computeUri(m_target, m_query, m_anchor); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy