org.opencms.xml.content.CmsDefaultXmlContentHandler Maven / Gradle / Ivy
Show all versions of opencms-test Show documentation
/*
* 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.xml.content;
import org.opencms.configuration.CmsConfigurationManager;
import org.opencms.db.log.CmsLogEntry;
import org.opencms.file.CmsDataAccessException;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsGroup;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsUser;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.i18n.CmsEncoder;
import org.opencms.i18n.CmsListResourceBundle;
import org.opencms.i18n.CmsLocaleManager;
import org.opencms.i18n.CmsMessages;
import org.opencms.i18n.CmsMultiMessages;
import org.opencms.i18n.CmsMultiMessages.I_KeyFallbackHandler;
import org.opencms.i18n.CmsResourceBundleLoader;
import org.opencms.lock.CmsLock;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.CmsRuntimeException;
import org.opencms.main.CmsStaticResourceHandler;
import org.opencms.main.OpenCms;
import org.opencms.relations.CmsCategory;
import org.opencms.relations.CmsCategoryService;
import org.opencms.relations.CmsLink;
import org.opencms.relations.CmsRelationType;
import org.opencms.search.fields.CmsSearchField;
import org.opencms.search.fields.CmsSearchFieldMapping;
import org.opencms.search.fields.CmsSearchFieldMappingType;
import org.opencms.search.fields.I_CmsSearchFieldMapping;
import org.opencms.search.galleries.CmsGalleryNameMacroResolver;
import org.opencms.search.solr.CmsSolrField;
import org.opencms.security.CmsAccessControlEntry;
import org.opencms.security.CmsPrincipal;
import org.opencms.security.CmsRole;
import org.opencms.security.I_CmsPrincipal;
import org.opencms.site.CmsSite;
import org.opencms.util.CmsDefaultSet;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsHtmlConverter;
import org.opencms.util.CmsMacroResolver;
import org.opencms.util.CmsStringUtil;
import org.opencms.widgets.CmsCategoryWidget;
import org.opencms.widgets.CmsDisplayWidget;
import org.opencms.widgets.I_CmsComplexWidget;
import org.opencms.widgets.I_CmsWidget;
import org.opencms.workplace.CmsWorkplace;
import org.opencms.workplace.editors.CmsXmlContentWidgetVisitor;
import org.opencms.xml.CmsXmlContentDefinition;
import org.opencms.xml.CmsXmlEntityResolver;
import org.opencms.xml.CmsXmlException;
import org.opencms.xml.CmsXmlGenericWrapper;
import org.opencms.xml.CmsXmlUtils;
import org.opencms.xml.containerpage.CmsFormatterBean;
import org.opencms.xml.containerpage.CmsFormatterConfiguration;
import org.opencms.xml.containerpage.CmsSchemaFormatterBeanWrapper;
import org.opencms.xml.containerpage.I_CmsFormatterBean;
import org.opencms.xml.types.CmsXmlDynamicCategoryValue;
import org.opencms.xml.types.CmsXmlNestedContentDefinition;
import org.opencms.xml.types.CmsXmlVarLinkValue;
import org.opencms.xml.types.CmsXmlVfsFileValue;
import org.opencms.xml.types.I_CmsXmlContentValue;
import org.opencms.xml.types.I_CmsXmlSchemaType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* Default implementation for the XML content handler, will be used by all XML contents that do not
* provide their own handler.
*
* @since 6.0.0
*/
public class CmsDefaultXmlContentHandler implements I_CmsXmlContentHandler, I_CmsXmlContentVisibilityHandler {
/**
* Contains the visibility handler configuration for a content field path.
*/
protected static class VisibilityConfiguration {
/** The handler instance. */
private I_CmsXmlContentVisibilityHandler m_handler;
/** The handler configuration parameters. */
private String m_params;
/**
* Constructor.
*
* @param handler the handler instance
* @param params the handler configuration parameteres
*/
protected VisibilityConfiguration(I_CmsXmlContentVisibilityHandler handler, String params) {
m_handler = handler;
m_params = params;
}
/**
* Returns the visibility handler instance.
*
* @return the handler instance
*/
public I_CmsXmlContentVisibilityHandler getHandler() {
return m_handler;
}
/**
* Returns the visibility handler configuration parameters.
*
* @return the configuration parameters
*/
public String getParams() {
return m_params;
}
}
/** Constant for the "appinfo" element name itself. */
public static final String APPINFO_APPINFO = "appinfo";
/** Constant for the "addto" appinfo attribute name. */
public static final String APPINFO_ATTR_ADD_TO = "addto";
/** Constant for the "boost" appinfo attribute name. */
public static final String APPINFO_ATTR_BOOST = "boost";
/** Constant for the "class" appinfo attribute name. */
public static final String APPINFO_ATTR_CLASS = "class";
/** Constant for the "collapse" appinfo attribute name. */
public static final String APPINFO_ATTR_COLLAPSE = "collapse";
/** Constant for the "configuration" appinfo attribute name. */
public static final String APPINFO_ATTR_CONFIGURATION = "configuration";
/** The exclude from index attribute. */
public static final String APPINFO_ATTR_CONTAINER_PAGE_ONLY = "containerPageOnly";
/** Constant for the "copyfields" appinfo attribute name. */
public static final String APPINFO_ATTR_COPY_FIELDS = "copyfields";
/** Constant for the "default" appinfo attribute name. */
public static final String APPINFO_ATTR_DEFAULT = "default";
/** Constant for the "description" appinfo attribute name. */
public static final String APPINFO_ATTR_DESCRIPTION = "description";
/** Constant for the "displaycompact" appinfo attribute name. */
public static final String APPINFO_ATTR_DISPLAY = "display";
/** Constant for the "element" appinfo attribute name. */
public static final String APPINFO_ATTR_ELEMENT = "element";
/** Constant for the "error" appinfo attribute name. */
public static final String APPINFO_ATTR_ERROR = "error";
/** Constant for the "invalidate" appinfo attribute name. */
public static final String APPINFO_ATTR_INVALIDATE = "invalidate";
/** Constant for the "key" appinfo attribute name. */
public static final String APPINFO_ATTR_KEY = "key";
/** Constant for the "locale" appinfo attribute name. */
public static final String APPINFO_ATTR_LOCALE = "locale";
/** Constant for the "mapping" appinfo attribute name. */
public static final String APPINFO_ATTR_MAPPING = "mapping";
/** Constant for the "mapto" appinfo attribute name. */
public static final String APPINFO_ATTR_MAPTO = "mapto";
/** Constant for the "maxwidth" appinfo attribute name. */
public static final String APPINFO_ATTR_MAXWIDTH = "maxwidth";
/** Constant for the "message" appinfo attribute name. */
public static final String APPINFO_ATTR_MESSAGE = "message";
/** Constant for the "minwidth" appinfo attribute name. */
public static final String APPINFO_ATTR_MINWIDTH = "minwidth";
/** Constant for the "name" appinfo attribute name. */
public static final String APPINFO_ATTR_NAME = "name";
/** Constant for the "nice-name" appinfo attribute name. */
public static final String APPINFO_ATTR_NICE_NAME = "nice-name";
/** Constant for the "params" appinfo attribute name. */
public static final String APPINFO_ATTR_PARAMS = "params";
/** Constant for the "preview" appinfo attribute name. */
public static final String APPINFO_ATTR_PREVIEW = "preview";
/** Constant for the "regex" appinfo attribute name. */
public static final String APPINFO_ATTR_REGEX = "regex";
/** Constant for the "resolveMacros" attribute name. */
public static final String APPINFO_ATTR_RESOLVE_MACROS = "resolveMacros";
/** Constant for the "rule-regex" appinfo attribute name. */
public static final String APPINFO_ATTR_RULE_REGEX = "rule-regex";
/** Constant for the "rule-type" appinfo attribute name. */
public static final String APPINFO_ATTR_RULE_TYPE = "rule-type";
/** Constant for the "scope" appinfo attribute name. */
public static final String APPINFO_ATTR_SCOPE = "scope";
/** Constant for the "searchcontent" appinfo attribute name. */
public static final String APPINFO_ATTR_SEARCHCONTENT = "searchcontent";
/** Constant for the "select-inherit" appinfo attribute name. */
public static final String APPINFO_ATTR_SELECT_INHERIT = "select-inherit";
/** Constant for the "sourcefield" appinfo attribute name. */
public static final String APPINFO_ATTR_SOURCE_FIELD = "sourcefield";
/** Constant for the "targetfield" appinfo attribute name. */
public static final String APPINFO_ATTR_TARGET_FIELD = "targetfield";
/** Constant for the "type" appinfo attribute name. */
public static final String APPINFO_ATTR_TYPE = "type";
/** Constant for the "node" appinfo attribute value. */
public static final String APPINFO_ATTR_TYPE_NODE = "node";
/** Constant for the "parent" appinfo attribute value. */
public static final String APPINFO_ATTR_TYPE_PARENT = "parent";
/** Constant for the "warning" appinfo attribute value. */
public static final String APPINFO_ATTR_TYPE_WARNING = "warning";
/** Constant for the "uri" appinfo attribute name. */
public static final String APPINFO_ATTR_URI = "uri";
/** Constant for the "useall" appinfo attribute name. */
public static final String APPINFO_ATTR_USEALL = "useall";
/** Constant for the "value" appinfo attribute name. */
public static final String APPINFO_ATTR_VALUE = "value";
/** Constant for the "widget" appinfo attribute name. */
public static final String APPINFO_ATTR_WIDGET = "widget";
/** Constant for the "widget-config" appinfo attribute name. */
public static final String APPINFO_ATTR_WIDGET_CONFIG = "widget-config";
/** Constant for formatter include resource type 'CSS'. */
public static final String APPINFO_ATTRIBUTE_TYPE_CSS = "css";
/** Constant for formatter include resource type 'JAVASCRIPT'. */
public static final String APPINFO_ATTRIBUTE_TYPE_JAVASCRIPT = "javascript";
/** Constant for the "bundle" appinfo element name. */
public static final String APPINFO_BUNDLE = "bundle";
/** Constant for the "default" appinfo element name. */
public static final String APPINFO_DEFAULT = "default";
/** Constant for the "defaults" appinfo element name. */
public static final String APPINFO_DEFAULTS = "defaults";
/** Constant for the "editorchangehandler" appinfo element name. */
public static final String APPINFO_EDITOR_CHANGE_HANDLER = "editorchangehandler";
/** Constant for the "editorchangehandlers" appinfo element name. */
public static final String APPINFO_EDITOR_CHANGE_HANDLERS = "editorchangehandlers";
/** Constant for the "forbidden-contexts" appinfo attribute name. */
public static final String APPINFO_FORBIDDEN_CONTEXTS = "forbidden-contexts";
/** Constant for the "formatter" appinfo element name. */
public static final String APPINFO_FORMATTER = "formatter";
/** Constant for the "formatters" appinfo element name. */
public static final String APPINFO_FORMATTERS = "formatters";
/** Constant for the "headinclude" appinfo element name. */
public static final String APPINFO_HEAD_INCLUDE = "headinclude";
/** Constant for the "headincludes" appinfo element name. */
public static final String APPINFO_HEAD_INCLUDES = "headincludes";
/** Constant for the "layout" appinfo element name. */
public static final String APPINFO_LAYOUT = "layout";
/** Constant for the "layouts" appinfo element name. */
public static final String APPINFO_LAYOUTS = "layouts";
/** Constant for the "mapping" appinfo element name. */
public static final String APPINFO_MAPPING = "mapping";
/** Constant for the "mappings" appinfo element name. */
public static final String APPINFO_MAPPINGS = "mappings";
/** Constant for the 'messagekeyhandler' node. */
public static final String APPINFO_MESSAGEKEYHANDLER = "messagekeyhandler";
/** Constant for the "modelfolder" appinfo element name. */
public static final String APPINFO_MODELFOLDER = "modelfolder";
/** Constant for the "preview" appinfo element name. */
public static final String APPINFO_PREVIEW = "preview";
/** Constant for the "propertybundle" appinfo element name. */
public static final String APPINFO_PROPERTYBUNDLE = "propertybundle";
/** Constant for the "relation" appinfo element name. */
public static final String APPINFO_RELATION = "relation";
/** Constant for the "relations" appinfo element name. */
public static final String APPINFO_RELATIONS = "relations";
/** Constant for the "resource" appinfo element name. */
public static final String APPINFO_RESOURCE = "resource";
/** Constant for the "resourcebundle" appinfo element name. */
public static final String APPINFO_RESOURCEBUNDLE = "resourcebundle";
/** Constant for the "resourcebundles" appinfo element name. */
public static final String APPINFO_RESOURCEBUNDLES = "resourcebundles";
/** Constant for the "rule" appinfo element name. */
public static final String APPINFO_RULE = "rule";
/** The file where the default appinfo schema is located. */
public static final String APPINFO_SCHEMA_FILE = "org/opencms/xml/content/DefaultAppinfo.xsd";
/** The file where the default appinfo schema types are located. */
public static final String APPINFO_SCHEMA_FILE_TYPES = "org/opencms/xml/content/DefaultAppinfoTypes.xsd";
/** The XML system id for the default appinfo schema types. */
public static final String APPINFO_SCHEMA_SYSTEM_ID = CmsConfigurationManager.DEFAULT_DTD_PREFIX
+ APPINFO_SCHEMA_FILE;
/** The XML system id for the default appinfo schema types. */
public static final String APPINFO_SCHEMA_TYPES_SYSTEM_ID = CmsConfigurationManager.DEFAULT_DTD_PREFIX
+ APPINFO_SCHEMA_FILE_TYPES;
/** Constant for the "searchsetting" appinfo element name. */
public static final String APPINFO_SEARCHSETTING = "searchsetting";
/** Constant for the "searchsettings" appinfo element name. */
public static final String APPINFO_SEARCHSETTINGS = "searchsettings";
/** Constant for the "setting" appinfo element name. */
public static final String APPINFO_SETTING = "setting";
/** Constant for the "settings" appinfo element name. */
public static final String APPINFO_SETTINGS = "settings";
/** Constant for the "solrfield" appinfo element name. */
public static final String APPINFO_SOLR_FIELD = "solrfield";
/** Constant for the "synchronization" appinfo element name. */
public static final String APPINFO_SYNCHRONIZATION = "synchronization";
/** Constant for the "synchronizations" appinfo element name. */
public static final String APPINFO_SYNCHRONIZATIONS = "synchronizations";
/** Constant for the "tab" appinfo element name. */
public static final String APPINFO_TAB = "tab";
/** Constant for the "tabs" appinfo element name. */
public static final String APPINFO_TABS = "tabs";
/** Node name. */
public static final String APPINFO_TEMPLATE = "template";
/** Node name. */
public static final String APPINFO_TEMPLATES = "templates";
/** Constant for the "validationrule" appinfo element name. */
public static final String APPINFO_VALIDATIONRULE = "validationrule";
/** Constant for the "validationrules" appinfo element name. */
public static final String APPINFO_VALIDATIONRULES = "validationrules";
/** Constant for the "element" value of the appinfo attribute "addto". */
public static final String APPINFO_VALUE_ADD_TO_CONTENT = "element";
/** Constant for the "page" value of the appinfo attribute "addto". */
public static final String APPINFO_VALUE_ADD_TO_PAGE = "page";
/** Constant for the "visibilities" appinfo element name. */
public static final String APPINFO_VISIBILITIES = "visibilities";
/** Constant for the "visibility" appinfo element name. */
public static final String APPINFO_VISIBILITY = "visibility";
/** Constant for the "xmlbundle" appinfo element name. */
public static final String APPINFO_XMLBUNDLE = "xmlbundle";
/** Attribute name. */
public static final String ATTR_ENABLED = "enabled";
/** Attribute name. */
public static final String ATTR_ENABLED_BY_DEFAULT = "enabledByDefault";
/** Attribute name. */
public static final String ATTR_USE_ACACIA = "useAcacia";
/** Constant for head include type attribute: CSS. */
public static final String ATTRIBUTE_INCLUDE_TYPE_CSS = "css";
/** Constant for head include type attribute: java-script. */
public static final String ATTRIBUTE_INCLUDE_TYPE_JAVASCRIPT = "javascript";
/** Macro for resolving the preview URI. */
public static final String MACRO_PREVIEW_TEMPFILE = "previewtempfile";
/** Default message for validation errors. */
protected static final String MESSAGE_VALIDATION_DEFAULT_ERROR = "${validation.path}: "
+ "${key."
+ Messages.GUI_EDITOR_XMLCONTENT_VALIDATION_ERROR_2
+ "|${validation.value}|[${validation.regex}]}";
/** Default message for validation warnings. */
protected static final String MESSAGE_VALIDATION_DEFAULT_WARNING = "${validation.path}: "
+ "${key."
+ Messages.GUI_EDITOR_XMLCONTENT_VALIDATION_WARNING_2
+ "|${validation.value}|[${validation.regex}]}";
/** The attribute name for the "prefer folder" option for properties. */
private static final String APPINFO_ATTR_PREFERFOLDER = "PreferFolder";
/** The 'useDefault' attribute name. */
private static final String APPINFO_ATTR_USE_DEFAULT = "useDefault";
/** The node name for the default complex widget configuration. */
private static final Object APPINFO_DEFAULTWIDGET = "defaultwidget";
/** Attribute name for the context used for resolving content mappings. */
private static final String ATTR_MAPPING_RESOLUTION_CONTEXT = "MAPPING_RESOLUTION_CONTEXT";
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsDefaultXmlContentHandler.class);
/** The principal list separator. */
private static final String PRINCIPAL_LIST_SEPARATOR = ",";
/** The title property individual mapping key. */
private static final String TITLE_PROPERTY_INDIVIDUAL_MAPPING = MAPTO_PROPERTY_INDIVIDUAL
+ CmsPropertyDefinition.PROPERTY_TITLE;
/** The title property mapping key. */
private static final String TITLE_PROPERTY_MAPPING = MAPTO_PROPERTY + CmsPropertyDefinition.PROPERTY_TITLE;
/** The title property shared mapping key. */
private static final String TITLE_PROPERTY_SHARED_MAPPING = MAPTO_PROPERTY_SHARED
+ CmsPropertyDefinition.PROPERTY_TITLE;
/** The set of allowed templates. */
protected CmsDefaultSet m_allowedTemplates = new CmsDefaultSet();
/** A map from attribute name to complex widgets. */
protected Map m_complexWidgets = new HashMap();
/** The configuration values for the element widgets (as defined in the annotations). */
protected Map m_configurationValues;
/** The CSS resources to include into the html-page head. */
protected Set m_cssHeadIncludes;
/** The default values for the elements (as defined in the annotations). */
protected Map m_defaultValues;
/** The element mappings (as defined in the annotations). */
protected Map> m_elementMappings;
/** The widgets used for the elements (as defined in the annotations). */
protected Map m_elementWidgets;
/** The formatter configuration. */
protected CmsFormatterConfiguration m_formatterConfiguration;
/** The list of formatters from the XSD. */
protected List m_formatters;
/** The java-script resources to include into the html-page head. */
protected Set m_jsHeadIncludes;
/** The resource bundle name to be used for localization of this content handler. */
protected List m_messageBundleNames;
/** The folder containing the model file(s) for the content. */
protected String m_modelFolder;
/** The preview location (as defined in the annotations). */
protected String m_previewLocation;
/** The relation check rules. */
protected Map m_relationChecks;
/** The relation check rules. */
protected Map m_relations;
/** The Solr field configurations. */
protected Map m_searchFields;
/** The Solr field configurations added to the container pages contents are on. */
protected Map m_searchFieldsPage;
/** The search settings. */
protected Map m_searchSettings;
/** The configured settings for the formatters (as defined in the annotations). */
protected Map m_settings;
/** The configured locale synchronization elements. */
protected List m_synchronizations;
/** The configured tabs. */
protected List m_tabs;
/** The list of mappings to the "Title" property. */
protected List m_titleMappings;
/** Flag which controls whether the Acacia editor should be disabled for this type. */
protected boolean m_useAcacia = true;
/** The messages for the error validation rules. */
protected Map m_validationErrorMessages;
/** The validation rules that cause an error (as defined in the annotations). */
protected Map m_validationErrorRules;
/** The messages for the warning validation rules. */
protected Map m_validationWarningMessages;
/** The validation rules that cause a warning (as defined in the annotations). */
protected Map m_validationWarningRules;
/** The container page only flag, indicating if this XML content should be indexed on container pages only. */
private boolean m_containerPageOnly;
/** The default complex widget class name. */
private String m_defaultWidget;
/** The default complex widget configuration. */
private String m_defaultWidgetConfig;
/** The default complex widget for this type. */
private I_CmsComplexWidget m_defaultWidgetInstance;
/** The elements to display in ncompact view. */
private HashMap m_displayTypes;
/** The editor change handlers. */
private List m_editorChangeHandlers;
/** A set of keys identifying the mappings which should use default values if the corresponding values are not set in the XML content. */
private Set m_mappingsUsingDefault = new HashSet();
/** Message key fallback handler for the editor. */
private CmsMultiMessages.I_KeyFallbackHandler m_messageKeyHandler = new CmsMultiMessages.I_KeyFallbackHandler() {
public Optional getFallbackKey(String key) {
return Optional.absent();
}
};
/** The paths of values for which no macros should be resolved when getting the default value. */
private Set m_nonMacroResolvableDefaults = new HashSet();
/** The visibility configurations by element path. */
private Map m_visibilityConfigurations;
/**
* Creates a new instance of the default XML content handler.
*/
public CmsDefaultXmlContentHandler() {
init();
}
/**
* Static initializer for caching the default appinfo validation schema.
*/
static {
// the schema definition is located in 2 separates file for easier editing
// 2 files are required in case an extended schema want to use the default definitions,
// but with an extended "appinfo" node
byte[] appinfoSchemaTypes;
try {
// first read the default types
appinfoSchemaTypes = CmsFileUtil.readFile(APPINFO_SCHEMA_FILE_TYPES);
} catch (Exception e) {
throw new CmsRuntimeException(
Messages.get().container(
org.opencms.xml.types.Messages.ERR_XMLCONTENT_LOAD_SCHEMA_1,
APPINFO_SCHEMA_FILE_TYPES),
e);
}
CmsXmlEntityResolver.cacheSystemId(APPINFO_SCHEMA_TYPES_SYSTEM_ID, appinfoSchemaTypes);
byte[] appinfoSchema;
try {
// now read the default base schema
appinfoSchema = CmsFileUtil.readFile(APPINFO_SCHEMA_FILE);
} catch (Exception e) {
throw new CmsRuntimeException(
Messages.get().container(
org.opencms.xml.types.Messages.ERR_XMLCONTENT_LOAD_SCHEMA_1,
APPINFO_SCHEMA_FILE),
e);
}
CmsXmlEntityResolver.cacheSystemId(APPINFO_SCHEMA_SYSTEM_ID, appinfoSchema);
}
/**
* Static initializer for caching the default appinfo validation schema.
*/
static {
// the schema definition is located in 2 separates file for easier editing
// 2 files are required in case an extended schema want to use the default definitions,
// but with an extended "appinfo" node
byte[] appinfoSchemaTypes;
try {
// first read the default types
appinfoSchemaTypes = CmsFileUtil.readFile(APPINFO_SCHEMA_FILE_TYPES);
} catch (Exception e) {
throw new CmsRuntimeException(
Messages.get().container(
org.opencms.xml.types.Messages.ERR_XMLCONTENT_LOAD_SCHEMA_1,
APPINFO_SCHEMA_FILE_TYPES),
e);
}
CmsXmlEntityResolver.cacheSystemId(APPINFO_SCHEMA_TYPES_SYSTEM_ID, appinfoSchemaTypes);
byte[] appinfoSchema;
try {
// now read the default base schema
appinfoSchema = CmsFileUtil.readFile(APPINFO_SCHEMA_FILE);
} catch (Exception e) {
throw new CmsRuntimeException(
Messages.get().container(
org.opencms.xml.types.Messages.ERR_XMLCONTENT_LOAD_SCHEMA_1,
APPINFO_SCHEMA_FILE),
e);
}
CmsXmlEntityResolver.cacheSystemId(APPINFO_SCHEMA_SYSTEM_ID, appinfoSchema);
}
/**
* Copies a given CMS context and set the copy's site root to '/'.
*
* @param cms the CMS context to copy
* @return the copy
*
* @throws CmsException if something goes wrong
*/
public CmsObject createRootCms(CmsObject cms) throws CmsException {
CmsObject rootCms = OpenCms.initCmsObject(cms);
Object logEntry = cms.getRequestContext().getAttribute(CmsLogEntry.ATTR_LOG_ENTRY);
if (logEntry != null) {
rootCms.getRequestContext().setAttribute(CmsLogEntry.ATTR_LOG_ENTRY, logEntry);
}
rootCms.getRequestContext().setSiteRoot("/");
return rootCms;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getAllowedTemplates()
*/
public CmsDefaultSet getAllowedTemplates() {
return m_allowedTemplates;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getComplexWidget(org.opencms.xml.types.I_CmsXmlSchemaType)
*/
public I_CmsComplexWidget getComplexWidget(I_CmsXmlSchemaType value) {
I_CmsComplexWidget result = m_complexWidgets.get(value.getName());
if (result == null) {
if (value instanceof CmsXmlNestedContentDefinition) {
I_CmsXmlContentHandler contentHandler = ((CmsXmlNestedContentDefinition)value).getNestedContentDefinition().getContentHandler();
if (contentHandler.getDefaultComplexWidget() != null) {
return contentHandler.getDefaultComplexWidget().configure(
contentHandler.getDefaultComplexWidgetConfiguration());
}
}
return null;
} else {
String configuration = getConfiguration(value);
result = result.configure(configuration);
return result;
}
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getConfiguration(org.opencms.xml.types.I_CmsXmlSchemaType)
*/
public String getConfiguration(I_CmsXmlSchemaType type) {
String elementName = type.getName();
return m_configurationValues.get(elementName);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getCSSHeadIncludes()
*/
public Set getCSSHeadIncludes() {
return Collections.unmodifiableSet(m_cssHeadIncludes);
}
/***
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getCSSHeadIncludes(org.opencms.file.CmsObject, org.opencms.file.CmsResource)
*/
@SuppressWarnings("unused")
public Set getCSSHeadIncludes(CmsObject cms, CmsResource resource) throws CmsException {
return getCSSHeadIncludes();
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getDefault(org.opencms.file.CmsObject, org.opencms.file.CmsResource, org.opencms.xml.types.I_CmsXmlSchemaType, java.lang.String, java.util.Locale)
*/
public String getDefault(CmsObject cms, CmsResource resource, I_CmsXmlSchemaType type, String path, Locale locale) {
String defaultValue;
if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
// use the "getDefault" method of the given value, will use value from standard XML schema
defaultValue = type.getDefault(locale);
} else {
// look up the default from the configured mappings
defaultValue = m_defaultValues.get(path);
if (defaultValue == null) {
// no value found, try default xpath
path = CmsXmlUtils.removeXpath(path);
path = CmsXmlUtils.createXpath(path, 1);
// look up the default value again with default index of 1 in all path elements
defaultValue = m_defaultValues.get(path);
}
}
if (defaultValue != null) {
CmsObject newCms = cms;
if (resource != null) {
try {
// switch the current URI to the XML document resource so that properties can be read
CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(resource.getRootPath());
if (site != null) {
newCms = OpenCms.initCmsObject(cms);
newCms.getRequestContext().setSiteRoot(site.getSiteRoot());
newCms.getRequestContext().setUri(newCms.getSitePath(resource));
}
} catch (Exception e) {
// on any error just use the default input OpenCms context
}
}
// return the default value with processed macros
String result = defaultValue;
if (!m_nonMacroResolvableDefaults.contains(path)) {
CmsMacroResolver resolver = CmsMacroResolver.newInstance().setCmsObject(newCms).setMessages(
getMessages(locale));
result = resolver.resolveMacros(defaultValue);
}
return result;
}
// no default value is available
return null;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getDefault(org.opencms.file.CmsObject, I_CmsXmlContentValue, java.util.Locale)
*/
public String getDefault(CmsObject cms, I_CmsXmlContentValue value, Locale locale) {
String path = null;
if (value.getElement() != null) {
path = value.getPath();
}
return getDefault(cms, value.getDocument() != null ? value.getDocument().getFile() : null, value, path, locale);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getDefaultComplexWidget()
*/
public I_CmsComplexWidget getDefaultComplexWidget() {
return m_defaultWidgetInstance;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getDefaultComplexWidgetClass()
*/
public String getDefaultComplexWidgetClass() {
return m_defaultWidget;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getDefaultComplexWidgetConfiguration()
*/
public String getDefaultComplexWidgetConfiguration() {
return m_defaultWidgetConfig;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getDisplayType(org.opencms.xml.types.I_CmsXmlSchemaType)
*/
public DisplayType getDisplayType(I_CmsXmlSchemaType type) {
if (m_displayTypes.containsKey(type.getName())) {
return m_displayTypes.get(type.getName());
} else {
return DisplayType.none;
}
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getEditorChangeHandlers()
*/
public List getEditorChangeHandlers() {
return Collections.unmodifiableList(m_editorChangeHandlers);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getFormatterConfiguration(org.opencms.file.CmsObject, org.opencms.file.CmsResource)
*/
public CmsFormatterConfiguration getFormatterConfiguration(CmsObject cms, CmsResource resource) {
List wrappers = Lists.newArrayList();
for (CmsFormatterBean formatter : m_formatters) {
CmsSchemaFormatterBeanWrapper wrapper = new CmsSchemaFormatterBeanWrapper(cms, formatter, this, resource);
wrappers.add(wrapper);
}
return CmsFormatterConfiguration.create(cms, wrappers);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getJSHeadIncludes()
*/
public Set getJSHeadIncludes() {
return Collections. unmodifiableSet(m_jsHeadIncludes);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getJSHeadIncludes(org.opencms.file.CmsObject, org.opencms.file.CmsResource)
*/
@SuppressWarnings("unused")
public Set getJSHeadIncludes(CmsObject cms, CmsResource resource) throws CmsException {
return getJSHeadIncludes();
}
/**
* Returns the all mappings defined for the given element xpath.
*
* @since 7.0.2
*
* @param elementName the element xpath to look up the mapping for
*
* @return the mapping defined for the given element xpath
*/
public List getMappings(String elementName) {
List result = m_elementMappings.get(elementName);
if (result == null) {
result = Collections.emptyList();
}
return result;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getMessageKeyHandler()
*/
public I_KeyFallbackHandler getMessageKeyHandler() {
return m_messageKeyHandler;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getMessages(java.util.Locale)
*/
public CmsMessages getMessages(Locale locale) {
CmsMessages result = null;
if ((m_messageBundleNames == null) || m_messageBundleNames.isEmpty()) {
return new CmsMessages(Messages.get().getBundleName(), locale);
} else {
// a message bundle was initialized
CmsMultiMessages multiMessages = new CmsMultiMessages(locale);
for (String messageBundleName : m_messageBundleNames) {
multiMessages.addMessages(new CmsMessages(messageBundleName, locale));
}
if (!m_messageBundleNames.contains(Messages.get().getBundleName())) {
multiMessages.addMessages(new CmsMessages(Messages.get().getBundleName(), locale));
}
result = multiMessages;
}
return result;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getModelFolder()
*/
public String getModelFolder() {
return m_modelFolder;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getPreview(org.opencms.file.CmsObject, org.opencms.xml.content.CmsXmlContent, java.lang.String)
*/
public String getPreview(CmsObject cms, CmsXmlContent content, String resourcename) {
CmsMacroResolver resolver = CmsMacroResolver.newInstance().setCmsObject(cms);
resolver.addMacro(MACRO_PREVIEW_TEMPFILE, resourcename);
return resolver.resolveMacros(m_previewLocation);
}
/**
* @see I_CmsXmlContentHandler#getRelationType(I_CmsXmlContentValue)
*/
@Deprecated
public CmsRelationType getRelationType(I_CmsXmlContentValue value) {
if (value == null) {
return CmsRelationType.XML_WEAK;
}
return getRelationType(value.getPath());
}
/**
* @see I_CmsXmlContentHandler#getRelationType(String)
*/
public CmsRelationType getRelationType(String xpath) {
return getRelationType(xpath, CmsRelationType.XML_WEAK);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getRelationType(java.lang.String, org.opencms.relations.CmsRelationType)
*/
public CmsRelationType getRelationType(String xpath, CmsRelationType defaultType) {
CmsRelationType relationType = null;
if (xpath != null) {
// look up the default from the configured mappings
relationType = m_relations.get(xpath);
if (relationType == null) {
// no value found, try default xpath
String path = CmsXmlUtils.removeAllXpathIndices(xpath);
// look up the default value again without indexes
relationType = m_relations.get(path);
}
if (relationType == null) {
// no value found, try the last simple type path
String path = CmsXmlUtils.getLastXpathElement(xpath);
// look up the default value again for the last simple type
relationType = m_relations.get(path);
}
}
if (relationType == null) {
relationType = defaultType;
}
return relationType;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getSearchFields()
*/
public Set getSearchFields() {
return Collections.unmodifiableSet(new HashSet(m_searchFields.values()));
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getSearchFieldsForPage()
*/
public Set getSearchFieldsForPage() {
return Collections.unmodifiableSet(new HashSet(m_searchFieldsPage.values()));
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getSearchSettings()
*/
public Map getSearchSettings() {
return m_searchSettings;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getSettings(org.opencms.file.CmsObject, org.opencms.file.CmsResource)
*/
public Map getSettings(CmsObject cms, CmsResource resource) {
return Collections.unmodifiableMap(m_settings);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getSynchronizations()
*/
public List getSynchronizations() {
return Collections.unmodifiableList(m_synchronizations);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getTabs()
*/
public List getTabs() {
return Collections.unmodifiableList(m_tabs);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getTitleMapping(org.opencms.file.CmsObject, org.opencms.xml.content.CmsXmlContent, java.util.Locale)
*/
public String getTitleMapping(CmsObject cms, CmsXmlContent document, Locale locale) {
String result = null;
if (m_titleMappings.size() > 0) {
// a title mapping is available
String xpath = m_titleMappings.get(0);
// currently just use the first mapping found, unsure if multiple "Title" mappings would make sense anyway
result = document.getStringValue(cms, xpath, locale);
if ((result == null)
&& (isMappingUsingDefault(xpath, TITLE_PROPERTY_MAPPING)
|| isMappingUsingDefault(xpath, TITLE_PROPERTY_SHARED_MAPPING)
|| isMappingUsingDefault(xpath, TITLE_PROPERTY_INDIVIDUAL_MAPPING))) {
result = getDefault(cms, document.getFile(), null, xpath, locale);
}
if (result != null) {
try {
CmsGalleryNameMacroResolver resolver = new CmsGalleryNameMacroResolver(
createRootCms(cms),
document,
locale);
resolver.setKeepEmptyMacros(true);
result = resolver.resolveMacros(result);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
return result;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#getWidget(org.opencms.xml.types.I_CmsXmlSchemaType)
*/
public I_CmsWidget getWidget(I_CmsXmlSchemaType value) {
// try the specific widget settings first
I_CmsWidget result = m_elementWidgets.get(value.getName());
if (result == null) {
// use default widget mappings
result = OpenCms.getXmlContentTypeManager().getWidgetDefault(value.getTypeName());
} else {
result = result.newInstance();
}
if (result != null) {
// set the configuration value for this widget
String configuration = getConfiguration(value);
if (configuration == null) {
// no individual configuration defined, try to get global default configuration
configuration = OpenCms.getXmlContentTypeManager().getWidgetDefaultConfiguration(result);
}
result.setConfiguration(configuration);
}
return result;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#hasModifiableFormatters()
*/
public boolean hasModifiableFormatters() {
return (m_formatters != null) && (m_formatters.size() > 0);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#hasSynchronizedElements()
*/
public boolean hasSynchronizedElements() {
return !m_synchronizations.isEmpty();
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#hasVisibilityHandlers()
*/
public boolean hasVisibilityHandlers() {
return (m_visibilityConfigurations != null) && !m_visibilityConfigurations.isEmpty();
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#initialize(org.dom4j.Element, org.opencms.xml.CmsXmlContentDefinition)
*/
public synchronized void initialize(Element appInfoElement, CmsXmlContentDefinition contentDefinition)
throws CmsXmlException {
if (appInfoElement != null) {
// validate the appinfo element XML content with the default appinfo handler schema
validateAppinfoElement(appInfoElement);
// re-initialize the local variables
init();
Iterator i = CmsXmlGenericWrapper.elementIterator(appInfoElement);
while (i.hasNext()) {
// iterate all elements in the appinfo node
Element element = i.next();
String nodeName = element.getName();
if (nodeName.equals(APPINFO_MAPPINGS)) {
initMappings(element, contentDefinition);
} else if (nodeName.equals(APPINFO_LAYOUTS)) {
initLayouts(element, contentDefinition);
} else if (nodeName.equals(APPINFO_VALIDATIONRULES)) {
initValidationRules(element, contentDefinition);
} else if (nodeName.equals(APPINFO_RELATIONS)) {
initRelations(element, contentDefinition);
} else if (nodeName.equals(APPINFO_DEFAULTS)) {
initDefaultValues(element, contentDefinition);
} else if (nodeName.equals(APPINFO_MODELFOLDER)) {
initModelFolder(element, contentDefinition);
} else if (nodeName.equals(APPINFO_PREVIEW)) {
initPreview(element, contentDefinition);
} else if (nodeName.equals(APPINFO_RESOURCEBUNDLE)) {
initResourceBundle(element, contentDefinition, true);
} else if (nodeName.equals(APPINFO_RESOURCEBUNDLES)) {
initResourceBundle(element, contentDefinition, false);
} else if (nodeName.equals(APPINFO_SEARCHSETTINGS)) {
initSearchSettings(element, contentDefinition);
} else if (nodeName.equals(APPINFO_TABS)) {
initTabs(element, contentDefinition);
} else if (nodeName.equals(APPINFO_FORMATTERS)) {
initFormatters(element, contentDefinition);
} else if (nodeName.equals(APPINFO_HEAD_INCLUDES)) {
initHeadIncludes(element, contentDefinition);
} else if (nodeName.equals(APPINFO_SETTINGS)) {
initSettings(element, contentDefinition);
} else if (nodeName.equals(APPINFO_TEMPLATES)) {
initTemplates(element, contentDefinition);
} else if (nodeName.equals(APPINFO_DEFAULTWIDGET)) {
initDefaultWidget(element);
} else if (nodeName.equals(APPINFO_VISIBILITIES)) {
initVisibilities(element, contentDefinition);
} else if (nodeName.equals(APPINFO_SYNCHRONIZATIONS)) {
initSynchronizations(element, contentDefinition);
} else if (nodeName.equals(APPINFO_EDITOR_CHANGE_HANDLERS)) {
initEditorChangeHandlers(element);
} else if (nodeName.equals(APPINFO_MESSAGEKEYHANDLER)) {
initMessageKeyHandler(element);
}
}
}
// at the end, add default check rules for optional file references
addDefaultCheckRules(contentDefinition, null, null);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#invalidateBrokenLinks(CmsObject, CmsXmlContent)
*/
public void invalidateBrokenLinks(CmsObject cms, CmsXmlContent document) {
if ((cms == null) || (cms.getRequestContext().getRequestTime() == CmsResource.DATE_RELEASED_EXPIRED_IGNORE)) {
// do not check if the request comes the editor
return;
}
boolean needReinitialization = false;
// iterate the locales
Iterator itLocales = document.getLocales().iterator();
while (itLocales.hasNext()) {
Locale locale = itLocales.next();
List removedNodes = new ArrayList();
Map valuesToRemove = Maps.newHashMap();
// iterate the values
Iterator itValues = document.getValues(locale).iterator();
while (itValues.hasNext()) {
I_CmsXmlContentValue value = itValues.next();
String path = value.getPath();
// check if this value has already been deleted by parent rules
boolean alreadyRemoved = false;
Iterator itRemNodes = removedNodes.iterator();
while (itRemNodes.hasNext()) {
String remNode = itRemNodes.next();
if (path.startsWith(remNode)) {
alreadyRemoved = true;
break;
}
}
// only continue if not already removed and if a rule match
if (alreadyRemoved
|| ((m_relationChecks.get(path) == null)
&& (m_relationChecks.get(CmsXmlUtils.removeXpath(path)) == null))) {
continue;
}
// check rule matched
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(Messages.LOG_XMLCONTENT_CHECK_RULE_MATCH_1, path));
}
if (validateLink(cms, value, null)) {
// invalid link
if (LOG.isDebugEnabled()) {
LOG.debug(
Messages.get().getBundle().key(
Messages.LOG_XMLCONTENT_CHECK_WARNING_2,
path,
value.getStringValue(cms)));
}
// find the node to remove
String parentPath = path;
while (isInvalidateParent(parentPath)) {
// check parent
parentPath = CmsXmlUtils.removeLastXpathElement(parentPath);
// log info
if (LOG.isDebugEnabled()) {
LOG.debug(
Messages.get().getBundle().key(
Messages.LOG_XMLCONTENT_CHECK_PARENT_2,
path,
parentPath));
}
}
value = document.getValue(parentPath, locale);
// Doing the actual DOM modifications here would make the bookmarks for this locale invalid,
// so we delay it until later because we need the bookmarks for document.getValue() in the next loop iterations
valuesToRemove.put(parentPath, value);
// mark node as deleted
removedNodes.add(parentPath);
}
}
if (!removedNodes.isEmpty()) {
needReinitialization = true;
}
for (I_CmsXmlContentValue valueToRemove : valuesToRemove.values()) {
// detach the value node from the XML document
valueToRemove.getElement().detach();
}
}
if (needReinitialization) {
// re-initialize the XML content
document.initDocument();
}
}
/**
* Returns true if the Acacia editor is disabled for this type.
*
* @return true if the acacia editor is disabled
*/
public boolean isAcaciaEditorDisabled() {
return !m_useAcacia;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#isContainerPageOnly()
*/
public boolean isContainerPageOnly() {
return m_containerPageOnly;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#isSearchable(org.opencms.xml.types.I_CmsXmlContentValue)
*/
public boolean isSearchable(I_CmsXmlContentValue value) {
String path = CmsXmlUtils.removeXpath(value.getPath());
// check for name configured in the annotations
Boolean searchSetting = m_searchSettings.get(path);
// if no search setting is found within the root handler, move the path upwards to look for other configurations
if (searchSetting == null) {
String[] pathElements = path.split("/");
I_CmsXmlSchemaType type = value.getDocument().getContentDefinition().getSchemaType(pathElements[0]);
for (int i = 1; i < pathElements.length; i++) {
type = ((CmsXmlNestedContentDefinition)type).getNestedContentDefinition().getSchemaType(
pathElements[i]);
String subPath = getSubPath(pathElements, i);
searchSetting = type.getContentDefinition().getContentHandler().getSearchSettings().get(subPath);
if (searchSetting != null) {
break;
}
}
}
// if no annotation has been found, use default for value
return (searchSetting == null) ? value.isSearchable() : searchSetting.booleanValue();
}
/**
* Returns the content field visibilty.
*
* This implementation will be used as default if no other org.opencms.xml.content.I_CmsXmlContentVisibilityHandler is configured.
*
* Only users that are member in one of the specified groups will be allowed to view and edit the given content field.
* The parameter should contain a '|' separated list of group names.
*
* @see org.opencms.xml.content.I_CmsXmlContentVisibilityHandler#isValueVisible(org.opencms.file.CmsObject, org.opencms.xml.types.I_CmsXmlSchemaType, java.lang.String, java.lang.String, org.opencms.file.CmsResource, java.util.Locale)
*/
public boolean isValueVisible(
CmsObject cms,
I_CmsXmlSchemaType value,
String elementName,
String params,
CmsResource resource,
Locale contentLocale) {
CmsUser user = cms.getRequestContext().getCurrentUser();
boolean result = false;
try {
List roles = OpenCms.getRoleManager().getRolesOfUser(cms, user.getName(), "", true, false, true);
List groups = cms.getGroupsOfUser(user.getName(), false);
String[] allowedPrincipals = params.split("\\|");
List groupNames = new ArrayList();
List roleNames = new ArrayList();
for (CmsGroup group : groups) {
groupNames.add(group.getName());
}
for (CmsRole role : roles) {
roleNames.add(role.getRoleName());
}
for (String principal : allowedPrincipals) {
if (CmsRole.hasPrefix(principal)) {
// prefixed as a role
principal = CmsRole.removePrefix(principal);
if (roleNames.contains(principal)) {
result = true;
break;
}
} else {
// otherwise we always assume this is a group, will work if prefixed or not
principal = CmsGroup.removePrefix(principal);
if (groupNames.contains(principal)) {
result = true;
break;
}
}
}
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
}
return result;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#isVisible(org.opencms.file.CmsObject, org.opencms.xml.types.I_CmsXmlSchemaType, java.lang.String, org.opencms.file.CmsResource, java.util.Locale)
*/
public boolean isVisible(
CmsObject cms,
I_CmsXmlSchemaType contentValue,
String valuePath,
CmsResource resource,
Locale contentLocale) {
if (hasVisibilityHandlers() && m_visibilityConfigurations.containsKey(valuePath)) {
VisibilityConfiguration config = m_visibilityConfigurations.get(valuePath);
return config.getHandler().isValueVisible(
cms,
contentValue,
valuePath,
config.getParams(),
resource,
contentLocale);
}
return true;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#prepareForUse(org.opencms.file.CmsObject, org.opencms.xml.content.CmsXmlContent)
*/
public CmsXmlContent prepareForUse(CmsObject cms, CmsXmlContent content) {
// NOOP, just return the unmodified content
return content;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#prepareForWrite(org.opencms.file.CmsObject, org.opencms.xml.content.CmsXmlContent, org.opencms.file.CmsFile)
*/
public CmsFile prepareForWrite(CmsObject cms, CmsXmlContent content, CmsFile file) throws CmsException {
if (!content.isAutoCorrectionEnabled()) {
// check if the XML should be corrected automatically (if not already set)
Object attribute = cms.getRequestContext().getAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE);
// set the auto correction mode as required
boolean autoCorrectionEnabled = (attribute != null) && ((Boolean)attribute).booleanValue();
content.setAutoCorrectionEnabled(autoCorrectionEnabled);
}
// validate the XML structure before writing the file if required
if (!content.isAutoCorrectionEnabled()) {
// an exception will be thrown if the structure is invalid
content.validateXmlStructure(new CmsXmlEntityResolver(cms));
}
// read the content-conversion property
String contentConversion = CmsHtmlConverter.getConversionSettings(cms, file);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(contentConversion)) {
// enable pretty printing and XHTML conversion of XML content html fields by default
contentConversion = CmsHtmlConverter.PARAM_XHTML;
}
content.setConversion(contentConversion);
// correct the HTML structure
file = content.correctXmlStructure(cms);
content.setFile(file);
// resolve the file mappings
CmsMappingResolutionContext mappingContext = new CmsMappingResolutionContext();
mappingContext.setCmsObject(cms);
// pass the mapping context as a request context attribute to preserve interface compatibility
cms.getRequestContext().setAttribute(ATTR_MAPPING_RESOLUTION_CONTEXT, mappingContext);
content.resolveMappings(cms);
// ensure all property or permission mappings of deleted optional values are removed
removeEmptyMappings(cms, file, content);
resolveDefaultMappings(cms, file, content);
cms.getRequestContext().removeAttribute(ATTR_MAPPING_RESOLUTION_CONTEXT);
mappingContext.finalizeMappings();
// write categories (if there is a category widget present)
file = writeCategories(cms, file, content);
// return the result
return file;
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#resolveMapping(org.opencms.file.CmsObject, org.opencms.xml.content.CmsXmlContent, org.opencms.xml.types.I_CmsXmlContentValue)
*/
public void resolveMapping(CmsObject cms, CmsXmlContent content, I_CmsXmlContentValue value) throws CmsException {
if (content.getFile() == null) {
throw new CmsXmlException(Messages.get().container(Messages.ERR_XMLCONTENT_RESOLVE_FILE_NOT_FOUND_0));
}
// get the mappings for the element name
boolean valueIsSimple = value.isSimpleType();
String valuePath = value.getPath();
int valueIndex = value.getIndex();
Locale valueLocale = value.getLocale();
CmsObject rootCms1 = createRootCms(cms);
String originalStringValue = null;
if (valueIsSimple) {
originalStringValue = value.getStringValue(rootCms1);
}
resolveMapping(cms, content, valuePath, valueIsSimple, valueIndex, valueLocale, originalStringValue);
}
/**
* @see org.opencms.xml.content.I_CmsXmlContentHandler#resolveValidation(org.opencms.file.CmsObject, org.opencms.xml.types.I_CmsXmlContentValue, org.opencms.xml.content.CmsXmlContentErrorHandler)
*/
public CmsXmlContentErrorHandler resolveValidation(
CmsObject cms,
I_CmsXmlContentValue value,
CmsXmlContentErrorHandler errorHandler) {
if (errorHandler == null) {
// init a new error handler if required
errorHandler = new CmsXmlContentErrorHandler();
}
if (!value.isSimpleType()) {
// no validation for a nested schema is possible
// note that the sub-elements of the nested schema ARE validated by the node visitor,
// it's just the nested schema value itself that does not support validation
return errorHandler;
}
// validate the error rules
errorHandler = validateValue(cms, value, errorHandler, m_validationErrorRules, false);
// validate the warning rules
errorHandler = validateValue(cms, value, errorHandler, m_validationWarningRules, true);
// validate categories
errorHandler = validateCategories(cms, value, errorHandler);
// return the result
return errorHandler;
}
/**
* Adds a check rule for a specified element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to add the rule to
* @param invalidate false
, to disable link check /
* true
or node
, to invalidate just the single node if the link is broken /
* parent
, if this rule will invalidate the whole parent node in nested content
* @param type the relation type
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addCheckRule(
CmsXmlContentDefinition contentDefinition,
String elementName,
String invalidate,
String type)
throws CmsXmlException {
I_CmsXmlSchemaType schemaType = contentDefinition.getSchemaType(elementName);
if (schemaType == null) {
// no element with the given name
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_CHECK_INVALID_ELEM_1, elementName));
}
if (!CmsXmlVfsFileValue.TYPE_NAME.equals(schemaType.getTypeName())
&& !CmsXmlVarLinkValue.TYPE_NAME.equals(schemaType.getTypeName())) {
// element is not a OpenCmsVfsFile
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_CHECK_INVALID_TYPE_1, elementName));
}
// cache the check rule data
Boolean invalidateParent = null;
if ((invalidate == null)
|| invalidate.equalsIgnoreCase(Boolean.TRUE.toString())
|| invalidate.equalsIgnoreCase(APPINFO_ATTR_TYPE_NODE)) {
invalidateParent = Boolean.FALSE;
} else if (invalidate.equalsIgnoreCase(APPINFO_ATTR_TYPE_PARENT)) {
invalidateParent = Boolean.TRUE;
}
if (invalidateParent != null) {
m_relationChecks.put(elementName, invalidateParent);
}
CmsRelationType relationType = (type == null ? CmsRelationType.XML_WEAK : CmsRelationType.valueOfXml(type));
m_relations.put(elementName, relationType);
if (invalidateParent != null) {
// check the whole xpath hierarchy
String path = elementName;
while (CmsStringUtil.isNotEmptyOrWhitespaceOnly(path)) {
if (!isInvalidateParent(path)) {
// if invalidate type = node, then the node needs to be optional
if (contentDefinition.getSchemaType(path).getMinOccurs() > 0) {
// element is not optional
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_CHECK_NOT_OPTIONAL_1, path));
}
// no need to further check
break;
} else if (!CmsXmlUtils.isDeepXpath(path)) {
// if invalidate type = parent, then the node needs to be nested
// document root can not be invalidated
throw new CmsXmlException(Messages.get().container(Messages.ERR_XMLCONTENT_CHECK_NOT_EMPTY_DOC_0));
}
path = CmsXmlUtils.removeLastXpathElement(path);
}
}
}
/**
* Adds a configuration value for an element widget.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to map
* @param configurationValue the configuration value to use
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addConfiguration(
CmsXmlContentDefinition contentDefinition,
String elementName,
String configurationValue)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_CONFIG_ELEM_UNKNOWN_1, elementName));
}
m_configurationValues.put(elementName, configurationValue);
}
/**
* Adds a default value for an element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to map
* @param defaultValue the default value to use
* @param resolveMacrosValue the value of the 'resolveMacros' attribute
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addDefault(
CmsXmlContentDefinition contentDefinition,
String elementName,
String defaultValue,
String resolveMacrosValue)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
org.opencms.xml.types.Messages.get().container(
Messages.ERR_XMLCONTENT_INVALID_ELEM_DEFAULT_1,
elementName));
}
// store mappings as xpath to allow better control about what is mapped
String xpath = CmsXmlUtils.createXpath(elementName, 1);
m_defaultValues.put(xpath, defaultValue);
// macros are resolved by default
if ((resolveMacrosValue != null) && !Boolean.parseBoolean(resolveMacrosValue)) {
m_nonMacroResolvableDefaults.add(xpath);
}
}
/**
* Adds all needed default check rules recursively for the given schema type.
*
* @param rootContentDefinition the root content definition
* @param schemaType the schema type to check
* @param elementPath the current element path
*
* @throws CmsXmlException if something goes wrong
*/
protected void addDefaultCheckRules(
CmsXmlContentDefinition rootContentDefinition,
I_CmsXmlSchemaType schemaType,
String elementPath)
throws CmsXmlException {
if ((schemaType != null) && schemaType.isSimpleType()) {
if ((schemaType.getMinOccurs() == 0)
&& (CmsXmlVfsFileValue.TYPE_NAME.equals(schemaType.getTypeName())
|| CmsXmlVarLinkValue.TYPE_NAME.equals(schemaType.getTypeName()))
&& !m_relationChecks.containsKey(elementPath)
&& !m_relations.containsKey(elementPath)) {
// add default check rule for the element
addCheckRule(rootContentDefinition, elementPath, null, null);
}
} else {
// recursion required
CmsXmlContentDefinition nestedContentDefinition = rootContentDefinition;
if (schemaType != null) {
CmsXmlNestedContentDefinition nestedDefinition = (CmsXmlNestedContentDefinition)schemaType;
nestedContentDefinition = nestedDefinition.getNestedContentDefinition();
}
Iterator itElems = nestedContentDefinition.getSchemaTypes().iterator();
while (itElems.hasNext()) {
String element = itElems.next();
String path = (schemaType != null) ? CmsXmlUtils.concatXpath(elementPath, element) : element;
I_CmsXmlSchemaType nestedSchema = nestedContentDefinition.getSchemaType(element);
if ((schemaType == null) || !nestedSchema.equals(schemaType)) {
addDefaultCheckRules(rootContentDefinition, nestedSchema, path);
}
}
}
}
/**
* Adds the given element to the compact view set.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name
* @param displayType the display type to use for the element widget
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addDisplayType(
CmsXmlContentDefinition contentDefinition,
String elementName,
DisplayType displayType)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_CONFIG_ELEM_UNKNOWN_1, elementName));
}
m_displayTypes.put(elementName, displayType);
}
/**
* Adds an element mapping.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to map
* @param mapping the mapping to use
* @param useDefault the 'useDefault' attribute
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addMapping(
CmsXmlContentDefinition contentDefinition,
String elementName,
String mapping,
String useDefault)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_INVALID_ELEM_MAPPING_1, elementName));
}
// store mappings as xpath to allow better control about what is mapped
String xpath = CmsXmlUtils.createXpath(elementName, 1);
// since 7.0.2 multiple mappings are possible, so the mappings are stored in an array
List values = m_elementMappings.get(xpath);
if (values == null) {
// there should not really be THAT much multiple mappings per value...
values = new ArrayList(4);
m_elementMappings.put(xpath, values);
}
if (Boolean.parseBoolean(useDefault)) {
m_mappingsUsingDefault.add(xpath + ":" + mapping);
}
values.add(mapping);
if (mapping.startsWith(MAPTO_PROPERTY) && mapping.endsWith(":" + CmsPropertyDefinition.PROPERTY_TITLE)) {
// this is a title mapping
m_titleMappings.add(xpath);
}
}
/**
* Adds a Solr field for an element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param field the Solr field
*/
@Deprecated
protected void addSearchField(CmsXmlContentDefinition contentDefinition, CmsSearchField field) {
addSearchField(contentDefinition, field, I_CmsXmlContentHandler.MappingType.ELEMENT);
}
/**
* Adds a Solr field for an element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param field the Solr field
* @param type the type, specifying if the field should be attached to the document of the XML content or to all container pages the content is placed on
*/
protected void addSearchField(
CmsXmlContentDefinition contentDefinition,
CmsSearchField field,
I_CmsXmlContentHandler.MappingType type) {
Locale locale = null;
if (field instanceof CmsSolrField) {
locale = ((CmsSolrField)field).getLocale();
}
String key = CmsXmlUtils.concatXpath(locale != null ? locale.toString() : null, field.getName());
switch (type) {
case PAGE:
m_searchFieldsPage.put(key, field);
break;
case ELEMENT:
default:
m_searchFields.put(key, field);
break;
}
}
/**
* Adds a search setting for an element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to map
* @param value the search setting value to store
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addSearchSetting(CmsXmlContentDefinition contentDefinition, String elementName, Boolean value)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_INVALID_ELEM_SEARCHSETTINGS_1, elementName));
}
// store the search exclusion as defined
m_searchSettings.put(elementName, value);
}
/**
* Adds a validation rule for a specified element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to add the rule to
* @param regex the validation rule regular expression
* @param message the message in case validation fails (may be null)
* @param isWarning if true, this rule is used for warnings, otherwise it's an error
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addValidationRule(
CmsXmlContentDefinition contentDefinition,
String elementName,
String regex,
String message,
boolean isWarning)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_INVALID_ELEM_VALIDATION_1, elementName));
}
if (isWarning) {
m_validationWarningRules.put(elementName, regex);
if (message != null) {
m_validationWarningMessages.put(elementName, message);
}
} else {
m_validationErrorRules.put(elementName, regex);
if (message != null) {
m_validationErrorMessages.put(elementName, message);
}
}
}
/**
* Adds a GUI widget for a specified element.
*
* @param contentDefinition the XML content definition this XML content handler belongs to
* @param elementName the element name to map
* @param widgetClassOrAlias the widget to use as GUI for the element (registered alias or class name)
*
* @throws CmsXmlException in case an unknown element name is used
*/
protected void addWidget(CmsXmlContentDefinition contentDefinition, String elementName, String widgetClassOrAlias)
throws CmsXmlException {
if (contentDefinition.getSchemaType(elementName) == null) {
throw new CmsXmlException(
Messages.get().container(Messages.ERR_XMLCONTENT_INVALID_ELEM_LAYOUTWIDGET_1, elementName));
}
// get the base widget from the XML content type manager
I_CmsWidget widget = OpenCms.getXmlContentTypeManager().getWidget(widgetClassOrAlias);
if (widget == null) {
// no registered widget class found
if (CmsStringUtil.isValidJavaClassName(widgetClassOrAlias)) {
// java class name given, try to create new instance of the class and cast to widget
try {
Class> specialWidgetClass = Class.forName(widgetClassOrAlias);
if (I_CmsComplexWidget.class.isAssignableFrom(specialWidgetClass)) {
m_complexWidgets.put(elementName, (I_CmsComplexWidget)(specialWidgetClass.newInstance()));
return;
} else {
widget = (I_CmsWidget)specialWidgetClass.newInstance();
}
} catch (Exception e) {
throw new CmsXmlException(
Messages.get().container(
Messages.ERR_XMLCONTENT_INVALID_CUSTOM_CLASS_3,
widgetClassOrAlias,
elementName,
contentDefinition.getSchemaLocation()),
e);
}
}
if (widget == null) {
// no valid widget found
throw new CmsXmlException(
Messages.get().container(
Messages.ERR_XMLCONTENT_INVALID_WIDGET_3,
widgetClassOrAlias,
elementName,
contentDefinition.getSchemaLocation()));
}
}
m_elementWidgets.put(elementName, widget);
}
/**
* Returns the configured default locales for the content of the given resource.
*
* @param cms the cms context
* @param resource the resource path to get the default locales for
*
* @return the default locales of the resource
*/
protected List getLocalesForResource(CmsObject cms, String resource) {
List locales = OpenCms.getLocaleManager().getDefaultLocales(cms, resource);
if ((locales == null) || locales.isEmpty()) {
locales = OpenCms.getLocaleManager().getAvailableLocales();
}
return locales;
}
/**
* Returns the category reference path for the given value.
*
* @param cms the cms context
* @param value the xml content value
*
* @return the category reference path for the given value
*/
protected String getReferencePath(CmsObject cms, I_CmsXmlContentValue value) {
// get the original file instead of the temp file
CmsFile file = value.getDocument().getFile();
String resourceName = cms.getSitePath(file);
if (CmsWorkplace.isTemporaryFile(file)) {
StringBuffer result = new StringBuffer(resourceName.length() + 2);
result.append(CmsResource.getFolderPath(resourceName));
result.append(CmsResource.getName(resourceName).substring(1));
resourceName = result.toString();
}
try {
List listsib = cms.readSiblings(resourceName, CmsResourceFilter.ALL);
for (int i = 0; i < listsib.size(); i++) {
CmsResource resource = listsib.get(i);
// get the default locale of the resource and set the categories
List locales = getLocalesForResource(cms, cms.getSitePath(resource));
for (Locale l : locales) {
if (value.getLocale().equals(l)) {
return cms.getSitePath(resource);
}
}
}
} catch (CmsVfsResourceNotFoundException e) {
// may hapen if editing a new resource
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
} catch (CmsException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getLocalizedMessage(), e);
}
}
// if the locale can not be found, just take the current file
return cms.getSitePath(file);
}
/**
* Returns the validation message to be displayed if a certain rule was violated.
*
* @param cms the current users OpenCms context
* @param value the value to validate
* @param regex the rule that was violated
* @param valueStr the string value of the given value
* @param matchResult if false, the rule was negated
* @param isWarning if true, this validation indicate a warning, otherwise an error
*
* @return the validation message to be displayed
*/
protected String getValidationMessage(
CmsObject cms,
I_CmsXmlContentValue value,
String regex,
String valueStr,
boolean matchResult,
boolean isWarning) {
String message = null;
if (isWarning) {
message = m_validationWarningMessages.get(value.getName());
} else {
message = m_validationErrorMessages.get(value.getName());
}
if (message == null) {
if (isWarning) {
message = MESSAGE_VALIDATION_DEFAULT_WARNING;
} else {
message = MESSAGE_VALIDATION_DEFAULT_ERROR;
}
}
// create additional macro values
Map additionalValues = new HashMap();
additionalValues.put(CmsMacroResolver.KEY_VALIDATION_VALUE, valueStr);
additionalValues.put(CmsMacroResolver.KEY_VALIDATION_REGEX, ((!matchResult) ? "!" : "") + regex);
additionalValues.put(CmsMacroResolver.KEY_VALIDATION_PATH, value.getPath());
CmsMacroResolver resolver = CmsMacroResolver.newInstance().setCmsObject(cms).setMessages(
getMessages(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms))).setAdditionalMacros(additionalValues);
return resolver.resolveMacros(message);
}
/**
* Called when this content handler is initialized.
*/
protected void init() {
m_elementMappings = new HashMap>();
m_elementWidgets = new HashMap();
m_validationErrorRules = new HashMap();
m_validationErrorMessages = new HashMap();
m_validationWarningRules = new HashMap();
m_validationWarningMessages = new HashMap();
m_defaultValues = new HashMap();
m_configurationValues = new HashMap();
m_searchSettings = new HashMap();
m_relations = new HashMap();
m_relationChecks = new HashMap();
m_previewLocation = null;
m_modelFolder = null;
m_tabs = new ArrayList();
m_cssHeadIncludes = new LinkedHashSet();
m_jsHeadIncludes = new LinkedHashSet();
m_settings = new LinkedHashMap();
m_titleMappings = new ArrayList(2);
m_formatters = new ArrayList();
m_searchFields = new HashMap();
m_searchFieldsPage = new HashMap();
m_allowedTemplates = new CmsDefaultSet();
m_allowedTemplates.setDefaultMembership(true);
m_displayTypes = new HashMap();
m_synchronizations = new ArrayList();
m_editorChangeHandlers = new ArrayList();
}
/**
* Initializes the default values for this content handler.
*
* Using the default values from the appinfo node, it's possible to have more
* sophisticated logic for generating the defaults then just using the XML schema "default"
* attribute.
*
* @param root the "defaults" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the default values belong to
* @throws CmsXmlException if something goes wrong
*/
protected void initDefaultValues(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
Iterator i = CmsXmlGenericWrapper.elementIterator(root, APPINFO_DEFAULT);
while (i.hasNext()) {
// iterate all "default" elements in the "defaults" node
Element element = i.next();
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String defaultValue = element.attributeValue(APPINFO_ATTR_VALUE);
String resolveMacrosValue = element.attributeValue(APPINFO_ATTR_RESOLVE_MACROS);
if ((elementName != null) && (defaultValue != null)) {
// add a default value mapping for the element
addDefault(contentDefinition, elementName, defaultValue, resolveMacrosValue);
}
}
}
/**
* Initializes the default complex widget.
*
* @param element the element in which the default complex widget is configured
*/
protected void initDefaultWidget(Element element) {
m_defaultWidget = element.attributeValue(APPINFO_ATTR_WIDGET);
m_defaultWidgetConfig = element.attributeValue(APPINFO_ATTR_CONFIGURATION);
try {
m_defaultWidgetInstance = (I_CmsComplexWidget)(Class.forName(m_defaultWidget).newInstance());
} catch (Exception e) {
LOG.error(e);
}
}
/**
* Initializes the editor change handlers.
*
* @param element the editorchangehandlers node of the app info
*/
protected void initEditorChangeHandlers(Element element) {
Iterator i = CmsXmlGenericWrapper.elementIterator(element, APPINFO_EDITOR_CHANGE_HANDLER);
while (i.hasNext()) {
// iterate all "default" elements in the "defaults" node
Element handlerElement = i.next();
String handlerClass = handlerElement.attributeValue(APPINFO_ATTR_CLASS);
String configuration = handlerElement.attributeValue(APPINFO_ATTR_CONFIGURATION);
String scope = handlerElement.attributeValue(APPINFO_ATTR_SCOPE);
try {
I_CmsXmlContentEditorChangeHandler handler = (I_CmsXmlContentEditorChangeHandler)Class.forName(
handlerClass).newInstance();
handler.setConfiguration(configuration);
handler.setScope(scope);
m_editorChangeHandlers.add(handler);
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
}
}
/**
* Initializes the formatters for this content handler.
*
* @param root the "formatters" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the formatters belong to
*/
protected void initFormatters(Element root, CmsXmlContentDefinition contentDefinition) {
// reading the include resources common for all formatters
Iterator itFormatter = CmsXmlGenericWrapper.elementIterator(root, APPINFO_FORMATTER);
while (itFormatter.hasNext()) {
// iterate all "formatter" elements in the "formatters" node
Element element = itFormatter.next();
String type = element.attributeValue(APPINFO_ATTR_TYPE);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(type)) {
// if not set use "*" as default for type
type = CmsFormatterBean.WILDCARD_TYPE;
}
String jspRootPath = element.attributeValue(APPINFO_ATTR_URI);
String minWidthStr = element.attributeValue(APPINFO_ATTR_MINWIDTH);
String maxWidthStr = element.attributeValue(APPINFO_ATTR_MAXWIDTH);
String preview = element.attributeValue(APPINFO_ATTR_PREVIEW);
String searchContent = element.attributeValue(APPINFO_ATTR_SEARCHCONTENT);
m_formatters.add(
new CmsFormatterBean(
type,
jspRootPath,
minWidthStr,
maxWidthStr,
preview,
searchContent,
contentDefinition.getSchemaLocation()));
}
}
/**
* Initializes the head includes for this content handler.
*
* @param root the "headincludes" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the head-includes belong to
*/
protected void initHeadIncludes(Element root, CmsXmlContentDefinition contentDefinition) {
Iterator itInclude = CmsXmlGenericWrapper.elementIterator(root, APPINFO_HEAD_INCLUDE);
while (itInclude.hasNext()) {
Element element = itInclude.next();
String type = element.attributeValue(APPINFO_ATTR_TYPE);
String uri = element.attributeValue(APPINFO_ATTR_URI);
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(uri)) {
if (ATTRIBUTE_INCLUDE_TYPE_CSS.equals(type)) {
m_cssHeadIncludes.add(uri);
} else if (ATTRIBUTE_INCLUDE_TYPE_JAVASCRIPT.equals(type)) {
m_jsHeadIncludes.add(uri);
}
}
}
}
/**
* Initializes the layout for this content handler.
*
* Unless otherwise instructed, the editor uses one specific GUI widget for each
* XML value schema type. For example, for a {@link org.opencms.xml.types.CmsXmlStringValue}
* the default widget is the {@link org.opencms.widgets.CmsInputWidget}.
* However, certain values can also use more then one widget, for example you may
* also use a {@link org.opencms.widgets.CmsCheckboxWidget} for a String value,
* and as a result the Strings possible values would be eithe "false"
or "true"
,
* but nevertheless be a String.
*
* The widget to use can further be controlled using the widget
attribute.
* You can specify either a valid widget alias such as StringWidget
,
* or the name of a Java class that implements {@link I_CmsWidget}
.
*
* Configuration options to the widget can be passed using the configuration
* attribute. You can specify any String as configuration. This String is then passed
* to the widget during initialization. It's up to the individual widget implementation
* to interpret this configuration String.
*
* @param root the "layouts" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the layout belongs to
*
* @throws CmsXmlException if something goes wrong
*/
protected void initLayouts(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
m_useAcacia = safeParseBoolean(root.attributeValue(ATTR_USE_ACACIA), true);
Iterator i = CmsXmlGenericWrapper.elementIterator(root, APPINFO_LAYOUT);
while (i.hasNext()) {
// iterate all "layout" elements in the "layouts" node
Element element = i.next();
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String widgetClassOrAlias = element.attributeValue(APPINFO_ATTR_WIDGET);
String configuration = element.attributeValue(APPINFO_ATTR_CONFIGURATION);
String displayCompact = element.attributeValue(APPINFO_ATTR_DISPLAY);
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(displayCompact) && (elementName != null)) {
addDisplayType(contentDefinition, elementName, DisplayType.valueOf(displayCompact));
}
if ((elementName != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(widgetClassOrAlias)) {
// add a widget mapping for the element
addWidget(contentDefinition, elementName, widgetClassOrAlias);
if (configuration != null) {
addConfiguration(contentDefinition, elementName, configuration);
}
}
}
}
/**
* Initializes the element mappings for this content handler.
*
* Element mappings allow storing values from the XML content in other locations.
* For example, if you have an element called "Title", it's likely a good idea to
* store the value of this element also in the "Title" property of a XML content resource.
*
* @param root the "mappings" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the mappings belong to
* @throws CmsXmlException if something goes wrong
*/
protected void initMappings(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
Iterator i = CmsXmlGenericWrapper.elementIterator(root, APPINFO_MAPPING);
while (i.hasNext()) {
// iterate all "mapping" elements in the "mappings" node
Element element = i.next();
// this is a mapping node
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String maptoName = element.attributeValue(APPINFO_ATTR_MAPTO);
String useDefault = element.attributeValue(APPINFO_ATTR_USE_DEFAULT);
if ((elementName != null) && (maptoName != null)) {
// add the element mapping
addMapping(contentDefinition, elementName, maptoName, useDefault);
}
}
}
/**
* Initializes the folder containing the model file(s) for this content handler.
*
* @param root the "modelfolder" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the model folder belongs to
* @throws CmsXmlException if something goes wrong
*/
protected void initModelFolder(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
String master = root.attributeValue(APPINFO_ATTR_URI);
if (master == null) {
throw new CmsXmlException(
Messages.get().container(
Messages.ERR_XMLCONTENT_MISSING_MODELFOLDER_URI_2,
root.getName(),
contentDefinition.getSchemaLocation()));
}
m_modelFolder = master;
}
/**
* Initializes the preview location for this content handler.
*
* @param root the "preview" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the validation rules belong to
* @throws CmsXmlException if something goes wrong
*/
protected void initPreview(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
String preview = root.attributeValue(APPINFO_ATTR_URI);
if (preview == null) {
throw new CmsXmlException(
Messages.get().container(
Messages.ERR_XMLCONTENT_MISSING_PREVIEW_URI_2,
root.getName(),
contentDefinition.getSchemaLocation()));
}
m_previewLocation = preview;
}
/**
* Initializes the relation configuration for this content handler.
*
* OpenCms performs link checks for all OPTIONAL links defined in XML content values of type
* OpenCmsVfsFile. However, for most projects in the real world a more fine-grained control
* over the link check process is required. For these cases, individual relation behavior can
* be defined for the appinfo node.
*
* Additional here can be defined an optional type for the relations, for instance.
*
* @param root the "relations" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the check rules belong to
*
* @throws CmsXmlException if something goes wrong
*/
protected void initRelations(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
Iterator i = CmsXmlGenericWrapper.elementIterator(root, APPINFO_RELATION);
while (i.hasNext()) {
// iterate all "checkrule" elements in the "checkrule" node
Element element = i.next();
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String invalidate = element.attributeValue(APPINFO_ATTR_INVALIDATE);
if (invalidate != null) {
invalidate = invalidate.toUpperCase();
}
String type = element.attributeValue(APPINFO_ATTR_TYPE);
if (type != null) {
type = type.toLowerCase();
}
if (elementName != null) {
// add a check rule for the element
addCheckRule(contentDefinition, elementName, invalidate, type);
}
}
}
/**
* Initializes the resource bundle to use for localized messages in this content handler.
*
* @param root the "resourcebundle" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the validation rules belong to
* @param single if true
we process the classic sinle line entry, otherwise it's the multiple line setting
*
* @throws CmsXmlException if something goes wrong
*/
protected void initResourceBundle(Element root, CmsXmlContentDefinition contentDefinition, boolean single)
throws CmsXmlException {
if (m_messageBundleNames == null) {
// it's uncommon to have more then one bundle so just initialize an array length of 2
m_messageBundleNames = new ArrayList(2);
}
if (single) {
// single "resourcebundle" node
String messageBundleName = root.attributeValue(APPINFO_ATTR_NAME);
if (messageBundleName == null) {
throw new CmsXmlException(
Messages.get().container(
Messages.ERR_XMLCONTENT_MISSING_RESOURCE_BUNDLE_NAME_2,
root.getName(),
contentDefinition.getSchemaLocation()));
}
if (!m_messageBundleNames.contains(messageBundleName)) {
// avoid duplicates
m_messageBundleNames.add(messageBundleName);
}
// clear the cached resource bundles for this bundle
CmsResourceBundleLoader.flushBundleCache(messageBundleName, false);
} else {
// multiple "resourcebundles" node
// get an iterator for all "propertybundle" subnodes
Iterator propertybundles = CmsXmlGenericWrapper.elementIterator(root, APPINFO_PROPERTYBUNDLE);
while (propertybundles.hasNext()) {
// iterate all "propertybundle" elements in the "resourcebundle" node
Element propBundle = propertybundles.next();
String propertyBundleName = propBundle.attributeValue(APPINFO_ATTR_NAME);
if (!m_messageBundleNames.contains(propertyBundleName)) {
// avoid duplicates
m_messageBundleNames.add(propertyBundleName);
}
// clear the cached resource bundles for this bundle
CmsResourceBundleLoader.flushBundleCache(propertyBundleName, false);
}
// get an iterator for all "xmlbundle" subnodes
Iterator xmlbundles = CmsXmlGenericWrapper.elementIterator(root, APPINFO_XMLBUNDLE);
while (xmlbundles.hasNext()) {
Element xmlbundle = xmlbundles.next();
String xmlBundleName = xmlbundle.attributeValue(APPINFO_ATTR_NAME);
// cache the bundle from the XML
if (!m_messageBundleNames.contains(xmlBundleName)) {
// avoid duplicates
m_messageBundleNames.add(xmlBundleName);
}
// clear the cached resource bundles for this bundle
CmsResourceBundleLoader.flushBundleCache(xmlBundleName, true);
Iterator bundles = CmsXmlGenericWrapper.elementIterator(xmlbundle, APPINFO_BUNDLE);
while (bundles.hasNext()) {
// iterate all "bundle" elements in the "xmlbundle" node
Element bundle = bundles.next();
String localeStr = bundle.attributeValue(APPINFO_ATTR_LOCALE);
Locale locale;
if (CmsStringUtil.isEmptyOrWhitespaceOnly(localeStr)) {
// no locale set, so use no locale
locale = null;
} else {
// use provided locale
locale = CmsLocaleManager.getLocale(localeStr);
}
boolean isDefaultLocaleAndNotNull = (locale != null)
&& locale.equals(CmsLocaleManager.getDefaultLocale());
CmsListResourceBundle xmlBundle = null;
Iterator resources = CmsXmlGenericWrapper.elementIterator(bundle, APPINFO_RESOURCE);
while (resources.hasNext()) {
// now collect all resource bundle keys
Element resource = resources.next();
String key = resource.attributeValue(APPINFO_ATTR_KEY);
String value = resource.attributeValue(APPINFO_ATTR_VALUE);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
// read from inside XML tag if value attribute is not set
value = resource.getTextTrim();
}
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(key)
&& CmsStringUtil.isNotEmptyOrWhitespaceOnly(value)) {
if (xmlBundle == null) {
// use lazy initilaizing of the bundle
xmlBundle = new CmsListResourceBundle();
}
xmlBundle.addMessage(key.trim(), value.trim());
}
}
if (xmlBundle != null) {
CmsResourceBundleLoader.addBundleToCache(xmlBundleName, locale, xmlBundle);
if (isDefaultLocaleAndNotNull) {
CmsResourceBundleLoader.addBundleToCache(xmlBundleName, null, xmlBundle);
}
}
}
}
}
}
/**
* Initializes the search exclusions values for this content handler.
*
* For the full text search, the value of all elements in one locale of the XML content are combined
* to one big text, which is referred to as the "content" in the context of the full text search.
* With this option, it is possible to hide certain elements from this "content" that does not make sense
* to include in the full text search.
*
* @param root the "searchsettings" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the default values belong to
*
* @throws CmsXmlException if something goes wrong
*/
protected void initSearchSettings(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
String containerPageOnly = root.attributeValue(APPINFO_ATTR_CONTAINER_PAGE_ONLY);
m_containerPageOnly = (!CmsStringUtil.isEmpty(containerPageOnly))
&& (Boolean.valueOf(containerPageOnly).booleanValue());
Iterator i = CmsXmlGenericWrapper.elementIterator(root, APPINFO_SEARCHSETTING);
while (i.hasNext()) {
Element element = i.next();
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String searchContent = element.attributeValue(APPINFO_ATTR_SEARCHCONTENT);
boolean include = (CmsStringUtil.isEmpty(searchContent)) || (Boolean.valueOf(searchContent).booleanValue());
if (elementName != null) {
addSearchSetting(contentDefinition, elementName, Boolean.valueOf(include));
}
Iterator it = CmsXmlGenericWrapper.elementIterator(element, APPINFO_SOLR_FIELD);
Element solrElement;
while (it.hasNext()) {
solrElement = it.next();
String localeNames = solrElement.attributeValue(APPINFO_ATTR_LOCALE);
boolean localized = true;
if ((localeNames != null)
&& (localeNames.equals("none") || localeNames.equals("null") || localeNames.trim().equals(""))) {
localized = false;
}
List locales = OpenCms.getLocaleManager().getAvailableLocales(localeNames);
if (localized && ((locales == null) || locales.isEmpty())) {
locales = OpenCms.getLocaleManager().getAvailableLocales();
} else if (locales.isEmpty()) {
locales.add(CmsLocaleManager.getDefaultLocale());
}
for (Locale locale : locales) {
String targetField = solrElement.attributeValue(APPINFO_ATTR_TARGET_FIELD);
if (localized) {
targetField = targetField + "_" + locale.toString();
}
String sourceField = solrElement.attributeValue(APPINFO_ATTR_SOURCE_FIELD);
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(sourceField)) {
int lastUnderScore = sourceField.lastIndexOf("_");
if (lastUnderScore > 0) {
sourceField = sourceField.substring(lastUnderScore);
}
targetField += sourceField;
}
String copyFieldNames = solrElement.attributeValue(APPINFO_ATTR_COPY_FIELDS, "");
List copyFields = CmsStringUtil.splitAsList(copyFieldNames, ',');
String defaultValue = solrElement.attributeValue(APPINFO_ATTR_DEFAULT);
String defaultBoost = String.valueOf(CmsSearchField.BOOST_DEFAULT);
float boost = Float.valueOf(
solrElement.attributeValue(APPINFO_ATTR_BOOST, defaultBoost)).floatValue();
CmsSolrField field = new CmsSolrField(targetField, copyFields, locale, defaultValue, boost);
// create the field mappings for this element
Iterator ite = CmsXmlGenericWrapper.elementIterator(solrElement, APPINFO_ATTR_MAPPING);
while (ite.hasNext()) {
Element mappingElement = ite.next();
field.addMapping(createSearchFieldMapping(contentDefinition, mappingElement, locale));
}
// if no mapping was defined yet, create a mapping for the element itself
if ((field.getMappings() == null) || field.getMappings().isEmpty()) {
String param = locale.toString() + "|" + elementName;
CmsSearchFieldMapping map = new CmsSearchFieldMapping(CmsSearchFieldMappingType.ITEM, param);
field.addMapping(map);
}
Set mappingTypes = parseSearchMappingTypes(solrElement);
for (I_CmsXmlContentHandler.MappingType type : mappingTypes) {
addSearchField(contentDefinition, field, type);
}
}
}
}
}
/**
* Initializes the element settings for this content handler.
*
* @param root the "settings" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the element settings belong to
*/
protected void initSettings(Element root, CmsXmlContentDefinition contentDefinition) {
Iterator itProperties = CmsXmlGenericWrapper.elementIterator(root, APPINFO_SETTING);
while (itProperties.hasNext()) {
Element element = itProperties.next();
CmsXmlContentProperty setting = new CmsXmlContentProperty(
element.attributeValue(APPINFO_ATTR_NAME),
element.attributeValue(APPINFO_ATTR_TYPE),
element.attributeValue(APPINFO_ATTR_WIDGET),
element.attributeValue(APPINFO_ATTR_WIDGET_CONFIG),
element.attributeValue(APPINFO_ATTR_RULE_REGEX),
element.attributeValue(APPINFO_ATTR_RULE_TYPE),
element.attributeValue(APPINFO_ATTR_DEFAULT),
element.attributeValue(APPINFO_ATTR_NICE_NAME),
element.attributeValue(APPINFO_ATTR_DESCRIPTION),
element.attributeValue(APPINFO_ATTR_ERROR),
element.attributeValue(APPINFO_ATTR_PREFERFOLDER));
String name = setting.getName();
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(name)) {
m_settings.put(name, setting);
}
}
}
/**
* Initializes the locale synchronizations elements.
*
* @param root the synchronizations element of the content schema appinfo.
* @param contentDefinition the content definition
*/
protected void initSynchronizations(Element root, CmsXmlContentDefinition contentDefinition) {
List elements = new ArrayList(CmsXmlGenericWrapper.elements(root, APPINFO_SYNCHRONIZATION));
for (Element element : elements) {
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
m_synchronizations.add(elementName);
}
}
/**
* Initializes the tabs for this content handler.
*
* @param root the "tabs" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the tabs belong to
*/
protected void initTabs(Element root, CmsXmlContentDefinition contentDefinition) {
if (Boolean.valueOf(root.attributeValue(APPINFO_ATTR_USEALL, CmsStringUtil.FALSE)).booleanValue()) {
// all first level elements should be treated as tabs
Iterator i = contentDefinition.getTypeSequence().iterator();
while (i.hasNext()) {
// get the type
I_CmsXmlSchemaType type = i.next();
m_tabs.add(new CmsXmlContentTab(type.getName()));
}
} else {
// manual definition of tabs
Iterator i = CmsXmlGenericWrapper.elementIterator(root, APPINFO_TAB);
while (i.hasNext()) {
// iterate all "tab" elements in the "tabs" node
Element element = i.next();
// this is a tab node
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String collapseValue = element.attributeValue(APPINFO_ATTR_COLLAPSE, CmsStringUtil.TRUE);
Node descriptionNode = element.selectSingleNode(APPINFO_ATTR_DESCRIPTION + "/text()");
String description = null;
if (descriptionNode != null) {
description = descriptionNode.getText();
}
String tabName = element.attributeValue(APPINFO_ATTR_NAME, elementName);
if (elementName != null) {
// add the element tab
m_tabs.add(
new CmsXmlContentTab(
elementName,
Boolean.valueOf(collapseValue).booleanValue(),
tabName,
description));
}
}
// check if first element has been defined as tab
I_CmsXmlSchemaType type = contentDefinition.getTypeSequence().get(0);
CmsXmlContentTab tab = new CmsXmlContentTab(type.getName());
if (!m_tabs.contains(tab)) {
m_tabs.add(0, tab);
}
}
}
/**
* Initializes the forbidden template contexts.
*
* @param root the root XML element
* @param contentDefinition the content definition
*/
protected void initTemplates(Element root, CmsXmlContentDefinition contentDefinition) {
String strEnabledByDefault = root.attributeValue(ATTR_ENABLED_BY_DEFAULT);
m_allowedTemplates.setDefaultMembership(safeParseBoolean(strEnabledByDefault, true));
List elements = root.selectNodes(APPINFO_TEMPLATE);
for (Element elem : elements) {
boolean enabled = safeParseBoolean(elem.attributeValue(ATTR_ENABLED), true);
String templateName = elem.getText().trim();
m_allowedTemplates.setContains(templateName, enabled);
}
m_allowedTemplates.freeze();
}
/**
* Initializes the validation rules this content handler.
*
* OpenCms always performs XML schema validation for all XML contents. However,
* for most projects in the real world a more fine-grained control over the validation process is
* required. For these cases, individual validation rules can be defined for the appinfo node.
*
* @param root the "validationrules" element from the appinfo node of the XML content definition
* @param contentDefinition the content definition the validation rules belong to
*
* @throws CmsXmlException if something goes wrong
*/
protected void initValidationRules(Element root, CmsXmlContentDefinition contentDefinition) throws CmsXmlException {
List elements = new ArrayList(CmsXmlGenericWrapper.elements(root, APPINFO_RULE));
elements.addAll(CmsXmlGenericWrapper.elements(root, APPINFO_VALIDATIONRULE));
Iterator i = elements.iterator();
while (i.hasNext()) {
// iterate all "rule" or "validationrule" elements in the "validationrules" node
Element element = i.next();
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String regex = element.attributeValue(APPINFO_ATTR_REGEX);
String type = element.attributeValue(APPINFO_ATTR_TYPE);
if (type != null) {
type = type.toLowerCase();
}
String message = element.attributeValue(APPINFO_ATTR_MESSAGE);
if ((elementName != null) && (regex != null)) {
// add a validation rule for the element
addValidationRule(
contentDefinition,
elementName,
regex,
message,
APPINFO_ATTR_TYPE_WARNING.equals(type));
}
}
}
/**
* Initializes the content visibility settings.
*
* @param root the visibilities appinfo element
* @param contentDefinition the content definition
*/
protected void initVisibilities(Element root, CmsXmlContentDefinition contentDefinition) {
m_visibilityConfigurations = new HashMap();
String mainHandlerClassName = root.attributeValue(APPINFO_ATTR_CLASS);
// using self as the default visibility handler implementation
I_CmsXmlContentVisibilityHandler mainHandler = this;
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(mainHandlerClassName)) {
try {
// in case there is a main handler configured, try to instanciate it
Class> handlerClass = Class.forName(mainHandlerClassName);
mainHandler = (I_CmsXmlContentVisibilityHandler)handlerClass.newInstance();
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
}
List elements = new ArrayList(CmsXmlGenericWrapper.elements(root, APPINFO_VISIBILITY));
for (Element element : elements) {
try {
String elementName = element.attributeValue(APPINFO_ATTR_ELEMENT);
String handlerClassName = element.attributeValue(APPINFO_ATTR_CLASS);
String params = element.attributeValue(APPINFO_ATTR_PARAMS);
I_CmsXmlContentVisibilityHandler handler = null;
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(handlerClassName)) {
Class> handlerClass = Class.forName(handlerClassName);
handler = (I_CmsXmlContentVisibilityHandler)handlerClass.newInstance();
} else {
handler = mainHandler;
}
m_visibilityConfigurations.put(elementName, new VisibilityConfiguration(handler, params));
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
}
}
/**
* Returns the is-invalidate-parent flag for the given xpath.
*
* @param xpath the path to get the check rule for
*
* @return the configured is-invalidate-parent flag for the given xpath
*/
protected boolean isInvalidateParent(String xpath) {
if (!CmsXmlUtils.isDeepXpath(xpath)) {
return false;
}
Boolean isInvalidateParent = null;
// look up the default from the configured mappings
isInvalidateParent = m_relationChecks.get(xpath);
if (isInvalidateParent == null) {
// no value found, try default xpath
String path = CmsXmlUtils.removeXpath(xpath);
// look up the default value again without indexes
isInvalidateParent = m_relationChecks.get(path);
}
if (isInvalidateParent == null) {
return false;
}
return isInvalidateParent.booleanValue();
}
/**
* Returns the localized resource string for a given message key according to the configured resource bundle
* of this content handler.
*
* If the key was not found in the configured bundle, or no bundle is configured for this
* content handler, the return value is
* "??? " + keyName + " ???"
.
*
* @param keyName the key for the desired string
* @param locale the locale to get the key from
*
* @return the resource string for the given key
*
* @see CmsMessages#formatUnknownKey(String)
* @see CmsMessages#isUnknownKey(String)
*/
protected String key(String keyName, Locale locale) {
CmsMessages messages = getMessages(locale);
if (messages != null) {
return messages.key(keyName);
}
return CmsMessages.formatUnknownKey(keyName);
}
/**
* @param solrElement the XML node of the <solrfield> node
* @return parsed values of the attribute "addto"
*/
protected Set parseSearchMappingTypes(Element solrElement) {
Set result = new HashSet();
String mappingTypes = solrElement.attributeValue(APPINFO_ATTR_ADD_TO);
if (mappingTypes != null) {
String[] types = mappingTypes.split(",");
for (int i = 0; i < types.length; i++) {
String type = types[i].trim();
if (APPINFO_VALUE_ADD_TO_PAGE.equals(type)) {
result.add(MappingType.PAGE);
} else if (APPINFO_VALUE_ADD_TO_CONTENT.equals(type)) {
result.add(MappingType.ELEMENT);
}
}
} else {
// for backwards compatibility
result.add(MappingType.ELEMENT);
}
return result;
}
/**
* Removes property values on resources for non-existing, optional elements.
*
* @param cms the current users OpenCms context
* @param file the file which is currently being prepared for writing
* @param content the XML content to remove the property values for
* @throws CmsException in case of read/write errors accessing the OpenCms VFS
*/
protected void removeEmptyMappings(CmsObject cms, CmsFile file, CmsXmlContent content) throws CmsException {
List siblings = null;
CmsObject rootCms = null;
Iterator>> allMappings = m_elementMappings.entrySet().iterator();
while (allMappings.hasNext()) {
Map.Entry> e = allMappings.next();
String path = e.getKey();
List mappings = e.getValue();
if (mappings == null) {
// nothing to do if we have no mappings at all
continue;
}
if ((siblings == null) || (rootCms == null)) {
// create OpenCms user context initialized with "/" as site root to read all siblings
rootCms = OpenCms.initCmsObject(cms);
rootCms.getRequestContext().setSiteRoot("/");
siblings = rootCms.readSiblings(content.getFile().getRootPath(), CmsResourceFilter.IGNORE_EXPIRATION);
}
for (int v = mappings.size() - 1; v >= 0; v--) {
String mapping = mappings.get(v);
if (mapping.startsWith(MAPTO_ATTRIBUTE)
|| mapping.startsWith(MAPTO_PROPERTY_LIST)
|| mapping.startsWith(MAPTO_PROPERTY)) {
for (int i = 0; i < siblings.size(); i++) {
// get siblings filename and locale
String filename = siblings.get(i).getRootPath();
Locale locale = OpenCms.getLocaleManager().getDefaultLocale(rootCms, filename);
if (!content.hasLocale(locale)) {
// only remove property if the locale fits
continue;
}
if (content.hasValue(path, locale)) {
// value is available, property must be kept
continue;
}
if (mapping.startsWith(MAPTO_PROPERTY_LIST) || mapping.startsWith(MAPTO_PROPERTY)) {
String property;
boolean shared = false;
if (mapping.startsWith(MAPTO_PROPERTY_LIST_INDIVIDUAL)) {
property = mapping.substring(MAPTO_PROPERTY_LIST_INDIVIDUAL.length());
} else if (mapping.startsWith(MAPTO_PROPERTY_LIST_SHARED)) {
property = mapping.substring(MAPTO_PROPERTY_LIST_SHARED.length());
shared = true;
} else if (mapping.startsWith(MAPTO_PROPERTY_LIST)) {
property = mapping.substring(MAPTO_PROPERTY_LIST.length());
} else if (mapping.startsWith(MAPTO_PROPERTY_SHARED)) {
property = mapping.substring(MAPTO_PROPERTY_SHARED.length());
shared = true;
} else if (mapping.startsWith(MAPTO_PROPERTY_INDIVIDUAL)) {
property = mapping.substring(MAPTO_PROPERTY_INDIVIDUAL.length());
} else {
property = mapping.substring(MAPTO_PROPERTY.length());
}
rootCms.writePropertyObject(
filename,
new CmsProperty(
property,
CmsProperty.DELETE_VALUE,
shared ? CmsProperty.DELETE_VALUE : null));
} else if (mapping.startsWith(MAPTO_ATTRIBUTE)) {
if (mapping.equals(MAPTO_ATTRIBUTE + ATTRIBUTE_DATERELEASED)) {
rootCms.setDateReleased(filename, CmsResource.DATE_RELEASED_DEFAULT, false);
if (filename.equals(rootCms.getSitePath(file))) {
file.setDateReleased(CmsResource.DATE_RELEASED_DEFAULT);
}
} else if (mapping.equals(MAPTO_ATTRIBUTE + ATTRIBUTE_DATEEXPIRED)) {
rootCms.setDateExpired(filename, CmsResource.DATE_EXPIRED_DEFAULT, false);
if (filename.equals(rootCms.getSitePath(file))) {
file.setDateExpired(CmsResource.DATE_EXPIRED_DEFAULT);
}
}
}
}
} else if (mapping.startsWith(MAPTO_PERMISSION)) {
for (int i = 0; i < siblings.size(); i++) {
// get siblings filename and locale
String filename = siblings.get(i).getRootPath();
Locale locale = OpenCms.getLocaleManager().getDefaultLocale(rootCms, filename);
if (!content.hasLocale(locale)) {
// only remove property if the locale fits
continue;
}
if (content.hasValue(path, locale)) {
// value is available, property must be kept
continue;
}
// remove all existing permissions from the file
List aces = rootCms.getAccessControlEntries(filename, false);
for (Iterator j = aces.iterator(); j.hasNext();) {
CmsAccessControlEntry ace = j.next();
if (ace.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_ID)) {
// remove the entry "All others", which has to be treated in a special way
rootCms.rmacc(
filename,
CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_NAME,
CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_ID.toString());
} else {
// this is a group or user principal
I_CmsPrincipal principal = CmsPrincipal.readPrincipal(rootCms, ace.getPrincipal());
if (principal.isGroup()) {
rootCms.rmacc(filename, I_CmsPrincipal.PRINCIPAL_GROUP, principal.getName());
} else if (principal.isUser()) {
rootCms.rmacc(filename, I_CmsPrincipal.PRINCIPAL_USER, principal.getName());
}
}
}
}
}
}
}
}
/**
* Resolves those mappings for which no content value exists and useDefault is set to true.
*
* @param cms the CMS context to use
* @param file the content file
* @param content the content object
*
* @throws CmsException if something goes wrong
*/
protected void resolveDefaultMappings(CmsObject cms, CmsFile file, CmsXmlContent content) throws CmsException {
for (Map.Entry> e : m_elementMappings.entrySet()) {
String path = e.getKey();
List mappings = e.getValue();
if (mappings == null) {
// nothing to do if we have no mappings at all
continue;
}
for (int v = mappings.size() - 1; v >= 0; v--) {
String mapping = mappings.get(v);
if (!isMappingUsingDefault(path, mapping)) {
continue;
}
for (Locale locale : content.getLocales()) {
if (content.hasValue(path, locale)) {
continue;
} else {
String defaultValue = getDefault(cms, file, null, path, locale);
if (defaultValue != null) {
resolveMapping(cms, content, path, true, 0, locale, defaultValue);
}
}
}
}
}
}
/**
* Validates if the given appinfo
element node from the XML content definition schema
* is valid according the the capabilities of this content handler.
*
* @param appinfoElement the appinfo
element node to validate
*
* @throws CmsXmlException in case the element validation fails
*/
protected void validateAppinfoElement(Element appinfoElement) throws CmsXmlException {
// create a document to validate
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement(APPINFO_APPINFO);
// attach the default appinfo schema
root.add(I_CmsXmlSchemaType.XSI_NAMESPACE);
root.addAttribute(I_CmsXmlSchemaType.XSI_NAMESPACE_ATTRIBUTE_NO_SCHEMA_LOCATION, APPINFO_SCHEMA_SYSTEM_ID);
// append the content from the appinfo node in the content definition
root.appendContent(appinfoElement);
// now validate the document with the default appinfo schema
CmsXmlUtils.validateXmlStructure(doc, CmsEncoder.ENCODING_UTF_8, new CmsXmlEntityResolver(null));
}
/**
* The errorHandler parameter is optional, if null
is given a new error handler
* instance must be created.
*
* @param cms the current OpenCms user context
* @param value the value to resolve the validation rules for
* @param errorHandler (optional) an error handler instance that contains previous error or warnings
*
* @return an error handler that contains all errors and warnings currently found
*/
protected CmsXmlContentErrorHandler validateCategories(
CmsObject cms,
I_CmsXmlContentValue value,
CmsXmlContentErrorHandler errorHandler) {
if (!value.isSimpleType()) {
// do not validate complex types
return errorHandler;
}
I_CmsWidget widget = null;
try {
widget = value.getContentDefinition().getContentHandler().getWidget(value);
} catch (CmsXmlException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getLocalizedMessage(), e);
}
}
if (!(widget instanceof CmsCategoryWidget)) {
// do not validate widget that are not category widgets
return errorHandler;
}
String stringValue = value.getStringValue(cms);
if (stringValue.isEmpty()) {
return errorHandler;
}
try {
String[] values = stringValue.split(",");
for (int i = 0; i < values.length; i++) {
String val = values[i];
String catPath = CmsCategoryService.getInstance().getCategory(cms, val).getPath();
String refPath = getReferencePath(cms, value);
CmsCategoryService.getInstance().readCategory(cms, catPath, refPath);
if (((CmsCategoryWidget)widget).isOnlyLeafs()) {
if (!CmsCategoryService.getInstance().readCategories(cms, catPath, false, refPath).isEmpty()) {
errorHandler.addError(
value,
Messages.get().getBundle(value.getLocale()).key(
Messages.GUI_CATEGORY_CHECK_NOLEAF_ERROR_0));
}
}
}
} catch (CmsDataAccessException e) {
// expected error in case of empty/invalid value
// see CmsCategory#getCategoryPath(String, String)
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
errorHandler.addError(
value,
Messages.get().getBundle(value.getLocale()).key(Messages.GUI_CATEGORY_CHECK_EMPTY_ERROR_0));
} catch (CmsException e) {
// unexpected error
if (LOG.isErrorEnabled()) {
LOG.error(e.getLocalizedMessage(), e);
}
errorHandler.addError(value, e.getLocalizedMessage());
}
return errorHandler;
}
/**
* Validates the given rules against the given value.
*
* @param cms the current users OpenCms context
* @param value the value to validate
* @param errorHandler the error handler to use in case errors or warnings are detected
*
* @return if a broken link has been found
*/
protected boolean validateLink(CmsObject cms, I_CmsXmlContentValue value, CmsXmlContentErrorHandler errorHandler) {
// if there is a value of type file reference
if ((value == null) || (!(value instanceof CmsXmlVfsFileValue) && !(value instanceof CmsXmlVarLinkValue))) {
return false;
}
// if the value has a link (this will automatically fix, for instance, the path of moved resources)
CmsLink link = null;
if (value instanceof CmsXmlVfsFileValue) {
link = ((CmsXmlVfsFileValue)value).getLink(cms);
} else if (value instanceof CmsXmlVarLinkValue) {
link = ((CmsXmlVarLinkValue)value).getLink(cms);
}
if ((link == null) || !link.isInternal()) {
return false;
}
try {
String sitePath = cms.getRequestContext().removeSiteRoot(link.getTarget());
// check for links to static resources
if (CmsStaticResourceHandler.isStaticResourceUri(sitePath)) {
return false;
}
// validate the link for error
CmsResource res = null;
CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(link.getTarget());
// the link target may be a root path for a resource in another site
if (site != null) {
CmsObject rootCms = OpenCms.initCmsObject(cms);
rootCms.getRequestContext().setSiteRoot("");
res = rootCms.readResource(link.getTarget(), CmsResourceFilter.IGNORE_EXPIRATION);
} else {
res = cms.readResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION);
}
// check the time range
if (res != null) {
long time = System.currentTimeMillis();
if (!res.isReleased(time)) {
if (errorHandler != null) {
// generate warning message
errorHandler.addWarning(
value,
Messages.get().getBundle(value.getLocale()).key(
Messages.GUI_XMLCONTENT_CHECK_WARNING_NOT_RELEASED_0));
}
return true;
} else if (res.isExpired(time)) {
if (errorHandler != null) {
// generate warning message
errorHandler.addWarning(
value,
Messages.get().getBundle(value.getLocale()).key(
Messages.GUI_XMLCONTENT_CHECK_WARNING_EXPIRED_0));
}
return true;
}
}
} catch (CmsException e) {
if (errorHandler != null) {
// generate error message
errorHandler.addError(
value,
Messages.get().getBundle(value.getLocale()).key(Messages.GUI_XMLCONTENT_CHECK_ERROR_0));
}
return true;
}
return false;
}
/**
* Validates the given rules against the given value.
*
* @param cms the current users OpenCms context
* @param value the value to validate
* @param errorHandler the error handler to use in case errors or warnings are detected
* @param rules the rules to validate the value against
* @param isWarning if true, this validation should be stored as a warning, otherwise as an error
*
* @return the updated error handler
*/
protected CmsXmlContentErrorHandler validateValue(
CmsObject cms,
I_CmsXmlContentValue value,
CmsXmlContentErrorHandler errorHandler,
Map rules,
boolean isWarning) {
if (validateLink(cms, value, errorHandler)) {
return errorHandler;
}
try {
if (value.getContentDefinition().getContentHandler().getWidget(value) instanceof CmsDisplayWidget) {
// display widgets should not be validated
return errorHandler;
}
} catch (CmsXmlException e) {
errorHandler.addError(value, e.getMessage());
return errorHandler;
}
String valueStr;
try {
valueStr = value.getStringValue(cms);
} catch (Exception e) {
// if the value can not be accessed it's useless to continue
errorHandler.addError(value, e.getMessage());
return errorHandler;
}
String regex = rules.get(value.getName());
if (regex == null) {
// no customized rule, check default XML schema validation rules
return validateValue(cms, value, valueStr, errorHandler, isWarning);
}
boolean matchResult = true;
if (regex.charAt(0) == '!') {
// negate the pattern
matchResult = false;
regex = regex.substring(1);
}
String matchValue = valueStr;
if (matchValue == null) {
// set match value to empty String to avoid exceptions in pattern matcher
matchValue = "";
}
// use the custom validation pattern
if (matchResult != Pattern.matches(regex, matchValue)) {
// generate the message
String message = getValidationMessage(cms, value, regex, valueStr, matchResult, isWarning);
if (isWarning) {
errorHandler.addWarning(value, message);
} else {
errorHandler.addError(value, message);
// if an error was found, the default XML schema validation is not applied
return errorHandler;
}
}
// no error found, check default XML schema validation rules
return validateValue(cms, value, valueStr, errorHandler, isWarning);
}
/**
* Checks the default XML schema validation rules.
*
* These rules should only be tested if this is not a test for warnings.
*
* @param cms the current users OpenCms context
* @param value the value to validate
* @param valueStr the string value of the given value
* @param errorHandler the error handler to use in case errors or warnings are detected
* @param isWarning if true, this validation should be stored as a warning, otherwise as an error
*
* @return the updated error handler
*/
protected CmsXmlContentErrorHandler validateValue(
CmsObject cms,
I_CmsXmlContentValue value,
String valueStr,
CmsXmlContentErrorHandler errorHandler,
boolean isWarning) {
if (isWarning) {
// default schema validation only applies to errors
return errorHandler;
}
if (!value.validateValue(valueStr)) {
// value is not valid, add an error to the handler
String message = getValidationMessage(cms, value, value.getTypeName(), valueStr, true, false);
errorHandler.addError(value, message);
}
return errorHandler;
}
/**
* Writes the categories if a category widget is present.
*
* @param cms the cms context
* @param file the file
* @param content the xml content to set the categories for
*
* @return the perhaps modified file
*
* @throws CmsException if something goes wrong
*/
protected CmsFile writeCategories(CmsObject cms, CmsFile file, CmsXmlContent content) throws CmsException {
if (CmsWorkplace.isTemporaryFile(file)) {
// ignore temporary file if the original file exists (not the case for direct edit: "new")
if (CmsResource.isTemporaryFileName(file.getRootPath())) {
String originalFileName = CmsResource.getFolderPath(file.getRootPath())
+ CmsResource.getName(file.getRootPath()).substring(CmsResource.TEMP_FILE_PREFIX.length());
if (cms.existsResource(cms.getRequestContext().removeSiteRoot(originalFileName))) {
// original file exists, ignore it
return file;
}
} else {
// file name does not start with temporary prefix, ignore the file
return file;
}
}
// check the presence of a category widget
boolean hasCategoryWidget = false;
Iterator it = m_elementWidgets.values().iterator();
while (it.hasNext()) {
Object widget = it.next();
if (widget instanceof CmsCategoryWidget) {
hasCategoryWidget = true;
break;
}
}
if (!hasCategoryWidget) {
// nothing to do if no category widget is present
return file;
}
boolean modified = false;
// clone the cms object, and use the root site
CmsObject tmpCms = OpenCms.initCmsObject(cms);
tmpCms.getRequestContext().setSiteRoot("");
// read all siblings
try {
List listsib = tmpCms.readSiblings(file.getRootPath(), CmsResourceFilter.ALL);
for (int i = 0; i < listsib.size(); i++) {
CmsResource resource = listsib.get(i);
// get the default locale of the sibling
List locales = getLocalesForResource(tmpCms, resource.getRootPath());
Locale locale = locales.get(0);
for (Locale l : locales) {
if (content.hasLocale(l)) {
locale = l;
break;
}
}
// remove all previously set categories
boolean clearedCategories = false;
// iterate over all values checking for the category widget
CmsXmlContentWidgetVisitor widgetCollector = new CmsXmlContentWidgetVisitor(locale);
content.visitAllValuesWith(widgetCollector);
Iterator> itWidgets = widgetCollector.getValues().entrySet().iterator();
while (itWidgets.hasNext()) {
Map.Entry entry = itWidgets.next();
String xpath = entry.getKey();
I_CmsWidget widget = widgetCollector.getWidgets().get(xpath);
I_CmsXmlContentValue value = entry.getValue();
if (!(widget instanceof CmsCategoryWidget)
|| value.getTypeName().equals(CmsXmlDynamicCategoryValue.TYPE_NAME)) {
// ignore other values than categories
continue;
}
if (!clearedCategories) {
CmsCategoryService.getInstance().clearCategoriesForResource(tmpCms, resource.getRootPath());
clearedCategories = true;
}
String stringValue = value.getStringValue(tmpCms);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(stringValue)) {
// skip empty values
continue;
}
try {
// add the file to the selected category
String[] catRootPathes = stringValue.split(",");
for (String catRootPath : catRootPathes) {
CmsCategory cat = CmsCategoryService.getInstance().getCategory(tmpCms, catRootPath);
CmsCategoryService.getInstance().addResourceToCategory(
tmpCms,
resource.getRootPath(),
cat.getPath());
}
} catch (CmsVfsResourceNotFoundException e) {
// invalid category
try {
// try to remove invalid value
content.removeValue(value.getName(), value.getLocale(), value.getIndex());
modified = true;
} catch (CmsRuntimeException ex) {
// in case minoccurs prevents removing the invalid value
if (LOG.isDebugEnabled()) {
LOG.debug(ex.getLocalizedMessage(), ex);
}
}
}
}
}
} catch (CmsException ex) {
if (LOG.isErrorEnabled()) {
LOG.error(ex.getLocalizedMessage(), ex);
}
}
if (modified) {
// when an invalid category has been removed
file = content.correctXmlStructure(cms);
content.setFile(file);
}
return file;
}
/**
* Creates a search field mapping for the given mapping element and the locale.
*
* @param contentDefinition the content definition
* @param element the mapping element configured in the schema
* @param locale the locale
*
* @return the created search field mapping
*
* @throws CmsXmlException if the dynamic field class could not be found
*/
private I_CmsSearchFieldMapping createSearchFieldMapping(
CmsXmlContentDefinition contentDefinition,
Element element,
Locale locale)
throws CmsXmlException {
I_CmsSearchFieldMapping fieldMapping = null;
String typeAsString = element.attributeValue(APPINFO_ATTR_TYPE);
CmsSearchFieldMappingType type = CmsSearchFieldMappingType.valueOf(typeAsString);
switch (type.getMode()) {
case 0: // content
case 3: // item
// localized
String param = locale.toString() + "|" + element.getStringValue();
fieldMapping = new CmsSearchFieldMapping(type, param);
break;
case 1: // property
case 2: // property-search
case 5: // attribute
// not localized
fieldMapping = new CmsSearchFieldMapping(type, element.getStringValue());
break;
case 4: // dynamic
String mappingClass = element.attributeValue(APPINFO_ATTR_CLASS);
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(mappingClass)) {
try {
fieldMapping = (I_CmsSearchFieldMapping)Class.forName(mappingClass).newInstance();
fieldMapping.setType(CmsSearchFieldMappingType.DYNAMIC);
fieldMapping.setParam(element.getStringValue());
} catch (Exception e) {
throw new CmsXmlException(
Messages.get().container(
Messages.ERR_XML_SCHEMA_MAPPING_CLASS_NOT_EXIST_3,
mappingClass,
contentDefinition.getTypeName(),
contentDefinition.getSchemaLocation()));
}
}
break;
default:
// NOOP
}
if (fieldMapping != null) {
fieldMapping.setDefaultValue(element.attributeValue(APPINFO_ATTR_DEFAULT));
}
return fieldMapping;
}
/**
* Utility method to return a path fragment.
*
* @param pathElements the path elements
* @param begin the begin index
*
* @return the path
*/
private String getSubPath(String[] pathElements, int begin) {
String result = "";
for (int i = begin; i < pathElements.length; i++) {
result += pathElements[i] + "/";
}
if (result.length() > 0) {
result = result.substring(0, result.length() - 1);
}
return result;
}
/**
* Initializes the message key fall back handler.
*
* @param element the XML element node
*/
private void initMessageKeyHandler(Element element) {
String className = element.attributeValue(APPINFO_ATTR_CLASS);
String configuration = element.attributeValue(APPINFO_ATTR_CONFIGURATION);
try {
Object messageKeyHandler = Class.forName(className).getConstructor(String.class).newInstance(configuration);
m_messageKeyHandler = (CmsMultiMessages.I_KeyFallbackHandler)messageKeyHandler;
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
}
/**
* Checks if the given mapping has the 'useDefault' flag set to true.
*
* @param path the mapping path
* @param mapping the mapping type
*
* @return true if 'useDefault' is enabled for this mapping
*/
private boolean isMappingUsingDefault(String path, String mapping) {
String key = path + ":" + mapping;
return m_mappingsUsingDefault.contains(key);
}
/**
* Helper method which does most of the mapping resolution work.
*
* @param cms the CMS context to use
* @param content the content object
* @param valuePath the xpath of the value
* @param valueIsSimple true if this is a simple value
* @param valueIndex the index of the value
* @param valueLocale the locale of the value
* @param originalStringValue the value as a string
*
* @throws CmsException if something goes wrong
*/
private void resolveMapping(
CmsObject cms,
CmsXmlContent content,
String valuePath,
boolean valueIsSimple,
int valueIndex,
Locale valueLocale,
String originalStringValue)
throws CmsException {
CmsObject rootCms = createRootCms(cms);
// get the original VFS file from the content
CmsFile file = content.getFile();
if (!valueIsSimple) {
// no mappings for a nested schema are possible
// note that the sub-elements of the nested schema ARE mapped by the node visitor,
// it's just the nested schema value itself that does not support mapping
return;
}
List mappings = getMappings(valuePath);
if (mappings.size() == 0) {
// nothing to do if we have no mappings at all
return;
}
// create OpenCms user context initialized with "/" as site root to read all siblings
// read all siblings of the file
List siblings = rootCms.readSiblings(
content.getFile().getRootPath(),
CmsResourceFilter.IGNORE_EXPIRATION);
Set urlNameMappingResources = new HashSet();
boolean mapToUrlName = false;
urlNameMappingResources.add(content.getFile());
// since 7.0.2 multiple mappings are possible
// get the string value of the current node
CmsGalleryNameMacroResolver resolver = new CmsGalleryNameMacroResolver(rootCms, content, valueLocale);
resolver.setKeepEmptyMacros(true);
String stringValue = resolver.resolveMacros(originalStringValue);
for (String mapping : mappings) {
// for multiple language mappings, we need to ensure
// a) all siblings are handled
// b) only the "right" locale is mapped to a sibling
if (CmsStringUtil.isNotEmpty(mapping)) {
for (int i = (siblings.size() - 1); i >= 0; i--) {
// get filename
String filename = (siblings.get(i)).getRootPath();
if (mapping.startsWith(MAPTO_URLNAME)) {
// should be written regardless of whether there is a sibling with the correct locale
mapToUrlName = true;
}
Locale locale = OpenCms.getLocaleManager().getDefaultLocale(rootCms, filename);
if (!locale.equals(valueLocale)) {
// only map property if the locale fits
continue;
}
// make sure the file is locked
CmsLock lock = rootCms.getLock(filename);
if (lock.isUnlocked()) {
rootCms.lockResource(filename);
} else if (!lock.isDirectlyOwnedInProjectBy(rootCms)) {
rootCms.changeLock(filename);
}
if (mapping.startsWith(MAPTO_PERMISSION) && (valueIndex == 0)) {
// map value to a permission
// example of a mapping: mapto="permission:GROUP:+r+v|GROUP.ALL_OTHERS:|GROUP.Projectmanagers:+r+v+w+c"
// get permission(s) to set
String permissionMappings = mapping.substring(MAPTO_PERMISSION.length());
String mainMapping = permissionMappings;
Map permissionsToSet = new HashMap();
// separate permission to set for element value from other permissions to set
int sepIndex = permissionMappings.indexOf('|');
if (sepIndex != -1) {
mainMapping = permissionMappings.substring(0, sepIndex);
permissionMappings = permissionMappings.substring(sepIndex + 1);
permissionsToSet = CmsStringUtil.splitAsMap(permissionMappings, "|", ":");
}
// determine principal type and permission string to set
String principalType = I_CmsPrincipal.PRINCIPAL_GROUP;
String permissionString = mainMapping;
sepIndex = mainMapping.indexOf(':');
if (sepIndex != -1) {
principalType = mainMapping.substring(0, sepIndex);
permissionString = mainMapping.substring(sepIndex + 1);
}
if (permissionString.toLowerCase().indexOf('o') == -1) {
permissionString += "+o";
}
// remove all existing permissions from the file
List aces = rootCms.getAccessControlEntries(filename, false);
for (Iterator j = aces.iterator(); j.hasNext();) {
CmsAccessControlEntry ace = j.next();
if (ace.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_ID)) {
// remove the entry "All others", which has to be treated in a special way
rootCms.rmacc(
filename,
CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_NAME,
CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_ID.toString());
} else {
// this is a group or user principal
I_CmsPrincipal principal = CmsPrincipal.readPrincipal(rootCms, ace.getPrincipal());
if (principal.isGroup()) {
rootCms.rmacc(filename, I_CmsPrincipal.PRINCIPAL_GROUP, principal.getName());
} else if (principal.isUser()) {
rootCms.rmacc(filename, I_CmsPrincipal.PRINCIPAL_USER, principal.getName());
}
}
}
// set additional permissions that are defined in mapping
for (Iterator> j = permissionsToSet.entrySet().iterator(); j.hasNext();) {
Map.Entry entry = j.next();
sepIndex = entry.getKey().indexOf('.');
if (sepIndex != -1) {
String type = entry.getKey().substring(0, sepIndex);
String name = entry.getKey().substring(sepIndex + 1);
String permissions = entry.getValue();
if (permissions.toLowerCase().indexOf('o') == -1) {
permissions += "+o";
}
try {
rootCms.chacc(filename, type, name, permissions);
} catch (CmsException e) {
// setting permission did not work
LOG.error(e);
}
}
}
// set permission(s) using the element value(s)
// the set with all selected principals
TreeSet allPrincipals = new TreeSet();
String path = CmsXmlUtils.removeXpathIndex(valuePath);
List values = content.getValues(path, valueLocale);
Iterator j = values.iterator();
while (j.hasNext()) {
I_CmsXmlContentValue val = j.next();
String principalName = val.getStringValue(rootCms);
// the prinicipal name can be a principal list
List principalNames = CmsStringUtil.splitAsList(
principalName,
PRINCIPAL_LIST_SEPARATOR);
// iterate over the principals
Iterator iterPrincipals = principalNames.iterator();
while (iterPrincipals.hasNext()) {
// get the next principal
String principal = iterPrincipals.next();
allPrincipals.add(principal);
}
}
// iterate over the set with all principals and set the permissions
Iterator iterAllPricinipals = allPrincipals.iterator();
while (iterAllPricinipals.hasNext()) {
// get the next principal
String principal = iterAllPricinipals.next();
rootCms.chacc(filename, principalType, principal, permissionString);
}
// special case: permissions are written only to one sibling, end loop
i = 0;
} else if (mapping.startsWith(MAPTO_PROPERTY_LIST) && (valueIndex == 0)) {
boolean mapToShared;
int prefixLength;
// check which mapping is used (shared or individual)
if (mapping.startsWith(MAPTO_PROPERTY_LIST_SHARED)) {
mapToShared = true;
prefixLength = MAPTO_PROPERTY_LIST_SHARED.length();
} else if (mapping.startsWith(MAPTO_PROPERTY_LIST_INDIVIDUAL)) {
mapToShared = false;
prefixLength = MAPTO_PROPERTY_LIST_INDIVIDUAL.length();
} else {
mapToShared = false;
prefixLength = MAPTO_PROPERTY_LIST.length();
}
// this is a property list mapping
String property = mapping.substring(prefixLength);
String path = CmsXmlUtils.removeXpathIndex(valuePath);
List values = content.getValues(path, valueLocale);
Iterator j = values.iterator();
StringBuffer result = new StringBuffer(values.size() * 64);
while (j.hasNext()) {
I_CmsXmlContentValue val = j.next();
result.append(val.getStringValue(rootCms));
if (j.hasNext()) {
result.append(CmsProperty.VALUE_LIST_DELIMITER);
}
}
CmsProperty p;
if (mapToShared) {
// map to shared value
p = new CmsProperty(property, null, result.toString());
} else {
// map to individual value
p = new CmsProperty(property, result.toString(), null);
}
// write the created list string value in the selected property
rootCms.writePropertyObject(filename, p);
if (mapToShared) {
// special case: shared mappings must be written only to one sibling, end loop
i = 0;
}
} else if (mapping.startsWith(MAPTO_PROPERTY)) {
boolean mapToShared;
int prefixLength;
// check which mapping is used (shared or individual)
if (mapping.startsWith(MAPTO_PROPERTY_SHARED)) {
mapToShared = true;
prefixLength = MAPTO_PROPERTY_SHARED.length();
} else if (mapping.startsWith(MAPTO_PROPERTY_INDIVIDUAL)) {
mapToShared = false;
prefixLength = MAPTO_PROPERTY_INDIVIDUAL.length();
} else {
mapToShared = false;
prefixLength = MAPTO_PROPERTY.length();
}
// this is a property mapping
String property = mapping.substring(prefixLength);
CmsProperty p;
if (mapToShared) {
// map to shared value
p = new CmsProperty(property, null, stringValue);
} else {
// map to individual value
p = new CmsProperty(property, stringValue, null);
}
// just store the string value in the selected property
rootCms.writePropertyObject(filename, p);
if (mapToShared) {
// special case: shared mappings must be written only to one sibling, end loop
i = 0;
}
} else if (mapping.startsWith(MAPTO_URLNAME)) {
// we write the actual mappings later
urlNameMappingResources.add(siblings.get(i));
} else if (mapping.startsWith(MAPTO_ATTRIBUTE)) {
// this is an attribute mapping
String attribute = mapping.substring(MAPTO_ATTRIBUTE.length());
switch (ATTRIBUTES.indexOf(attribute)) {
case 0: // date released
long date = 0;
try {
date = Long.valueOf(stringValue).longValue();
} catch (NumberFormatException e) {
// ignore, value can be a macro
}
if (date == 0) {
date = CmsResource.DATE_RELEASED_DEFAULT;
}
// set the sibling release date
rootCms.setDateReleased(filename, date, false);
// set current file release date
if (filename.equals(rootCms.getSitePath(file))) {
file.setDateReleased(date);
}
break;
case 1: // date expired
date = 0;
try {
date = Long.valueOf(stringValue).longValue();
} catch (NumberFormatException e) {
// ignore, value can be a macro
}
if (date == 0) {
date = CmsResource.DATE_EXPIRED_DEFAULT;
}
// set the sibling expired date
rootCms.setDateExpired(filename, date, false);
// set current file expired date
if (filename.equals(rootCms.getSitePath(file))) {
file.setDateExpired(date);
}
break;
default:
// ignore invalid / other mappings
}
}
}
}
}
if (mapToUrlName) {
CmsMappingResolutionContext context = (CmsMappingResolutionContext)(cms.getRequestContext().getAttribute(
ATTR_MAPPING_RESOLUTION_CONTEXT));
for (CmsResource resourceForUrlNameMapping : urlNameMappingResources) {
if (!CmsResource.isTemporaryFileName(resourceForUrlNameMapping.getRootPath())) {
String mappedName = stringValue;
if (!CmsStringUtil.isEmptyOrWhitespaceOnly(mappedName)) {
mappedName = mappedName.trim();
context.addUrlNameMapping(mappedName, valueLocale, resourceForUrlNameMapping.getStructureId());
}
}
}
}
// make sure the original is locked
CmsLock lock = rootCms.getLock(file);
if (lock.isUnlocked()) {
rootCms.lockResource(file.getRootPath());
} else if (!lock.isExclusiveOwnedBy(rootCms.getRequestContext().getCurrentUser())) {
rootCms.changeLock(file.getRootPath());
}
}
/**
* Parses a boolean from a string and returns a default value if the string couldn't be parsed.
*
* @param text the text from which to get the boolean value
* @param defaultValue the value to return if parsing fails
*
* @return the parsed boolean
*/
private boolean safeParseBoolean(String text, boolean defaultValue) {
if (text == null) {
return defaultValue;
}
try {
return Boolean.parseBoolean(text);
} catch (Throwable t) {
return defaultValue;
}
}
}