/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (C) Alkacon Software (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, 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.ade.configuration;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsRequestContext;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsVfsResourceAlreadyExistsException;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.types.CmsResourceTypeFolder;
import org.opencms.file.types.I_CmsResourceType;
import org.opencms.lock.CmsLock;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.security.CmsPermissionSet;
import org.opencms.security.CmsRole;
import org.opencms.util.CmsStringUtil;
import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
import org.opencms.xml.containerpage.CmsFormatterConfiguration;
import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import com.google.common.collect.Lists;
/**
* The configuration for a single resource type.<p>
*/
public class CmsResourceTypeConfig implements I_CmsConfigurationObject<CmsResourceTypeConfig> {
/** The log instance for this class. */
private static final Log LOG = CmsLog.getLog(CmsResourceTypeConfig.class);
/** The CMS object used for VFS operations. */
protected CmsObject m_cms;
/** The flag for disabling detail pages. */
private boolean m_detailPagesDisabled;
/** True if this is a disabled configuration. */
private boolean m_disabled;
/** A reference to a folder of folder name. */
private CmsFolderOrName m_folderOrName;
/** The formatter configuration. */
private CmsFormatterConfiguration m_formatterConfig;
/** The name pattern .*/
private String m_namePattern;
/** The number used for sorting the resource type configurations. */
private int m_order;
/** The name of the resource type. */
private String m_typeName;
/**
* Creates a new resource type configuration.<p>
*
* @param typeName the resource type name
* @param disabled true if this is a disabled configuration
* @param folder the folder reference
* @param pattern the name pattern
* @param formatterConfig the formatter configuration
*/
public CmsResourceTypeConfig(
String typeName,
boolean disabled,
CmsFolderOrName folder,
String pattern,
CmsFormatterConfiguration formatterConfig) {
this(typeName, disabled, folder, pattern, formatterConfig, false, I_CmsConfigurationObject.DEFAULT_ORDER);
}
/**
* Creates a new resource type configuration.<p>
*
* @param typeName the resource type name
* @param disabled true if this is a disabled configuration
* @param folder the folder reference
* @param pattern the name pattern
* @param formatterConfig the formatter configuration
* @param detailPagesDisabled true if detail page creation should be disabled for this type
* @param order the number used for sorting resource types from modules
*/
public CmsResourceTypeConfig(
String typeName,
boolean disabled,
CmsFolderOrName folder,
String pattern,
CmsFormatterConfiguration formatterConfig,
boolean detailPagesDisabled,
int order) {
m_typeName = typeName;
m_disabled = disabled;
m_folderOrName = folder;
m_namePattern = pattern;
m_formatterConfig = formatterConfig;
m_detailPagesDisabled = detailPagesDisabled;
m_order = order;
}
/**
* Creates a new resource type configuration.<p>
*
* @param typeName the resource type name
* @param disabled true if this is a disabled configuration
* @param folder the folder reference
* @param pattern the name pattern
* @param formatterConfig the formatter configuration
* @param order the number used for sorting resource types from modules
*/
public CmsResourceTypeConfig(
String typeName,
boolean disabled,
CmsFolderOrName folder,
String pattern,
CmsFormatterConfiguration formatterConfig,
int order) {
this(typeName, disabled, folder, pattern, formatterConfig, false, order);
}
/**
* Checks if this resource type is creatable.<p>
*
* @param cms the current CMS context
*
* @return <code>true</code> if the resource type is creatable
*
* @throws CmsException if something goes wrong
*/
public boolean checkCreatable(CmsObject cms) throws CmsException {
if (cms.getRequestContext().getCurrentProject().isOnlineProject()
|| "".equals(cms.getRequestContext().getSiteRoot())) {
return false;
}
if (CmsXmlDynamicFunctionHandler.TYPE_FUNCTION.equals(m_typeName)) {
return OpenCms.getRoleManager().hasRole(cms, CmsRole.DEVELOPER);
}
checkInitialized();
String folderPath = getFolderPath(cms);
CmsObject createCms = OpenCms.initCmsObject(m_cms);
createCms.getRequestContext().setCurrentProject(cms.getRequestContext().getCurrentProject());
String oldSiteRoot = cms.getRequestContext().getSiteRoot();
cms.getRequestContext().setSiteRoot("");
tryToUnlock(cms, folderPath);
createFolder(createCms, folderPath);
try {
CmsResource permissionCheckFolder = cms.readResource(folderPath);
CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(m_typeName);
boolean editable = settings.isEditable(cms, permissionCheckFolder);
boolean controlPermission = settings.getAccess().getPermissions(cms, permissionCheckFolder).requiresControlPermission();
boolean hasWritePermission = cms.hasPermissions(
permissionCheckFolder,
CmsPermissionSet.ACCESS_WRITE,
false,
CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
return editable && controlPermission && hasWritePermission;
} catch (CmsVfsResourceNotFoundException e) {
return false;
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
return false;
} finally {
cms.getRequestContext().setSiteRoot(oldSiteRoot);
}
}
/**
* Checks if a resource type is viewable for the current user.
* If not, this resource type should not be available at all within the ADE 'add-wizard'.<p>
*
* @param cms the current CMS context
* @param referenceUri the resource URI to check permissions for
*
* @return <code>true</code> if the resource type is viewable
*/
public boolean checkViewable(CmsObject cms, String referenceUri) {
try {
CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(m_typeName);
CmsResource siteRoot = cms.readResource(referenceUri);
return settings.getAccess().getPermissions(cms, siteRoot).requiresViewPermission();
} catch (CmsException e) {
LOG.error(e.getLocalizedMessage(), e);
return false;
}
}
/**
* Checks whether the object is initialized and throws an exception otherwise.<p>
*/
public void checkInitialized() {
if (m_cms == null) {
throw new IllegalStateException();
}
}
/**
* Checks whether the cms context is in the offline project and throws an exception otherwise.<p>
* @param cms
*/
public void checkOffline(CmsObject cms) {
if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
throw new IllegalStateException();
}
}
/**
* Creates a folder and its parent folders if they don't exist.<p>
*
* @param cms the CMS context to use
* @param rootPath the folder root path
*
* @throws CmsException if something goes wrong
*/
public void createFolder(CmsObject cms, String rootPath) throws CmsException {
cms.getRequestContext().setSiteRoot("");
List<String> parents = new ArrayList<String>();
String currentPath = rootPath;
while (currentPath != null) {
if (cms.existsResource(currentPath)) {
break;
}
parents.add(currentPath);
currentPath = CmsResource.getParentFolder(currentPath);
}
parents = Lists.reverse(parents);
for (String parent : parents) {
try {
cms.createResource(parent, CmsResourceTypeFolder.getStaticTypeId());
try {
cms.unlockResource(parent);
} catch (CmsException e) {
// may happen if parent folder is locked also
if (LOG.isInfoEnabled()) {
LOG.info(e);
}
}
} catch (CmsVfsResourceAlreadyExistsException e) {
// nop
}
}
}
/**
* Creates a new element.<p>
*
* @param userCms the CMS context to use
*
* @return the created resource
*
* @throws CmsException if something goes wrong
*/
public CmsResource createNewElement(CmsObject userCms) throws CmsException {
return createNewElement(userCms, null);
}
/**
* Creates a new element.<p>
*
* @param userCms the CMS context to use
* @param modelResource the model resource to use
*
* @return the created resource
*
* @throws CmsException if something goes wrong
*/
public CmsResource createNewElement(CmsObject userCms, CmsResource modelResource) throws CmsException {
checkOffline(userCms);
checkInitialized();
CmsObject rootCms = rootCms(userCms);
String folderPath = getFolderPath(userCms);
createFolder(m_cms, folderPath);
String destination = CmsStringUtil.joinPaths(folderPath, getNamePattern(true));
String creationPath = OpenCms.getResourceManager().getNameGenerator().getNewFileName(rootCms, destination, 5);
// set the content locale
rootCms.getRequestContext().setAttribute(
CmsRequestContext.ATTRIBUTE_NEW_RESOURCE_LOCALE,
userCms.getRequestContext().getLocale());
if (modelResource != null) {
// set the model resource
rootCms.getRequestContext().setAttribute(CmsRequestContext.ATTRIBUTE_MODEL, modelResource.getRootPath());
}
CmsResource createdResource = rootCms.createResource(
creationPath,
getType().getTypeId(),
null,
new ArrayList<CmsProperty>(0));
return createdResource;
}
/**
* Computes the folder path for this resource type.<p>
*
* @param cms the cms context to use
*
* @return the folder root path for this resource type
*/
public String getFolderPath(CmsObject cms) {
checkInitialized();
if (m_folderOrName != null) {
return m_folderOrName.getFolderPath(cms);
} else {
return CmsStringUtil.joinPaths(
cms.getRequestContext().getSiteRoot(),
CmsADEConfigData.CONTENT_FOLDER_NAME,
m_typeName);
}
}
/**
* @see org.opencms.ade.configuration.I_CmsConfigurationObject#getKey()
*/
public String getKey() {
return m_typeName;
}
/**
* Gets the name pattern.<p>
*
* @param useDefaultIfEmpty if true, uses a default value if the name pattern isn't set directly
*
* @return the name pattern
*/
public String getNamePattern(boolean useDefaultIfEmpty) {
if (m_namePattern != null) {
return m_namePattern;
}
if (useDefaultIfEmpty) {
return m_typeName + "-%(number).html";
}
return null;
}
/**
* Returns the number used for sorting module resource types.<p>
*
* @return the number used for sorting module resource types
*/
public int getOrder() {
return m_order;
}
/**
* Gets the actual resource type for which this is a configuration.<p>
*
* @return the actual resource type
*
* @throws CmsException if something goes wrong
*/
public I_CmsResourceType getType() throws CmsException {
return OpenCms.getResourceManager().getResourceType(m_typeName);
}
/**
* Returns the type name.<p>
*
* @return the type name
*/
public String getTypeName() {
return m_typeName;
}
/**
* Initializes this instance.<p>
*
* @param cms the CMS context to use
*/
public void initialize(CmsObject cms) {
m_cms = cms;
}
/**
* True if the detail page creation should be disabled for this resource type.<p>
*
* @return true if detail page creation should be disabled for this type
*/
public boolean isDetailPagesDisabled() {
return m_detailPagesDisabled;
}
/**
* @see org.opencms.ade.configuration.I_CmsConfigurationObject#isDisabled()
*/
public boolean isDisabled() {
return m_disabled;
}
/**
* @see org.opencms.ade.configuration.I_CmsConfigurationObject#merge(org.opencms.ade.configuration.I_CmsConfigurationObject)
*/
public CmsResourceTypeConfig merge(CmsResourceTypeConfig childConfig) {
CmsFolderOrName folderOrName = childConfig.m_folderOrName != null ? childConfig.m_folderOrName : m_folderOrName;
String namePattern = childConfig.m_namePattern != null ? childConfig.m_namePattern : m_namePattern;
CmsFormatterConfiguration formatterConfig = childConfig.m_formatterConfig != null
? childConfig.m_formatterConfig
: m_formatterConfig;
return new CmsResourceTypeConfig(
m_typeName,
false,
folderOrName,
namePattern,
formatterConfig,
isDetailPagesDisabled() || childConfig.isDetailPagesDisabled(),
m_order);
}
/**
* Creates a shallow copy of this resource type configuration object.<p>
*
* @return a copy of the resource type configuration object
*/
protected CmsResourceTypeConfig copy() {
return new CmsResourceTypeConfig(
m_typeName,
m_disabled,
getFolderOrName(),
m_namePattern,
m_formatterConfig,
m_detailPagesDisabled,
m_order);
}
/**
* Returns the folder bean from the configuration.<p>
*
* Normally, you should use getFolderPath() instead.<p>
*
* @return the folder bean from the configuration
*/
protected CmsFolderOrName getFolderOrName() {
return m_folderOrName;
}
/**
* Gets the formatter configuration of this resource type.<p>
*
* @return the formatter configuration of this resource type
*/
protected CmsFormatterConfiguration getFormatterConfiguration() {
return m_formatterConfig;
}
/**
* Gets the configured name pattern.<p>
*
* @return the configured name pattern
*/
protected String getNamePattern() {
return m_namePattern;
}
/**
* Creates a new CMS object based on existing one and changes its site root to the site root.<p>
*
* @param cms the CMS context
* @return the root site CMS context
* @throws CmsException if something goes wrong
*/
protected CmsObject rootCms(CmsObject cms) throws CmsException {
CmsObject result = OpenCms.initCmsObject(cms);
result.getRequestContext().setSiteRoot("");
return result;
}
/**
* Tries to remove a lock on an ancestor of a given path owned by the current user.<p>
*
* @param cms the CMS context
* @param folderPath the path for which the lock should be removed
*
* @throws CmsException if something goes wrong
*/
protected void tryToUnlock(CmsObject cms, String folderPath) throws CmsException {
// Get path of first ancestor that actually exists
while (!cms.existsResource(folderPath)) {
folderPath = CmsResource.getParentFolder(folderPath);
}
CmsResource resource = cms.readResource(folderPath);
CmsLock lock = cms.getLock(resource);
// we are only interested in locks we can safely unlock, i.e. locks by the current user
if (lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
// walk up the tree until we get to the location from which the lock is inherited
while (lock.isInherited()) {
folderPath = CmsResource.getParentFolder(folderPath);
resource = cms.readResource(folderPath);
lock = cms.getLock(resource);
}
cms.unlockResource(folderPath);
}
}
/**
* Updates the base path for the folder information.<p>
*
* @param basePath the new base path
*/
protected void updateBasePath(String basePath) {
if (m_folderOrName != null) {
if (m_folderOrName.isName()) {
m_folderOrName = new CmsFolderOrName(basePath, m_folderOrName.getFolderName());
}
} else {
m_folderOrName = new CmsFolderOrName(basePath, m_typeName);
}
}
}