/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH (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, 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.db;
import org.opencms.configuration.CmsConfigurationManager;
import org.opencms.configuration.CmsParameterConfiguration;
import org.opencms.configuration.CmsSystemConfiguration;
import org.opencms.db.generic.CmsUserDriver;
import org.opencms.db.log.CmsLogEntry;
import org.opencms.db.log.CmsLogEntryType;
import org.opencms.db.log.CmsLogFilter;
import org.opencms.db.urlname.CmsUrlNameMappingEntry;
import org.opencms.db.urlname.CmsUrlNameMappingFilter;
import org.opencms.file.CmsDataAccessException;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsFolder;
import org.opencms.file.CmsGroup;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsRequestContext;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsUser;
import org.opencms.file.CmsUserSearchParameters;
import org.opencms.file.CmsVfsException;
import org.opencms.file.CmsVfsResourceAlreadyExistsException;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.I_CmsResource;
import org.opencms.file.history.CmsHistoryFile;
import org.opencms.file.history.CmsHistoryFolder;
import org.opencms.file.history.CmsHistoryPrincipal;
import org.opencms.file.history.CmsHistoryProject;
import org.opencms.file.history.I_CmsHistoryResource;
import org.opencms.file.types.CmsResourceTypeFolder;
import org.opencms.file.types.CmsResourceTypeJsp;
import org.opencms.file.types.I_CmsResourceType;
import org.opencms.flex.CmsFlexRequestContextInfo;
import org.opencms.i18n.CmsLocaleManager;
import org.opencms.i18n.CmsMessageContainer;
import org.opencms.lock.CmsLock;
import org.opencms.lock.CmsLockException;
import org.opencms.lock.CmsLockFilter;
import org.opencms.lock.CmsLockManager;
import org.opencms.lock.CmsLockType;
import org.opencms.main.CmsEvent;
import org.opencms.main.CmsException;
import org.opencms.main.CmsIllegalArgumentException;
import org.opencms.main.CmsIllegalStateException;
import org.opencms.main.CmsInitException;
import org.opencms.main.CmsLog;
import org.opencms.main.CmsMultiException;
import org.opencms.main.I_CmsEventListener;
import org.opencms.main.OpenCms;
import org.opencms.module.CmsModule;
import org.opencms.monitor.CmsMemoryMonitor;
import org.opencms.publish.CmsPublishEngine;
import org.opencms.publish.CmsPublishJobInfoBean;
import org.opencms.publish.CmsPublishReport;
import org.opencms.relations.CmsCategoryService;
import org.opencms.relations.CmsLink;
import org.opencms.relations.CmsRelation;
import org.opencms.relations.CmsRelationFilter;
import org.opencms.relations.CmsRelationSystemValidator;
import org.opencms.relations.CmsRelationType;
import org.opencms.relations.I_CmsLinkParseable;
import org.opencms.report.CmsLogReport;
import org.opencms.report.I_CmsReport;
import org.opencms.security.CmsAccessControlEntry;
import org.opencms.security.CmsAccessControlList;
import org.opencms.security.CmsAuthentificationException;
import org.opencms.security.CmsOrganizationalUnit;
import org.opencms.security.CmsPasswordEncryptionException;
import org.opencms.security.CmsPermissionSet;
import org.opencms.security.CmsPermissionSetCustom;
import org.opencms.security.CmsPrincipal;
import org.opencms.security.CmsRole;
import org.opencms.security.CmsSecurityException;
import org.opencms.security.I_CmsPermissionHandler;
import org.opencms.security.I_CmsPrincipal;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.util.PrintfFormat;
import org.opencms.workplace.commons.CmsProgressThread;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.logging.Log;
import org.apache.commons.pool.ObjectPool;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
/**
* The OpenCms driver manager.<p>
*
* @since 6.0.0
*/
public final class CmsDriverManager implements I_CmsEventListener {
/**
* The comparator used for comparing url name mapping entries by date.<p>
*/
class UrlNameMappingComparator implements Comparator<CmsUrlNameMappingEntry> {
/**
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(CmsUrlNameMappingEntry o1, CmsUrlNameMappingEntry o2) {
long date1 = o1.getDateChanged();
long date2 = o2.getDateChanged();
if (date1 < date2) {
return -1;
}
if (date1 > date2) {
return +1;
}
return 0;
}
}
/**
* Enumeration class for the mode parameter in the
* {@link CmsDriverManager#readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}
* method.<p>
*/
private static class CmsReadChangedProjectResourceMode {
/**
* Default constructor.<p>
*/
protected CmsReadChangedProjectResourceMode() {
// noop
}
}
/** Attribute login. */
public static final String ATTRIBUTE_LOGIN = "A_LOGIN";
/** Cache key for all properties. */
public static final String CACHE_ALL_PROPERTIES = "_CAP_";
/**
* Values indicating changes of a resource,
* ordered according to the scope of the change.
*/
/** Value to indicate a change in access control entries of a resource. */
public static final int CHANGED_ACCESSCONTROL = 1;
/** Value to indicate a content change. */
public static final int CHANGED_CONTENT = 16;
/** Value to indicate a change in the lastmodified settings of a resource. */
public static final int CHANGED_LASTMODIFIED = 4;
/** Value to indicate a change in the resource data. */
public static final int CHANGED_RESOURCE = 8;
/** Value to indicate a change in the availability timeframe. */
public static final int CHANGED_TIMEFRAME = 2;
/** "cache" string in the configuration-file. */
public static final String CONFIGURATION_CACHE = "cache";
/** "db" string in the configuration-file. */
public static final String CONFIGURATION_DB = "db";
/** "driver.history" string in the configuration-file. */
public static final String CONFIGURATION_HISTORY = "driver.history";
/** "driver.project" string in the configuration-file. */
public static final String CONFIGURATION_PROJECT = "driver.project";
/** "subscription.vfs" string in the configuration file. */
public static final String CONFIGURATION_SUBSCRIPTION = "driver.subscription";
/** "driver.user" string in the configuration-file. */
public static final String CONFIGURATION_USER = "driver.user";
/** "driver.vfs" string in the configuration-file. */
public static final String CONFIGURATION_VFS = "driver.vfs";
/** DBC attribute key needed to fix publishing behavior involving siblings. */
public static final String KEY_CHANGED_AND_DELETED = "changedAndDeleted";
/** The vfs path of the loast and found folder. */
public static final String LOST_AND_FOUND_FOLDER = "/system/lost-found";
/** The maximum length of a VFS resource path. */
public static final int MAX_VFS_RESOURCE_PATH_LENGTH = 512;
/** Key for indicating no changes. */
public static final int NOTHING_CHANGED = 0;
/** Indicates to ignore the resource path when matching resources. */
public static final String READ_IGNORE_PARENT = null;
/** Indicates to ignore the time value. */
public static final long READ_IGNORE_TIME = 0L;
/** Indicates to ignore the resource type when matching resources. */
public static final int READ_IGNORE_TYPE = -1;
/** Indicates to match resources NOT having the given state. */
public static final int READMODE_EXCLUDE_STATE = 8;
/** Indicates to match immediate children only. */
public static final int READMODE_EXCLUDE_TREE = 1;
/** Indicates to match resources NOT having the given type. */
public static final int READMODE_EXCLUDE_TYPE = 4;
/** Mode for reading project resources from the db. */
public static final int READMODE_IGNORESTATE = 0;
/** Indicates to match resources in given project only. */
public static final int READMODE_INCLUDE_PROJECT = 2;
/** Indicates to match all successors. */
public static final int READMODE_INCLUDE_TREE = 0;
/** Mode for reading project resources from the db. */
public static final int READMODE_MATCHSTATE = 1;
/** Indicates if only file resources should be read. */
public static final int READMODE_ONLY_FILES = 128;
/** Indicates if only folder resources should be read. */
public static final int READMODE_ONLY_FOLDERS = 64;
/** Mode for reading project resources from the db. */
public static final int READMODE_UNMATCHSTATE = 2;
/** Prefix char for temporary files in the VFS. */
public static final String TEMP_FILE_PREFIX = "~";
/** Key to indicate complete update. */
public static final int UPDATE_ALL = 3;
/** Key to indicate update of resource record. */
public static final int UPDATE_RESOURCE = 4;
/** Key to indicate update of last modified project reference. */
public static final int UPDATE_RESOURCE_PROJECT = 6;
/** Key to indicate update of resource state. */
public static final int UPDATE_RESOURCE_STATE = 1;
/** Key to indicate update of resource state including the content date. */
public static final int UPDATE_RESOURCE_STATE_CONTENT = 7;
/** Key to indicate update of structure record. */
public static final int UPDATE_STRUCTURE = 5;
/** Key to indicate update of structure state. */
public static final int UPDATE_STRUCTURE_STATE = 2;
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsDriverManager.class);
/** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */
private static final CmsReadChangedProjectResourceMode RCPRM_FILES_AND_FOLDERS_MODE = new CmsReadChangedProjectResourceMode();
/** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */
private static final CmsReadChangedProjectResourceMode RCPRM_FILES_ONLY_MODE = new CmsReadChangedProjectResourceMode();
/** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */
private static final CmsReadChangedProjectResourceMode RCPRM_FOLDERS_ONLY_MODE = new CmsReadChangedProjectResourceMode();
/** The list of initialized JDBC pools. */
private List<PoolingDriver> m_connectionPools;
/** The history driver. */
private I_CmsHistoryDriver m_historyDriver;
/** The HTML link validator. */
private CmsRelationSystemValidator m_htmlLinkValidator;
/** The class used for cache key generation. */
private I_CmsCacheKey m_keyGenerator;
/** The lock manager. */
private CmsLockManager m_lockManager;
/** The log entry cache. */
private List<CmsLogEntry> m_log = new ArrayList<CmsLogEntry>();
/** Local reference to the memory monitor to avoid multiple lookups through the OpenCms singleton. */
private CmsMemoryMonitor m_monitor;
/** The project driver. */
private I_CmsProjectDriver m_projectDriver;
/** The the configuration read from the <code>opencms.properties</code> file. */
private CmsParameterConfiguration m_propertyConfiguration;
/** the publish engine. */
private CmsPublishEngine m_publishEngine;
/** The security manager (for access checks). */
private CmsSecurityManager m_securityManager;
/** The sql manager. */
private CmsSqlManager m_sqlManager;
/** The subscription driver. */
private I_CmsSubscriptionDriver m_subscriptionDriver;
/** The user driver. */
private I_CmsUserDriver m_userDriver;
/** The VFS driver. */
private I_CmsVfsDriver m_vfsDriver;
/**
* Private constructor, initializes some required member variables.<p>
*/
private CmsDriverManager() {
// intentionally left blank
}
/**
* Reads the required configurations from the opencms.properties file and creates
* the various drivers to access the cms resources.<p>
*
* The initialization process of the driver manager and its drivers is split into
* the following phases:
* <ul>
* <li>the database pool configuration is read</li>
* <li>a plain and empty driver manager instance is created</li>
* <li>an instance of each driver is created</li>
* <li>the driver manager is passed to each driver during initialization</li>
* <li>finally, the driver instances are passed to the driver manager during initialization</li>
* </ul>
*
* @param configurationManager the configuration manager
* @param securityManager the security manager
* @param runtimeInfoFactory the initialized OpenCms runtime info factory
* @param publishEngine the publish engine
*
* @return CmsDriverManager the instantiated driver manager
* @throws CmsInitException if the driver manager couldn't be instantiated
*/
public static CmsDriverManager newInstance(
CmsConfigurationManager configurationManager,
CmsSecurityManager securityManager,
I_CmsDbContextFactory runtimeInfoFactory,
CmsPublishEngine publishEngine) throws CmsInitException {
// read the opencms.properties from the configuration
CmsParameterConfiguration config = configurationManager.getConfiguration();
CmsDriverManager driverManager = null;
try {
// create a driver manager instance
driverManager = new CmsDriverManager();
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE1_0));
}
if (runtimeInfoFactory == null) {
throw new CmsInitException(org.opencms.main.Messages.get().container(
org.opencms.main.Messages.ERR_CRITICAL_NO_DB_CONTEXT_0));
}
} catch (Exception exc) {
CmsMessageContainer message = Messages.get().container(Messages.LOG_ERR_DRIVER_MANAGER_START_0);
if (LOG.isFatalEnabled()) {
LOG.fatal(message.key(), exc);
}
throw new CmsInitException(message, exc);
}
// store the configuration
driverManager.m_propertyConfiguration = config;
// set the security manager
driverManager.m_securityManager = securityManager;
// set connection pools
driverManager.m_connectionPools = new ArrayList<PoolingDriver>();
// set the lock manager
driverManager.m_lockManager = new CmsLockManager(driverManager);
// create and set the sql manager
driverManager.m_sqlManager = new CmsSqlManager(driverManager);
// set the publish engine
driverManager.m_publishEngine = publishEngine;
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE2_0));
}
// read the pool names to initialize
List<String> driverPoolNames = config.getList(CmsDriverManager.CONFIGURATION_DB + ".pools");
if (CmsLog.INIT.isInfoEnabled()) {
String names = "";
for (String name : driverPoolNames) {
names += name + " ";
}
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_POOLS_1, names));
}
// initialize each pool
for (String name : driverPoolNames) {
driverManager.newPoolInstance(config, name);
}
// initialize the runtime info factory with the generated driver manager
runtimeInfoFactory.initialize(driverManager);
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE3_0));
}
// store the access objects
CmsDbContext dbc = runtimeInfoFactory.getDbContext();
driverManager.m_vfsDriver = (I_CmsVfsDriver)driverManager.createDriver(
dbc,
configurationManager,
config,
CONFIGURATION_VFS,
".vfs.driver");
dbc.clear();
dbc = runtimeInfoFactory.getDbContext();
driverManager.m_userDriver = (I_CmsUserDriver)driverManager.createDriver(
dbc,
configurationManager,
config,
CONFIGURATION_USER,
".user.driver");
dbc.clear();
dbc = runtimeInfoFactory.getDbContext();
driverManager.m_projectDriver = (I_CmsProjectDriver)driverManager.createDriver(
dbc,
configurationManager,
config,
CONFIGURATION_PROJECT,
".project.driver");
dbc.clear();
dbc = runtimeInfoFactory.getDbContext();
driverManager.m_historyDriver = (I_CmsHistoryDriver)driverManager.createDriver(
dbc,
configurationManager,
config,
CONFIGURATION_HISTORY,
".history.driver");
dbc.clear();
dbc = runtimeInfoFactory.getDbContext();
try {
// we wrap this in a try-catch because otherwise it would fail during the update
// process, since the subscription driver configuration does not exist at that point.
driverManager.m_subscriptionDriver = (I_CmsSubscriptionDriver)driverManager.createDriver(
dbc,
configurationManager,
config,
CONFIGURATION_SUBSCRIPTION,
".subscription.driver");
} catch (IndexOutOfBoundsException npe) {
LOG.warn("Could not instantiate subscription driver!");
LOG.warn(npe.getLocalizedMessage(), npe);
}
dbc.clear();
// register the driver manager for required events
org.opencms.main.OpenCms.addCmsEventListener(driverManager, new int[] {
I_CmsEventListener.EVENT_UPDATE_EXPORTS,
I_CmsEventListener.EVENT_CLEAR_CACHES,
I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES,
I_CmsEventListener.EVENT_USER_MODIFIED,
I_CmsEventListener.EVENT_PUBLISH_PROJECT});
// return the configured driver manager
return driverManager;
}
/**
* Adds a new relation to the given resource.<p>
*
* @param dbc the database context
* @param resource the resource to add the relation to
* @param target the target of the relation
* @param type the type of the relation
* @param importCase if importing relations
*
* @throws CmsException if something goes wrong
*/
public void addRelationToResource(
CmsDbContext dbc,
CmsResource resource,
CmsResource target,
CmsRelationType type,
boolean importCase) throws CmsException {
if (type.isDefinedInContent()) {
throw new CmsIllegalArgumentException(Messages.get().container(
Messages.ERR_ADD_RELATION_IN_CONTENT_3,
dbc.removeSiteRoot(resource.getRootPath()),
dbc.removeSiteRoot(target.getRootPath()),
type.getLocalizedName(dbc.getRequestContext().getLocale())));
}
CmsRelation relation = new CmsRelation(resource, target, type);
getVfsDriver(dbc).createRelation(dbc, dbc.currentProject().getUuid(), relation);
if (!importCase) {
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_ADD_RELATION,
new String[] {relation.getSourcePath(), relation.getTargetPath()}), false);
// touch the resource
setDateLastModified(dbc, resource, System.currentTimeMillis());
}
}
/**
* Adds a resource to the given organizational unit.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to add the resource to
* @param resource the resource that is to be added to the organizational unit
*
* @throws CmsException if something goes wrong
*
* @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
* @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
*/
public void addResourceToOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource)
throws CmsException {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
getUserDriver(dbc).addResourceToOrganizationalUnit(dbc, orgUnit, resource);
}
/**
* Adds a user to a group.<p>
*
* @param dbc the current database context
* @param username the name of the user that is to be added to the group
* @param groupname the name of the group
* @param readRoles if reading roles or groups
*
* @throws CmsException if operation was not successful
* @throws CmsDbEntryNotFoundException if the given user or the given group was not found
*
* @see #removeUserFromGroup(CmsDbContext, String, String, boolean)
*/
public void addUserToGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles)
throws CmsException, CmsDbEntryNotFoundException {
//check if group exists
CmsGroup group = readGroup(dbc, groupname);
if (group == null) {
// the group does not exists
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
}
if (group.isVirtual() && !readRoles) {
// if adding a user from a virtual role treat it as removing the user from the role
addUserToGroup(dbc, username, CmsRole.valueOf(group).getGroupName(), true);
return;
}
if (group.isVirtual()) {
// this is an hack to prevent unlimited recursive calls
readRoles = false;
}
if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) {
// we want a role but we got a group, or the other way
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
}
if (userInGroup(dbc, username, groupname, readRoles)) {
// the user is already member of the group
return;
}
//check if the user exists
CmsUser user = readUser(dbc, username);
if (user == null) {
// the user does not exists
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, username));
}
// if adding an user to a role
if (readRoles) {
CmsRole role = CmsRole.valueOf(group);
// a role can only be set if the user has the given role
m_securityManager.checkRole(dbc, role);
// now we check if we already have the role
if (m_securityManager.hasRole(dbc, user, role)) {
// do nothing
return;
}
// and now we need to remove all possible child-roles
List<CmsRole> children = role.getChildren(true);
Iterator<CmsGroup> itUserGroups = getGroupsOfUser(
dbc,
username,
group.getOuFqn(),
true,
true,
true,
dbc.getRequestContext().getRemoteAddress()).iterator();
while (itUserGroups.hasNext()) {
CmsGroup roleGroup = itUserGroups.next();
if (children.contains(CmsRole.valueOf(roleGroup))) {
// remove only child roles
removeUserFromGroup(dbc, username, roleGroup.getName(), true);
}
}
// update virtual groups
Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator();
while (it.hasNext()) {
CmsGroup virtualGroup = it.next();
// here we say readroles = true, to prevent an unlimited recursive calls
addUserToGroup(dbc, username, virtualGroup.getName(), true);
}
// if setting a role that is not the workplace user role ensure the user is also wp user
CmsRole wpUser = CmsRole.WORKPLACE_USER.forOrgUnit(group.getOuFqn());
if (!role.equals(wpUser)
&& !role.getChildren(true).contains(wpUser)
&& !m_securityManager.hasRole(dbc, user, wpUser)) {
addUserToGroup(dbc, username, wpUser.getGroupName(), true);
}
}
//add this user to the group
getUserDriver(dbc).createUserInGroup(dbc, user.getId(), group.getId());
// flush the cache
if (readRoles) {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
}
m_monitor.flushCache(CmsMemoryMonitor.CacheType.USERGROUPS, CmsMemoryMonitor.CacheType.USER_LIST);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
eventData.put(
I_CmsEventListener.KEY_USER_ACTION,
I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
}
/**
* Changes the lock of a resource to the current user,
* that is "steals" the lock from another user.<p>
*
* @param dbc the current database context
* @param resource the resource to change the lock for
* @param lockType the new lock type to set
*
* @throws CmsException if something goes wrong
* @throws CmsSecurityException if something goes wrong
*
*
* @see CmsObject#changeLock(String)
* @see I_CmsResourceType#changeLock(CmsObject, CmsSecurityManager, CmsResource)
*
* @see CmsSecurityManager#hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter)
*/
public void changeLock(CmsDbContext dbc, CmsResource resource, CmsLockType lockType)
throws CmsException, CmsSecurityException {
// get the current lock
CmsLock currentLock = getLock(dbc, resource);
// check if the resource is locked at all
if (currentLock.getEditionLock().isUnlocked() && currentLock.getSystemLock().isUnlocked()) {
throw new CmsLockException(Messages.get().container(
Messages.ERR_CHANGE_LOCK_UNLOCKED_RESOURCE_1,
dbc.getRequestContext().getSitePath(resource)));
} else if ((lockType == CmsLockType.EXCLUSIVE)
&& currentLock.isExclusiveOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) {
// the current lock requires no change
return;
}
// duplicate logic from CmsSecurityManager#hasPermissions() because lock state can't be ignored
// if another user has locked the file, the current user can never get WRITE permissions with the default check
int denied = 0;
// check if the current user is vfs manager
boolean canIgnorePermissions = m_securityManager.hasRoleForResource(
dbc,
dbc.currentUser(),
CmsRole.VFS_MANAGER,
resource);
// if the resource type is jsp
// write is only allowed for developers
if (!canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) {
if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.DEVELOPER, resource)) {
denied |= CmsPermissionSet.PERMISSION_WRITE;
}
}
CmsPermissionSetCustom permissions;
if (canIgnorePermissions) {
// if the current user is administrator, anything is allowed
permissions = new CmsPermissionSetCustom(~0);
} else {
// otherwise, get the permissions from the access control list
permissions = getPermissions(dbc, resource, dbc.currentUser());
}
// revoke the denied permissions
permissions.denyPermissions(denied);
// now check if write permission is granted
if ((CmsPermissionSet.ACCESS_WRITE.getPermissions() & permissions.getPermissions()) != CmsPermissionSet.ACCESS_WRITE.getPermissions()) {
// check failed, throw exception
m_securityManager.checkPermissions(
dbc.getRequestContext(),
resource,
CmsPermissionSet.ACCESS_WRITE,
I_CmsPermissionHandler.PERM_DENIED);
}
// if we got here write permission is granted on the target
// remove the old lock
m_lockManager.removeResource(dbc, resource, true, lockType.isSystem());
// apply the new lock
lockResource(dbc, resource, lockType);
}
/**
* Returns a list with all sub resources of a given folder that have set the given property,
* matching the current property's value with the given old value and replacing it by a given new value.<p>
*
* @param dbc the current database context
* @param resource the resource on which property definition values are changed
* @param propertyDefinition the name of the propertydefinition to change the value
* @param oldValue the old value of the propertydefinition
* @param newValue the new value of the propertydefinition
* @param recursive if true, change the property value on the resource and recursively all property values on
* sub-resources (only for folders)
* @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed
*
* @throws CmsVfsException for now only when the search for the oldvalue failed.
* @throws CmsException if operation was not successful
*/
public List<CmsResource> changeResourcesInFolderWithProperty(
CmsDbContext dbc,
CmsResource resource,
String propertyDefinition,
String oldValue,
String newValue,
boolean recursive) throws CmsVfsException, CmsException {
CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION;
// collect the resources to look up
List<CmsResource> resources = new ArrayList<CmsResource>();
if (recursive) {
// read the files in the folder
resources = readResourcesWithProperty(dbc, resource, propertyDefinition, null, filter);
// add the folder itself
resources.add(resource);
} else {
resources.add(resource);
}
Pattern oldPattern;
try {
// remove the place holder if available
String tmpOldValue = oldValue;
if (tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_START)
&& tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_END)) {
tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_START, "");
tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_END, "");
}
// compile regular expression pattern
oldPattern = Pattern.compile(tmpOldValue);
} catch (PatternSyntaxException e) {
throw new CmsVfsException(Messages.get().container(
Messages.ERR_CHANGE_RESOURCES_IN_FOLDER_WITH_PROP_4,
new Object[] {propertyDefinition, oldValue, newValue, resource.getRootPath()}), e);
}
List<CmsResource> changedResources = new ArrayList<CmsResource>(resources.size());
// create permission set and filter to check each resource
CmsPermissionSet perm = CmsPermissionSet.ACCESS_WRITE;
for (int i = 0; i < resources.size(); i++) {
// loop through found resources and check property values
CmsResource res = resources.get(i);
// check resource state and permissions
try {
m_securityManager.checkPermissions(dbc, res, perm, true, filter);
} catch (Exception e) {
// resource is deleted or not writable for current user
continue;
}
CmsProperty property = readPropertyObject(dbc, res, propertyDefinition, false);
String propertyValue = property.getValue();
boolean changed = false;
if ((propertyValue != null) && oldPattern.matcher(propertyValue).matches()) {
// apply the place holder content
String tmpNewValue = CmsStringUtil.transformValues(oldValue, newValue, propertyValue);
// change structure value
property.setStructureValue(tmpNewValue);
changed = true;
}
if (changed) {
// write property object if something has changed
writePropertyObject(dbc, res, property);
changedResources.add(res);
}
}
return changedResources;
}
/**
* Changes the resource flags of a resource.<p>
*
* The resource flags are used to indicate various "special" conditions
* for a resource. Most notably, the "internal only" setting which signals
* that a resource can not be directly requested with it's URL.<p>
*
* @param dbc the current database context
* @param resource the resource to change the flags for
* @param flags the new resource flags for this resource
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#chflags(String, int)
* @see I_CmsResourceType#chflags(CmsObject, CmsSecurityManager, CmsResource, int)
*/
public void chflags(CmsDbContext dbc, CmsResource resource, int flags) throws CmsException {
// must operate on a clone to ensure resource is not modified in case permissions are not granted
CmsResource clone = (CmsResource)resource.clone();
clone.setFlags(flags);
// log it
log(
dbc,
new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_FLAGS,
new String[] {resource.getRootPath()}),
false);
// write it
writeResource(dbc, clone);
}
/**
* Changes the resource type of a resource.<p>
*
* OpenCms handles resources according to the resource type,
* not the file suffix. This is e.g. why a JSP in OpenCms can have the
* suffix ".html" instead of ".jsp" only. Changing the resource type
* makes sense e.g. if you want to make a plain text file a JSP resource,
* or a binary file an image, etc.<p>
*
* @param dbc the current database context
* @param resource the resource to change the type for
* @param type the new resource type for this resource
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#chtype(String, int)
* @see I_CmsResourceType#chtype(CmsObject, CmsSecurityManager, CmsResource, int)
*/
public void chtype(CmsDbContext dbc, CmsResource resource, int type) throws CmsException {
// must operate on a clone to ensure resource is not modified in case permissions are not granted
CmsResource clone = (CmsResource)resource.clone();
I_CmsResourceType newType = OpenCms.getResourceManager().getResourceType(type);
clone.setType(newType.getTypeId());
// log it
log(
dbc,
new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_TYPE,
new String[] {resource.getRootPath()}),
false);
// write it
writeResource(dbc, clone);
}
/**
* @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent)
*/
public void cmsEvent(CmsEvent event) {
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(Messages.LOG_CMS_EVENT_1, new Integer(event.getType())));
}
I_CmsReport report;
CmsDbContext dbc;
switch (event.getType()) {
case I_CmsEventListener.EVENT_UPDATE_EXPORTS:
dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT);
updateExportPoints(dbc);
break;
case I_CmsEventListener.EVENT_PUBLISH_PROJECT:
CmsUUID publishHistoryId = new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID));
report = (I_CmsReport)event.getData().get(I_CmsEventListener.KEY_REPORT);
dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT);
m_monitor.clearCache();
writeExportPoints(dbc, report, publishHistoryId);
break;
case I_CmsEventListener.EVENT_CLEAR_CACHES:
m_monitor.clearCache();
break;
case I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES:
case I_CmsEventListener.EVENT_USER_MODIFIED:
m_monitor.clearPrincipalsCache();
break;
default:
// noop
}
}
/**
* Copies the access control entries of a given resource to a destination resource.<p>
*
* Already existing access control entries of the destination resource are removed.<p>
*
* @param dbc the current database context
* @param source the resource to copy the access control entries from
* @param destination the resource to which the access control entries are copied
* @param updateLastModifiedInfo if true, user and date "last modified" information on the target resource will be updated
*
* @throws CmsException if something goes wrong
*/
public void copyAccessControlEntries(
CmsDbContext dbc,
CmsResource source,
CmsResource destination,
boolean updateLastModifiedInfo) throws CmsException {
// get the entries to copy
ListIterator<CmsAccessControlEntry> aceList = getUserDriver(dbc).readAccessControlEntries(
dbc,
dbc.currentProject(),
source.getResourceId(),
false).listIterator();
// remove the current entries from the destination
getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), destination.getResourceId());
// now write the new entries
while (aceList.hasNext()) {
CmsAccessControlEntry ace = aceList.next();
getUserDriver(dbc).createAccessControlEntry(
dbc,
dbc.currentProject(),
destination.getResourceId(),
ace.getPrincipal(),
ace.getPermissions().getAllowedPermissions(),
ace.getPermissions().getDeniedPermissions(),
ace.getFlags());
}
// log it
log(dbc, new CmsLogEntry(
dbc,
destination.getStructureId(),
CmsLogEntryType.RESOURCE_PERMISSIONS,
new String[] {destination.getRootPath()}), false);
// update the "last modified" information
if (updateLastModifiedInfo) {
setDateLastModified(dbc, destination, destination.getDateLastModified());
}
// clear the cache
m_monitor.clearAccessControlListCache();
// fire a resource modification event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, destination);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_ACCESSCONTROL));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Copies a resource.<p>
*
* You must ensure that the destination path is an absolute, valid and
* existing VFS path. Relative paths from the source are currently not supported.<p>
*
* In case the target resource already exists, it is overwritten with the
* source resource.<p>
*
* The <code>siblingMode</code> parameter controls how to handle siblings
* during the copy operation.
* Possible values for this parameter are:
* <ul>
* <li><code>{@link org.opencms.file.CmsResource#COPY_AS_NEW}</code></li>
* <li><code>{@link org.opencms.file.CmsResource#COPY_AS_SIBLING}</code></li>
* <li><code>{@link org.opencms.file.CmsResource#COPY_PRESERVE_SIBLING}</code></li>
* </ul><p>
*
* @param dbc the current database context
* @param source the resource to copy
* @param destination the name of the copy destination with complete path
* @param siblingMode indicates how to handle siblings during copy
*
* @throws CmsException if something goes wrong
* @throws CmsIllegalArgumentException if the <code>source</code> argument is <code>null</code>
*
* @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode)
* @see I_CmsResourceType#copyResource(CmsObject, CmsSecurityManager, CmsResource, String, CmsResource.CmsResourceCopyMode)
*/
public void copyResource(
CmsDbContext dbc,
CmsResource source,
String destination,
CmsResource.CmsResourceCopyMode siblingMode) throws CmsException, CmsIllegalArgumentException {
// check the sibling mode to see if this resource has to be copied as a sibling
boolean copyAsSibling = false;
// siblings of folders are not supported
if (!source.isFolder()) {
// if the "copy as sibling" mode is used, set the flag to true
if (siblingMode == CmsResource.COPY_AS_SIBLING) {
copyAsSibling = true;
}
// if the mode is "preserve siblings", we have to check the sibling counter
if (siblingMode == CmsResource.COPY_PRESERVE_SIBLING) {
if (source.getSiblingCount() > 1) {
copyAsSibling = true;
}
}
}
// read the source properties
List<CmsProperty> properties = readPropertyObjects(dbc, source, false);
if (copyAsSibling) {
// create a sibling of the source file at the destination
createSibling(dbc, source, destination, properties);
// after the sibling is created the copy operation is finished
return;
}
// prepare the content if required
byte[] content = null;
if (source.isFile()) {
if (source instanceof CmsFile) {
// resource already is a file
content = ((CmsFile)source).getContents();
}
if ((content == null) || (content.length < 1)) {
// no known content yet - read from database
content = getVfsDriver(dbc).readContent(dbc, dbc.currentProject().getUuid(), source.getResourceId());
}
}
// determine destination folder
String destinationFoldername = CmsResource.getParentFolder(destination);
// read the destination folder (will also check read permissions)
CmsFolder destinationFolder = m_securityManager.readFolder(
dbc,
destinationFoldername,
CmsResourceFilter.IGNORE_EXPIRATION);
// no further permission check required here, will be done in createResource()
// set user and creation time stamps
long currentTime = System.currentTimeMillis();
long dateLastModified;
CmsUUID userLastModified;
if (source.isFolder()) {
// folders always get a new date and user when they are copied
dateLastModified = currentTime;
userLastModified = dbc.currentUser().getId();
} else {
// files keep the date and user last modified from the source
dateLastModified = source.getDateLastModified();
userLastModified = source.getUserLastModified();
}
// check the resource flags
int flags = source.getFlags();
if (source.isLabeled()) {
// reset "labeled" link flag for new resource
flags &= ~CmsResource.FLAG_LABELED;
}
// create the new resource
CmsResource newResource = new CmsResource(
new CmsUUID(),
new CmsUUID(),
destination,
source.getTypeId(),
source.isFolder(),
flags,
dbc.currentProject().getUuid(),
CmsResource.STATE_NEW,
currentTime,
dbc.currentUser().getId(),
dateLastModified,
userLastModified,
source.getDateReleased(),
source.getDateExpired(),
1,
source.getLength(),
source.getDateContent(),
source.getVersion()); // version number does not matter since it will be computed later
// trigger "is touched" state on resource (will ensure modification date is kept unchanged)
newResource.setDateLastModified(dateLastModified);
// log it
log(dbc, new CmsLogEntry(
dbc,
newResource.getStructureId(),
CmsLogEntryType.RESOURCE_COPIED,
new String[] {newResource.getRootPath()}), false);
// create the resource
newResource = createResource(dbc, destination, newResource, content, properties, false);
// copy relations
copyRelations(dbc, source, newResource);
// copy the access control entries to the created resource
copyAccessControlEntries(dbc, source, newResource, false);
// clear the cache
m_monitor.clearAccessControlListCache();
List<CmsResource> modifiedResources = new ArrayList<CmsResource>();
modifiedResources.add(source);
modifiedResources.add(newResource);
modifiedResources.add(destinationFolder);
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_COPIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, modifiedResources)));
}
/**
* Copies a resource to the current project of the user.<p>
*
* @param dbc the current database context
* @param resource the resource to apply this operation to
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#copyResourceToProject(String)
* @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource)
*/
public void copyResourceToProject(CmsDbContext dbc, CmsResource resource) throws CmsException {
// copy the resource to the project only if the resource is not already in the project
if (!isInsideCurrentProject(dbc, resource.getRootPath())) {
// check if there are already any subfolders of this resource
I_CmsProjectDriver projectDriver = getProjectDriver(dbc);
if (resource.isFolder()) {
List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject());
for (int i = 0; i < projectResources.size(); i++) {
String resname = projectResources.get(i);
if (resname.startsWith(resource.getRootPath())) {
// delete the existing project resource first
projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname);
}
}
}
try {
projectDriver.createProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath());
} catch (CmsException exc) {
// if the subfolder exists already - all is ok
} finally {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_PROJECT_MODIFIED,
Collections.<String, Object> singletonMap("project", dbc.currentProject())));
}
}
}
/**
* Counts the locked resources in this project.<p>
*
* @param project the project to count the locked resources in
*
* @return the amount of locked resources in this project
*/
public int countLockedResources(CmsProject project) {
// count locks
return m_lockManager.countExclusiveLocksInProject(project);
}
/**
* Add a new group to the Cms.<p>
*
* Only the admin can do this.
* Only users, which are in the group "administrators" are granted.<p>
*
* @param dbc the current database context
* @param id the id of the new group
* @param name the name of the new group
* @param description the description for the new group
* @param flags the flags for the new group
* @param parent the name of the parent group (or <code>null</code>)
*
* @return new created group
*
* @throws CmsException if the creation of the group failed
* @throws CmsIllegalArgumentException if the length of the given name was below 1
*/
public CmsGroup createGroup(CmsDbContext dbc, CmsUUID id, String name, String description, int flags, String parent)
throws CmsIllegalArgumentException, CmsException {
// check the group name
OpenCms.getValidationHandler().checkGroupName(CmsOrganizationalUnit.getSimpleName(name));
// trim the name
name = name.trim();
// check the OU
readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
// get the id of the parent group if necessary
if (CmsStringUtil.isNotEmpty(parent)) {
CmsGroup parentGroup = readGroup(dbc, parent);
if (!parentGroup.isRole()
&& !CmsOrganizationalUnit.getParentFqn(parent).equals(CmsOrganizationalUnit.getParentFqn(name))) {
throw new CmsDataAccessException(Messages.get().container(
Messages.ERR_PARENT_GROUP_MUST_BE_IN_SAME_OU_3,
CmsOrganizationalUnit.getSimpleName(name),
CmsOrganizationalUnit.getParentFqn(name),
parent));
}
}
// create the group
CmsGroup group = getUserDriver(dbc).createGroup(dbc, id, name, description, flags, parent);
// if the group is in fact a role, initialize it
if (group.isVirtual()) {
// get all users that have the given role
String groupname = CmsRole.valueOf(group).getGroupName();
Iterator<CmsUser> it = getUsersOfGroup(dbc, groupname, true, false, true).iterator();
while (it.hasNext()) {
CmsUser user = it.next();
// put them in the new group
addUserToGroup(dbc, user.getName(), group.getName(), true);
}
}
// put it into the cache
m_monitor.cacheGroup(group);
if (!dbc.getProjectId().isNullUUID()) {
// group modified event is not needed
return group;
}
// fire group modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_CREATE);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData));
// return it
return group;
}
/**
* Creates a new organizational unit.<p>
*
* @param dbc the current db context
* @param ouFqn the fully qualified name of the new organizational unit
* @param description the description of the new organizational unit
* @param flags the flags for the new organizational unit
* @param resource the first associated resource
*
* @return a <code>{@link CmsOrganizationalUnit}</code> object representing
* the newly created organizational unit
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#createOrganizationalUnit(CmsObject, String, String, int, String)
*/
public CmsOrganizationalUnit createOrganizationalUnit(
CmsDbContext dbc,
String ouFqn,
String description,
int flags,
CmsResource resource) throws CmsException {
// normal case
CmsOrganizationalUnit parent = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(ouFqn));
String name = CmsOrganizationalUnit.getSimpleName(ouFqn);
if (name.endsWith(CmsOrganizationalUnit.SEPARATOR)) {
name = name.substring(0, name.length() - 1);
}
// check the name
CmsResource.checkResourceName(name);
// trim the name
name = name.trim();
// check the description
if (CmsStringUtil.isEmptyOrWhitespaceOnly(description)) {
throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_OU_DESCRIPTION_EMPTY_0));
}
// create the organizational unit
CmsOrganizationalUnit orgUnit = getUserDriver(dbc).createOrganizationalUnit(
dbc,
name,
description,
flags,
parent,
resource != null ? resource.getRootPath() : null);
// put the new created org unit into the cache
m_monitor.cacheOrgUnit(orgUnit);
// flush relevant caches
m_monitor.clearPrincipalsCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
// create a publish list for the 'virtual' publish event
CmsResource ouRes = readResource(
dbc,
CmsUserDriver.ORGUNIT_BASE_FOLDER + orgUnit.getName(),
CmsResourceFilter.DEFAULT);
CmsPublishList pl = new CmsPublishList(ouRes, false);
pl.add(ouRes, false);
getProjectDriver(dbc).writePublishHistory(
dbc,
pl.getPublishHistoryId(),
new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW));
// fire the 'virtual' publish event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString());
eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData);
OpenCms.fireCmsEvent(afterPublishEvent);
if (!dbc.getProjectId().isNullUUID()) {
// OU modified event is not needed
return orgUnit;
}
// fire OU modified event
Map<String, Object> event2Data = new HashMap<String, Object>();
event2Data.put(I_CmsEventListener.KEY_OU_NAME, orgUnit.getName());
event2Data.put(I_CmsEventListener.KEY_OU_ID, orgUnit.getId().toString());
event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_CREATE);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data));
// return it
return orgUnit;
}
/**
* Creates a project.<p>
*
* @param dbc the current database context
* @param name the name of the project to create
* @param description the description of the project
* @param groupname the project user group to be set
* @param managergroupname the project manager group to be set
* @param projecttype the type of the project
*
* @return the created project
*
* @throws CmsIllegalArgumentException if the chosen <code>name</code> is already used
* by the online project, or if the name is not valid
* @throws CmsException if something goes wrong
*/
public CmsProject createProject(
CmsDbContext dbc,
String name,
String description,
String groupname,
String managergroupname,
CmsProject.CmsProjectType projecttype) throws CmsIllegalArgumentException, CmsException {
if (CmsProject.ONLINE_PROJECT_NAME.equals(name)) {
throw new CmsIllegalArgumentException(Messages.get().container(
Messages.ERR_CREATE_PROJECT_ONLINE_PROJECT_NAME_1,
CmsProject.ONLINE_PROJECT_NAME));
}
// check the name
CmsProject.checkProjectName(CmsOrganizationalUnit.getSimpleName(name));
// check the ou
readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
// read the needed groups from the cms
CmsGroup group = readGroup(dbc, groupname);
CmsGroup managergroup = readGroup(dbc, managergroupname);
return getProjectDriver(dbc).createProject(
dbc,
new CmsUUID(),
dbc.currentUser(),
group,
managergroup,
name,
description,
CmsProject.PROJECT_FLAG_NONE,
projecttype);
}
/**
* Creates a property definition.<p>
*
* Property definitions are valid for all resource types.<p>
*
* @param dbc the current database context
* @param name the name of the property definition to create
*
* @return the created property definition
*
* @throws CmsException if something goes wrong
*/
public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name) throws CmsException {
CmsPropertyDefinition propertyDefinition = null;
name = name.trim();
// validate the property name
CmsPropertyDefinition.checkPropertyName(name);
// TODO: make the type a parameter
try {
try {
propertyDefinition = getVfsDriver(dbc).readPropertyDefinition(dbc, name, dbc.currentProject().getUuid());
} catch (CmsException e) {
propertyDefinition = getVfsDriver(dbc).createPropertyDefinition(
dbc,
dbc.currentProject().getUuid(),
name,
CmsPropertyDefinition.TYPE_NORMAL);
}
try {
getVfsDriver(dbc).readPropertyDefinition(dbc, name, CmsProject.ONLINE_PROJECT_ID);
} catch (CmsException e) {
getVfsDriver(dbc).createPropertyDefinition(
dbc,
CmsProject.ONLINE_PROJECT_ID,
name,
CmsPropertyDefinition.TYPE_NORMAL);
}
try {
getHistoryDriver(dbc).readPropertyDefinition(dbc, name);
} catch (CmsException e) {
getHistoryDriver(dbc).createPropertyDefinition(dbc, name, CmsPropertyDefinition.TYPE_NORMAL);
}
} finally {
// fire an event that a property of a resource has been deleted
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_PROPERTY_DEFINITION_CREATED,
Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition)));
}
return propertyDefinition;
}
/**
* Creates a new publish job.<p>
*
* @param dbc the current database context
* @param publishJob the publish job to create
*
* @throws CmsException if something goes wrong
*/
public void createPublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException {
getProjectDriver(dbc).createPublishJob(dbc, publishJob);
}
/**
* Creates a new resource with the provided content and properties.<p>
*
* The <code>content</code> parameter may be <code>null</code> if the resource id
* already exists. If so, the created resource will be a sibling of the existing
* resource, the existing content will remain unchanged.<p>
*
* This is used during file import for import of siblings as the
* <code>manifest.xml</code> only contains one binary copy per file.<p>
*
* If the resource id exists but the <code>content</code> is not <code>null</code>,
* the created resource will be made a sibling of the existing resource,
* and both will share the new content.<p>
*
* @param dbc the current database context
* @param resourcePath the name of the resource to create (full path)
* @param resource the new resource to create
* @param content the content for the new resource
* @param properties the properties for the new resource
* @param importCase if <code>true</code>, signals that this operation is done while
* importing resource, causing different lock behavior and
* potential "lost and found" usage
*
* @return the created resource
*
* @throws CmsException if something goes wrong
*/
public synchronized CmsResource createResource(
CmsDbContext dbc,
String resourcePath,
CmsResource resource,
byte[] content,
List<CmsProperty> properties,
boolean importCase) throws CmsException {
CmsResource newResource = null;
if (resource.isFolder()) {
resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath);
}
try {
// need to provide the parent folder id for resource creation
String parentFolderName = CmsResource.getParentFolder(resourcePath);
CmsResource parentFolder = readFolder(dbc, parentFolderName, CmsResourceFilter.IGNORE_EXPIRATION);
CmsLock parentLock = getLock(dbc, parentFolder);
// it is not allowed to create a resource in a folder locked by other user
if (!parentLock.isUnlocked() && !parentLock.isOwnedBy(dbc.currentUser())) {
// one exception is if the admin user tries to create a temporary resource
if (!CmsResource.getName(resourcePath).startsWith(TEMP_FILE_PREFIX)
|| !m_securityManager.hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) {
throw new CmsLockException(Messages.get().container(
Messages.ERR_CREATE_RESOURCE_PARENT_LOCK_1,
dbc.removeSiteRoot(resourcePath)));
}
}
if (CmsResourceTypeJsp.isJsp(resource)) {
// security check when trying to create a new jsp file
m_securityManager.checkRoleForResource(dbc, CmsRole.DEVELOPER, parentFolder);
}
// check import configuration of "lost and found" folder
boolean useLostAndFound = importCase && !OpenCms.getImportExportManager().overwriteCollidingResources();
// check if the resource already exists by name
CmsResource currentResourceByName = null;
try {
currentResourceByName = readResource(dbc, resourcePath, CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
// if the resource does exist, we have to check the id later to decide what to do
}
// check if the resource already exists by id
try {
CmsResource currentResourceById = readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL);
// it is not allowed to import resources when there is already a resource with the same id but different path
if (!currentResourceById.getRootPath().equals(resourcePath)) {
throw new CmsVfsResourceAlreadyExistsException(Messages.get().container(
Messages.ERR_RESOURCE_WITH_ID_ALREADY_EXISTS_3,
dbc.removeSiteRoot(resourcePath),
dbc.removeSiteRoot(currentResourceById.getRootPath()),
currentResourceById.getStructureId()));
}
} catch (CmsVfsResourceNotFoundException e) {
// if the resource does exist, we have to check the id later to decide what to do
}
// check the permissions
if (currentResourceByName == null) {
// resource does not exist - check parent folder
m_securityManager.checkPermissions(
dbc,
parentFolder,
CmsPermissionSet.ACCESS_WRITE,
false,
CmsResourceFilter.IGNORE_EXPIRATION);
} else {
// resource already exists - check existing resource
m_securityManager.checkPermissions(
dbc,
currentResourceByName,
CmsPermissionSet.ACCESS_WRITE,
!importCase,
CmsResourceFilter.ALL);
}
// now look for the resource by name
if (currentResourceByName != null) {
boolean overwrite = true;
if (currentResourceByName.getState().isDeleted()) {
if (!currentResourceByName.isFolder()) {
// if a non-folder resource was deleted it's treated like a new resource
overwrite = false;
}
} else {
if (!importCase) {
// direct "overwrite" of a resource is possible only during import,
// or if the resource has been deleted
throw new CmsVfsResourceAlreadyExistsException(org.opencms.db.generic.Messages.get().container(
org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
// the resource already exists
if (!resource.isFolder()
&& useLostAndFound
&& (!currentResourceByName.getResourceId().equals(resource.getResourceId()))) {
// semantic change: the current resource is moved to L&F and the imported resource will overwrite the old one
// will leave the resource with state deleted,
// but it does not matter, since the state will be set later again
moveToLostAndFound(dbc, currentResourceByName, false);
}
}
if (!overwrite) {
// lock the resource, will throw an exception if not lockable
lockResource(dbc, currentResourceByName, CmsLockType.EXCLUSIVE);
// trigger createResource instead of writeResource
currentResourceByName = null;
}
}
// if null, create new resource, if not null write resource
CmsResource overwrittenResource = currentResourceByName;
// extract the name (without path)
String targetName = CmsResource.getName(resourcePath);
int contentLength;
// modify target name and content length in case of folder creation
if (resource.isFolder()) {
// folders never have any content
contentLength = -1;
// must cut of trailing '/' for folder creation (or name check fails)
if (CmsResource.isFolder(targetName)) {
targetName = targetName.substring(0, targetName.length() - 1);
}
} else {
// otherwise ensure content and content length are set correctly
if (content != null) {
// if a content is provided, in each case the length is the length of this content
contentLength = content.length;
} else if (overwrittenResource != null) {
// we have no content, but an already existing resource - length remains unchanged
contentLength = overwrittenResource.getLength();
} else {
// we have no content - length is used as set in the resource
contentLength = resource.getLength();
}
}
// check if the target name is valid (forbidden chars etc.),
// if not throw an exception
// must do this here since targetName is modified in folder case (see above)
CmsResource.checkResourceName(targetName);
// set structure and resource ids as given
CmsUUID structureId = resource.getStructureId();
CmsUUID resourceId = resource.getResourceId();
// decide which structure id to use
if (overwrittenResource != null) {
// resource exists, re-use existing ids
structureId = overwrittenResource.getStructureId();
}
if (structureId.isNullUUID()) {
// need a new structure id
structureId = new CmsUUID();
}
// decide which resource id to use
if (overwrittenResource != null) {
// if we are overwriting we have to assure the resource id is the same
resourceId = overwrittenResource.getResourceId();
}
if (resourceId.isNullUUID()) {
// need a new resource id
resourceId = new CmsUUID();
}
try {
// check online resource
CmsResource onlineResource = getVfsDriver(dbc).readResource(
dbc,
CmsProject.ONLINE_PROJECT_ID,
resourcePath,
true);
// only allow to overwrite with different id if importing (createResource will set the right id)
try {
CmsResource offlineResource = getVfsDriver(dbc).readResource(
dbc,
dbc.currentProject().getUuid(),
onlineResource.getStructureId(),
true);
if (!offlineResource.getRootPath().equals(onlineResource.getRootPath())) {
throw new CmsVfsOnlineResourceAlreadyExistsException(Messages.get().container(
Messages.ERR_ONLINE_RESOURCE_EXISTS_2,
dbc.removeSiteRoot(resourcePath),
dbc.removeSiteRoot(offlineResource.getRootPath())));
}
} catch (CmsVfsResourceNotFoundException e) {
// there is no problem for now
// but should never happen
if (LOG.isErrorEnabled()) {
LOG.error(e.getLocalizedMessage(), e);
}
}
} catch (CmsVfsResourceNotFoundException e) {
// ok, there is no online entry to worry about
}
// now create a resource object with all informations
newResource = new CmsResource(
structureId,
resourceId,
resourcePath,
resource.getTypeId(),
resource.isFolder(),
resource.getFlags(),
dbc.currentProject().getUuid(),
resource.getState(),
resource.getDateCreated(),
resource.getUserCreated(),
resource.getDateLastModified(),
resource.getUserLastModified(),
resource.getDateReleased(),
resource.getDateExpired(),
1,
contentLength,
resource.getDateContent(),
resource.getVersion()); // version number does not matter since it will be computed later
// ensure date is updated only if required
if (resource.isTouched()) {
// this will trigger the internal "is touched" state on the new resource
newResource.setDateLastModified(resource.getDateLastModified());
}
if (resource.isFile()) {
// check if a sibling to the imported resource lies in a marked site
if (labelResource(dbc, resource, resourcePath, 2)) {
int flags = resource.getFlags();
flags |= CmsResource.FLAG_LABELED;
resource.setFlags(flags);
}
// ensure siblings don't overwrite existing resource records
if (content == null) {
newResource.setState(CmsResource.STATE_KEEP);
}
}
// delete all relations for the resource, before writing the content
getVfsDriver(dbc).deleteRelations(
dbc,
dbc.currentProject().getUuid(),
newResource,
CmsRelationFilter.TARGETS);
if (overwrittenResource == null) {
CmsLock lock = getLock(dbc, newResource);
if (lock.getEditionLock().isExclusive()) {
unlockResource(dbc, newResource, true, false);
}
// resource does not exist.
newResource = getVfsDriver(dbc).createResource(
dbc,
dbc.currentProject().getUuid(),
newResource,
content);
} else {
// resource already exists.
// probably the resource is a merged page file that gets overwritten during import, or it gets
// overwritten by a copy operation. if so, the structure & resource state are not modified to changed.
int updateStates = (overwrittenResource.getState().isNew()
? CmsDriverManager.NOTHING_CHANGED
: CmsDriverManager.UPDATE_ALL);
getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), newResource, updateStates);
if ((content != null) && resource.isFile()) {
// also update file content if required
getVfsDriver(dbc).writeContent(dbc, newResource.getResourceId(), content);
}
}
// write the properties (internal operation, no events or duplicate permission checks)
writePropertyObjects(dbc, newResource, properties, false);
// lock the created resource
try {
// if it is locked by another user (copied or moved resource) this lock should be preserved and
// the exception is OK: locks on created resources are a slave feature to original locks
lockResource(dbc, newResource, CmsLockType.EXCLUSIVE);
} catch (CmsLockException cle) {
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(
Messages.ERR_CREATE_RESOURCE_LOCK_1,
new Object[] {dbc.removeSiteRoot(newResource.getRootPath())}));
}
}
if (!importCase) {
log(dbc, new CmsLogEntry(
dbc,
newResource.getStructureId(),
CmsLogEntryType.RESOURCE_CREATED,
new String[] {resource.getRootPath()}), false);
} else {
log(dbc, new CmsLogEntry(
dbc,
newResource.getStructureId(),
CmsLogEntryType.RESOURCE_IMPORTED,
new String[] {resource.getRootPath()}), false);
}
} finally {
// clear the internal caches
m_monitor.clearAccessControlListCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
if (newResource != null) {
// fire an event that a new resource has been created
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_CREATED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, newResource)));
}
}
return newResource;
}
/**
* Creates a new resource of the given resource type
* with the provided content and properties.<p>
*
* If the provided content is null and the resource is not a folder,
* the content will be set to an empty byte array.<p>
*
* @param dbc the current database context
* @param resourcename the name of the resource to create (full path)
* @param type the type of the resource to create
* @param content the content for the new resource
* @param properties the properties for the new resource
*
* @return the created resource
*
* @throws CmsException if something goes wrong
* @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0
*
* @see CmsObject#createResource(String, int, byte[], List)
* @see CmsObject#createResource(String, int)
* @see I_CmsResourceType#createResource(CmsObject, CmsSecurityManager, String, byte[], List)
*/
public CmsResource createResource(
CmsDbContext dbc,
String resourcename,
int type,
byte[] content,
List<CmsProperty> properties) throws CmsException, CmsIllegalArgumentException {
String targetName = resourcename;
if (content == null) {
// name based resource creation MUST have a content
content = new byte[0];
}
int size;
if (CmsFolder.isFolderType(type)) {
// must cut of trailing '/' for folder creation
if (CmsResource.isFolder(targetName)) {
targetName = targetName.substring(0, targetName.length() - 1);
}
size = -1;
} else {
size = content.length;
}
// create a new resource
CmsResource newResource = new CmsResource(CmsUUID.getNullUUID(), // uuids will be "corrected" later
CmsUUID.getNullUUID(),
targetName,
type,
CmsFolder.isFolderType(type),
0,
dbc.currentProject().getUuid(),
CmsResource.STATE_NEW,
0,
dbc.currentUser().getId(),
0,
dbc.currentUser().getId(),
CmsResource.DATE_RELEASED_DEFAULT,
CmsResource.DATE_EXPIRED_DEFAULT,
1,
size,
0, // version number does not matter since it will be computed later
0); // content time will be corrected later
return createResource(dbc, targetName, newResource, content, properties, false);
}
/**
* Creates a new sibling of the source resource.<p>
*
* @param dbc the current database context
* @param source the resource to create a sibling for
* @param destination the name of the sibling to create with complete path
* @param properties the individual properties for the new sibling
*
* @return the new created sibling
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#createSibling(String, String, List)
* @see I_CmsResourceType#createSibling(CmsObject, CmsSecurityManager, CmsResource, String, List)
*/
public CmsResource createSibling(
CmsDbContext dbc,
CmsResource source,
String destination,
List<CmsProperty> properties) throws CmsException {
if (source.isFolder()) {
throw new CmsVfsException(Messages.get().container(Messages.ERR_VFS_FOLDERS_DONT_SUPPORT_SIBLINGS_0));
}
// determine destination folder and resource name
String destinationFoldername = CmsResource.getParentFolder(destination);
// read the destination folder (will also check read permissions)
CmsFolder destinationFolder = readFolder(dbc, destinationFoldername, CmsResourceFilter.IGNORE_EXPIRATION);
// no further permission check required here, will be done in createResource()
// check the resource flags
int flags = source.getFlags();
if (labelResource(dbc, source, destination, 1)) {
// set "labeled" link flag for new resource
flags |= CmsResource.FLAG_LABELED;
}
// create the new resource
CmsResource newResource = new CmsResource(
new CmsUUID(),
source.getResourceId(),
destination,
source.getTypeId(),
source.isFolder(),
flags,
dbc.currentProject().getUuid(),
CmsResource.STATE_KEEP,
source.getDateCreated(), // ensures current resource record remains untouched
source.getUserCreated(),
source.getDateLastModified(),
source.getUserLastModified(),
source.getDateReleased(),
source.getDateExpired(),
source.getSiblingCount() + 1,
source.getLength(),
source.getDateContent(),
source.getVersion()); // version number does not matter since it will be computed later
// trigger "is touched" state on resource (will ensure modification date is kept unchanged)
newResource.setDateLastModified(newResource.getDateLastModified());
log(dbc, new CmsLogEntry(
dbc,
newResource.getStructureId(),
CmsLogEntryType.RESOURCE_CLONED,
new String[] {newResource.getRootPath()}), false);
// create the resource (null content signals creation of sibling)
newResource = createResource(dbc, destination, newResource, null, properties, false);
// copy relations
copyRelations(dbc, source, newResource);
// clear the caches
m_monitor.clearAccessControlListCache();
List<CmsResource> modifiedResources = new ArrayList<CmsResource>();
modifiedResources.add(source);
modifiedResources.add(newResource);
modifiedResources.add(destinationFolder);
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, modifiedResources)));
return newResource;
}
/**
* Creates the project for the temporary workplace files.<p>
*
* @param dbc the current database context
*
* @return the created project for the temporary workplace files
*
* @throws CmsException if something goes wrong
*/
public CmsProject createTempfileProject(CmsDbContext dbc) throws CmsException {
// read the needed groups from the cms
CmsGroup projectUserGroup = readGroup(dbc, dbc.currentProject().getGroupId());
CmsGroup projectManagerGroup = readGroup(dbc, dbc.currentProject().getManagerGroupId());
CmsProject tempProject = getProjectDriver(dbc).createProject(
dbc,
new CmsUUID(),
dbc.currentUser(),
projectUserGroup,
projectManagerGroup,
I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME,
Messages.get().getBundle(dbc.getRequestContext().getLocale()).key(
Messages.GUI_WORKPLACE_TEMPFILE_PROJECT_DESC_0),
CmsProject.PROJECT_FLAG_HIDDEN,
CmsProject.PROJECT_TYPE_NORMAL);
getProjectDriver(dbc).createProjectResource(dbc, tempProject.getUuid(), "/");
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_PROJECT_MODIFIED,
Collections.<String, Object> singletonMap("project", tempProject)));
return tempProject;
}
/**
* Creates a new user.<p>
*
* @param dbc the current database context
* @param name the name for the new user
* @param password the password for the new user
* @param description the description for the new user
* @param additionalInfos the additional infos for the user
*
* @return the created user
*
* @see CmsObject#createUser(String, String, String, Map)
*
* @throws CmsException if something goes wrong
* @throws CmsIllegalArgumentException if the name for the user is not valid
*/
public CmsUser createUser(
CmsDbContext dbc,
String name,
String password,
String description,
Map<String, Object> additionalInfos) throws CmsException, CmsIllegalArgumentException {
// no space before or after the name
name = name.trim();
// check the user name
String userName = CmsOrganizationalUnit.getSimpleName(name);
OpenCms.getValidationHandler().checkUserName(userName);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) {
throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName));
}
// check the ou
CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
// check the password
validatePassword(password);
Map<String, Object> info = new HashMap<String, Object>();
if (additionalInfos != null) {
info.putAll(additionalInfos);
}
if (description != null) {
info.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description);
}
int flags = 0;
if (ou.hasFlagWebuser()) {
flags += I_CmsPrincipal.FLAG_USER_WEBUSER;
}
CmsUser user = getUserDriver(dbc).createUser(
dbc,
new CmsUUID(),
name,
OpenCms.getPasswordHandler().digest(password),
" ",
" ",
" ",
0,
I_CmsPrincipal.FLAG_ENABLED + flags,
0,
info);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return user;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_CREATE_USER);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
return user;
}
/**
* Deletes all property values of a file or folder.<p>
*
* If there are no other siblings than the specified resource,
* both the structure and resource property values get deleted.
* If the specified resource has siblings, only the structure
* property values get deleted.<p>
*
* @param dbc the current database context
* @param resourcename the name of the resource for which all properties should be deleted
*
* @throws CmsException if operation was not successful
*/
public void deleteAllProperties(CmsDbContext dbc, String resourcename) throws CmsException {
CmsResource resource = null;
List<CmsResource> resources = new ArrayList<CmsResource>();
try {
// read the resource
resource = readResource(dbc, resourcename, CmsResourceFilter.IGNORE_EXPIRATION);
// check the security
m_securityManager.checkPermissions(
dbc,
resource,
CmsPermissionSet.ACCESS_WRITE,
false,
CmsResourceFilter.ALL);
// delete the property values
if (resource.getSiblingCount() > 1) {
// the resource has siblings- delete only the (structure) properties of this sibling
getVfsDriver(dbc).deletePropertyObjects(
dbc,
dbc.currentProject().getUuid(),
resource,
CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES);
resources.addAll(readSiblings(dbc, resource, CmsResourceFilter.ALL));
} else {
// the resource has no other siblings- delete all (structure+resource) properties
getVfsDriver(dbc).deletePropertyObjects(
dbc,
dbc.currentProject().getUuid(),
resource,
CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES);
resources.add(resource);
}
} finally {
// clear the driver manager cache
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
// fire an event that all properties of a resource have been deleted
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources)));
}
}
/**
* Deletes all entries in the published resource table.<p>
*
* @param dbc the current database context
* @param linkType the type of resource deleted (0= non-paramter, 1=parameter)
*
* @throws CmsException if something goes wrong
*/
public void deleteAllStaticExportPublishedResources(CmsDbContext dbc, int linkType) throws CmsException {
getProjectDriver(dbc).deleteAllStaticExportPublishedResources(dbc, linkType);
}
/**
* Deletes a group, where all permissions, users and children of the group
* are transfered to a replacement group.<p>
*
* @param dbc the current request context
* @param group the id of the group to be deleted
* @param replacementId the id of the group to be transfered, can be <code>null</code>
*
* @throws CmsException if operation was not successful
* @throws CmsDataAccessException if group to be deleted contains user
*/
public void deleteGroup(CmsDbContext dbc, CmsGroup group, CmsUUID replacementId)
throws CmsDataAccessException, CmsException {
CmsGroup replacementGroup = null;
if (replacementId != null) {
replacementGroup = readGroup(dbc, replacementId);
}
// get all child groups of the group
List<CmsGroup> children = getChildren(dbc, group, false);
// get all users in this group
List<CmsUser> users = getUsersOfGroup(dbc, group.getName(), true, true, group.isRole());
// get online project
CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
if (replacementGroup == null) {
// remove users
Iterator<CmsUser> itUsers = users.iterator();
while (itUsers.hasNext()) {
CmsUser user = itUsers.next();
if (userInGroup(dbc, user.getName(), group.getName(), group.isRole())) {
removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole());
}
}
// transfer children to grandfather if possible
CmsUUID parentId = group.getParentId();
if (parentId == null) {
parentId = CmsUUID.getNullUUID();
}
Iterator<CmsGroup> itChildren = children.iterator();
while (itChildren.hasNext()) {
CmsGroup child = itChildren.next();
child.setParentId(parentId);
writeGroup(dbc, child);
}
} else {
// move children
Iterator<CmsGroup> itChildren = children.iterator();
while (itChildren.hasNext()) {
CmsGroup child = itChildren.next();
child.setParentId(replacementId);
writeGroup(dbc, child);
}
// move users
Iterator<CmsUser> itUsers = users.iterator();
while (itUsers.hasNext()) {
CmsUser user = itUsers.next();
addUserToGroup(dbc, user.getName(), replacementGroup.getName(), group.isRole());
removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole());
}
// transfer for offline
transferPrincipalResources(dbc, dbc.currentProject(), group.getId(), replacementId, true);
// transfer for online
transferPrincipalResources(dbc, onlineProject, group.getId(), replacementId, true);
}
// remove the group
getUserDriver(dbc).removeAccessControlEntriesForPrincipal(
dbc,
dbc.currentProject(),
onlineProject,
group.getId());
getUserDriver(dbc).deleteGroup(dbc, group.getName());
// backup the group
getHistoryDriver(dbc).writePrincipal(dbc, group);
if (OpenCms.getSubscriptionManager().isEnabled()) {
// delete all subscribed resources for group
unsubscribeAllResourcesFor(dbc, OpenCms.getSubscriptionManager().getPoolName(), group);
}
// clear the relevant caches
m_monitor.uncacheGroup(group);
m_monitor.flushCache(
CmsMemoryMonitor.CacheType.USERGROUPS,
CmsMemoryMonitor.CacheType.USER_LIST,
CmsMemoryMonitor.CacheType.ACL);
if (!dbc.getProjectId().isNullUUID()) {
// group modified event is not needed
return;
}
// fire group modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_DELETE);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData));
}
/**
* Deletes the versions from the history tables, keeping the given number of versions per resource.<p>
*
* if the <code>cleanUp</code> option is set, additionally versions of deleted resources will be removed.<p>
*
* @param dbc the current database context
* @param versionsToKeep number of versions to keep, is ignored if negative
* @param versionsDeleted number of versions to keep for deleted resources, is ignored if negative
* @param timeDeleted deleted resources older than this will also be deleted, is ignored if negative
* @param report the report for output logging
*
* @throws CmsException if operation was not successful
*/
public void deleteHistoricalVersions(
CmsDbContext dbc,
int versionsToKeep,
int versionsDeleted,
long timeDeleted,
I_CmsReport report) throws CmsException {
report.println(Messages.get().container(Messages.RPT_START_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE);
if (versionsToKeep >= 0) {
report.println(
Messages.get().container(Messages.RPT_START_DELETE_ACT_VERSIONS_1, new Integer(versionsToKeep)),
I_CmsReport.FORMAT_HEADLINE);
List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllNotDeletedEntries(dbc);
if (resources.isEmpty()) {
report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK);
}
int n = resources.size();
int m = 1;
Iterator<I_CmsHistoryResource> itResources = resources.iterator();
while (itResources.hasNext()) {
I_CmsHistoryResource histResource = itResources.next();
report.print(
org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_SUCCESSION_2,
String.valueOf(m),
String.valueOf(n)),
I_CmsReport.FORMAT_NOTE);
report.print(org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_ARGUMENT_1,
dbc.removeSiteRoot(histResource.getRootPath())));
report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
try {
int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsToKeep, -1);
report.print(
Messages.get().container(Messages.RPT_VERSION_DELETING_1, new Integer(deleted)),
I_CmsReport.FORMAT_NOTE);
report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
I_CmsReport.FORMAT_OK);
} catch (CmsDataAccessException e) {
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
m++;
}
report.println(
Messages.get().container(Messages.RPT_END_DELETE_ACT_VERSIONS_0),
I_CmsReport.FORMAT_HEADLINE);
}
if ((versionsDeleted >= 0) || (timeDeleted >= 0)) {
if (timeDeleted >= 0) {
report.println(
Messages.get().container(
Messages.RPT_START_DELETE_DEL_VERSIONS_2,
new Integer(versionsDeleted),
new Date(timeDeleted)),
I_CmsReport.FORMAT_HEADLINE);
} else {
report.println(
Messages.get().container(Messages.RPT_START_DELETE_DEL_VERSIONS_1, new Integer(versionsDeleted)),
I_CmsReport.FORMAT_HEADLINE);
}
List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllDeletedEntries(dbc);
if (resources.isEmpty()) {
report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK);
}
int n = resources.size();
int m = 1;
Iterator<I_CmsHistoryResource> itResources = resources.iterator();
while (itResources.hasNext()) {
I_CmsHistoryResource histResource = itResources.next();
report.print(
org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_SUCCESSION_2,
String.valueOf(m),
String.valueOf(n)),
I_CmsReport.FORMAT_NOTE);
report.print(org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_ARGUMENT_1,
dbc.removeSiteRoot(histResource.getRootPath())));
report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
try {
int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsDeleted, timeDeleted);
report.print(
Messages.get().container(Messages.RPT_VERSION_DELETING_1, new Integer(deleted)),
I_CmsReport.FORMAT_NOTE);
report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
I_CmsReport.FORMAT_OK);
} catch (CmsDataAccessException e) {
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0),
I_CmsReport.FORMAT_ERROR);
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
m++;
}
report.println(
Messages.get().container(Messages.RPT_END_DELETE_DEL_VERSIONS_0),
I_CmsReport.FORMAT_HEADLINE);
}
report.println(Messages.get().container(Messages.RPT_END_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE);
}
/**
* Deletes all log entries matching the given filter.<p>
*
* @param dbc the current db context
* @param filter the filter to use for deletion
*
* @throws CmsException if something goes wrong
*
* @see CmsSecurityManager#deleteLogEntries(CmsRequestContext, CmsLogFilter)
*/
public void deleteLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException {
updateLog(dbc);
m_projectDriver.deleteLog(dbc, filter);
}
/**
* Deletes an organizational unit.<p>
*
* Only organizational units that contain no suborganizational unit can be deleted.<p>
*
* The organizational unit can not be delete if it is used in the request context,
* or if the current user belongs to it.<p>
*
* All users and groups in the given organizational unit will be deleted.<p>
*
* @param dbc the current db context
* @param organizationalUnit the organizational unit to delete
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#deleteOrganizationalUnit(CmsObject, String)
*/
public void deleteOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit)
throws CmsException {
// check organizational unit in context
if (dbc.getRequestContext().getOuFqn().equals(organizationalUnit.getName())) {
throw new CmsDbConsistencyException(Messages.get().container(
Messages.ERR_ORGUNIT_DELETE_IN_CONTEXT_1,
organizationalUnit.getName()));
}
// check organizational unit for user
if (dbc.currentUser().getOuFqn().equals(organizationalUnit.getName())) {
throw new CmsDbConsistencyException(Messages.get().container(
Messages.ERR_ORGUNIT_DELETE_CURRENT_USER_1,
organizationalUnit.getName()));
}
// check sub organizational units
if (!getOrganizationalUnits(dbc, organizationalUnit, true).isEmpty()) {
throw new CmsDbConsistencyException(Messages.get().container(
Messages.ERR_ORGUNIT_DELETE_SUB_ORGUNITS_1,
organizationalUnit.getName()));
}
// check groups
List<CmsGroup> groups = getGroups(dbc, organizationalUnit, true, false);
Iterator<CmsGroup> itGroups = groups.iterator();
while (itGroups.hasNext()) {
CmsGroup group = itGroups.next();
if (!OpenCms.getDefaultUsers().isDefaultGroup(group.getName())) {
throw new CmsDbConsistencyException(Messages.get().container(
Messages.ERR_ORGUNIT_DELETE_GROUPS_1,
organizationalUnit.getName()));
}
}
// check users
if (!getUsers(dbc, organizationalUnit, true).isEmpty()) {
throw new CmsDbConsistencyException(Messages.get().container(
Messages.ERR_ORGUNIT_DELETE_USERS_1,
organizationalUnit.getName()));
}
// delete default groups if needed
itGroups = groups.iterator();
while (itGroups.hasNext()) {
CmsGroup group = itGroups.next();
deleteGroup(dbc, group, null);
}
// delete projects
Iterator<CmsProject> itProjects = getProjectDriver(dbc).readProjects(dbc, organizationalUnit.getName()).iterator();
while (itProjects.hasNext()) {
CmsProject project = itProjects.next();
deleteProject(dbc, project);
}
// delete roles
Iterator<CmsGroup> itRoles = getGroups(dbc, organizationalUnit, true, true).iterator();
while (itRoles.hasNext()) {
CmsGroup role = itRoles.next();
deleteGroup(dbc, role, null);
}
// create a publish list for the 'virtual' publish event
CmsResource resource = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT);
CmsPublishList pl = new CmsPublishList(resource, false);
pl.add(resource, false);
// remove the organizational unit itself
getUserDriver(dbc).deleteOrganizationalUnit(dbc, organizationalUnit);
// write the publish history entry
getProjectDriver(dbc).writePublishHistory(
dbc,
pl.getPublishHistoryId(),
new CmsPublishedResource(resource, -1, CmsResourceState.STATE_DELETED));
// flush relevant caches
m_monitor.clearPrincipalsCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
// fire the 'virtual' publish event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString());
eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData);
OpenCms.fireCmsEvent(afterPublishEvent);
m_lockManager.removeDeletedResource(dbc, resource.getRootPath());
if (!dbc.getProjectId().isNullUUID()) {
// OU modified event is not needed
return;
}
// fire OU modified event
Map<String, Object> event2Data = new HashMap<String, Object>();
event2Data.put(I_CmsEventListener.KEY_OU_NAME, organizationalUnit.getName());
event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_DELETE);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data));
}
/**
* Deletes a project.<p>
*
* Only the admin or the owner of the project can do this.
*
* @param dbc the current database context
* @param deleteProject the project to be deleted
*
* @throws CmsException if something goes wrong
*/
public void deleteProject(CmsDbContext dbc, CmsProject deleteProject) throws CmsException {
CmsUUID projectId = deleteProject.getUuid();
// changed/new/deleted files in the specified project
List<CmsResource> modifiedFiles = readChangedResourcesInsideProject(dbc, projectId, RCPRM_FILES_ONLY_MODE);
// changed/new/deleted folders in the specified project
List<CmsResource> modifiedFolders = readChangedResourcesInsideProject(dbc, projectId, RCPRM_FOLDERS_ONLY_MODE);
// all resources inside the project have to be be reset to their online state.
// 1. step: delete all new files
for (int i = 0; i < modifiedFiles.size(); i++) {
CmsResource currentFile = modifiedFiles.get(i);
if (currentFile.getState().isNew()) {
CmsLock lock = getLock(dbc, currentFile);
if (lock.isNullLock()) {
// lock the resource
lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE);
} else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) {
changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE);
}
// delete the properties
getVfsDriver(dbc).deletePropertyObjects(
dbc,
projectId,
currentFile,
CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES);
// delete the file
getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentFile);
// remove the access control entries
getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFile.getResourceId());
// fire the corresponding event
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile)));
}
}
// 2. step: delete all new folders
for (int i = 0; i < modifiedFolders.size(); i++) {
CmsResource currentFolder = modifiedFolders.get(i);
if (currentFolder.getState().isNew()) {
// delete the properties
getVfsDriver(dbc).deletePropertyObjects(
dbc,
projectId,
currentFolder,
CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES);
// delete the folder
getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentFolder);
// remove the access control entries
getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFolder.getResourceId());
// fire the corresponding event
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder)));
}
}
// 3. step: undo changes on all changed or deleted folders
for (int i = 0; i < modifiedFolders.size(); i++) {
CmsResource currentFolder = modifiedFolders.get(i);
if ((currentFolder.getState().isChanged()) || (currentFolder.getState().isDeleted())) {
CmsLock lock = getLock(dbc, currentFolder);
if (lock.isNullLock()) {
// lock the resource
lockResource(dbc, currentFolder, CmsLockType.EXCLUSIVE);
} else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) {
changeLock(dbc, currentFolder, CmsLockType.EXCLUSIVE);
}
// undo all changes in the folder
undoChanges(dbc, currentFolder, CmsResource.UNDO_CONTENT);
// fire the corresponding event
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder)));
}
}
// 4. step: undo changes on all changed or deleted files
for (int i = 0; i < modifiedFiles.size(); i++) {
CmsResource currentFile = modifiedFiles.get(i);
if (currentFile.getState().isChanged() || currentFile.getState().isDeleted()) {
CmsLock lock = getLock(dbc, currentFile);
if (lock.isNullLock()) {
// lock the resource
lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE);
} else if (!lock.isOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) {
if (lock.isLockableBy(dbc.currentUser())) {
changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE);
}
}
// undo all changes in the file
undoChanges(dbc, currentFile, CmsResource.UNDO_CONTENT);
// fire the corresponding event
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile)));
}
}
// unlock all resources in the project
m_lockManager.removeResourcesInProject(deleteProject.getUuid(), true);
m_monitor.clearAccessControlListCache();
m_monitor.clearResourceCache();
// set project to online project if current project is the one which will be deleted
if (projectId.equals(dbc.currentProject().getUuid())) {
dbc.getRequestContext().setCurrentProject(readProject(dbc, CmsProject.ONLINE_PROJECT_ID));
}
// delete the project itself
getProjectDriver(dbc).deleteProject(dbc, deleteProject);
m_monitor.uncacheProject(deleteProject);
// fire the corresponding event
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_PROJECT_MODIFIED,
Collections.<String, Object> singletonMap("project", deleteProject)));
}
/**
* Deletes a property definition.<p>
*
* @param dbc the current database context
* @param name the name of the property definition to delete
*
* @throws CmsException if something goes wrong
*/
public void deletePropertyDefinition(CmsDbContext dbc, String name) throws CmsException {
CmsPropertyDefinition propertyDefinition = null;
try {
// first read and then delete the metadefinition.
propertyDefinition = readPropertyDefinition(dbc, name);
getVfsDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition);
getHistoryDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition);
} finally {
// fire an event that a property of a resource has been deleted
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_PROPERTY_DEFINITION_MODIFIED,
Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition)));
}
}
/**
* Deletes a publish job identified by its history id.<p>
*
* @param dbc the current database context
* @param publishHistoryId the history id identifying the publish job
*
* @throws CmsException if something goes wrong
*/
public void deletePublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
getProjectDriver(dbc).deletePublishJob(dbc, publishHistoryId);
}
/**
* Deletes the publish list assigned to a publish job.<p>
*
* @param dbc the current database context
* @param publishHistoryId the history id identifying the publish job
* @throws CmsException if something goes wrong
*/
public void deletePublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
getProjectDriver(dbc).deletePublishList(dbc, publishHistoryId);
}
/**
* Deletes all relations for the given resource matching the given filter.<p>
*
* @param dbc the current db context
* @param resource the resource to delete the relations for
* @param filter the filter to use for deletion
*
* @throws CmsException if something goes wrong
*
* @see CmsSecurityManager#deleteRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter)
*/
public void deleteRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter)
throws CmsException {
if (filter.includesDefinedInContent()) {
throw new CmsIllegalArgumentException(Messages.get().container(
Messages.ERR_DELETE_RELATION_IN_CONTENT_2,
dbc.removeSiteRoot(resource.getRootPath()),
filter.getTypes()));
}
getVfsDriver(dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), resource, filter);
setDateLastModified(dbc, resource, System.currentTimeMillis());
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_REMOVE_RELATION,
new String[] {resource.getRootPath(), filter.toString()}), false);
}
/**
* Deletes a resource.<p>
*
* The <code>siblingMode</code> parameter controls how to handle siblings
* during the delete operation.
* Possible values for this parameter are:
* <ul>
* <li><code>{@link CmsResource#DELETE_REMOVE_SIBLINGS}</code></li>
* <li><code>{@link CmsResource#DELETE_PRESERVE_SIBLINGS}</code></li>
* </ul><p>
*
* @param dbc the current database context
* @param resource the name of the resource to delete (full path)
* @param siblingMode indicates how to handle siblings of the deleted resource
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#deleteResource(String, CmsResource.CmsResourceDeleteMode)
* @see I_CmsResourceType#deleteResource(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode)
*/
public void deleteResource(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceDeleteMode siblingMode)
throws CmsException {
// upgrade a potential inherited, non-shared lock into a common lock
CmsLock currentLock = getLock(dbc, resource);
if (currentLock.getEditionLock().isDirectlyInherited()) {
// upgrade the lock status if required
lockResource(dbc, resource, CmsLockType.EXCLUSIVE);
}
// check if siblings of the resource exist and must be deleted as well
if (resource.isFolder()) {
// folder can have no siblings
siblingMode = CmsResource.DELETE_PRESERVE_SIBLINGS;
}
// if selected, add all siblings of this resource to the list of resources to be deleted
boolean allSiblingsRemoved;
List<CmsResource> resources;
if (siblingMode == CmsResource.DELETE_REMOVE_SIBLINGS) {
resources = new ArrayList<CmsResource>(readSiblings(dbc, resource, CmsResourceFilter.ALL));
allSiblingsRemoved = true;
// ensure that the resource requested to be deleted is the last resource that gets actually deleted
// to keep the shared locks of the siblings while those get deleted.
resources.remove(resource);
resources.add(resource);
} else {
// only delete the resource, no siblings
resources = Collections.singletonList(resource);
allSiblingsRemoved = false;
}
int size = resources.size();
// if we have only one resource no further check is required
if (size > 1) {
CmsMultiException me = new CmsMultiException();
// ensure that each sibling is unlocked or locked by the current user
for (int i = 0; i < size; i++) {
CmsResource currentResource = resources.get(i);
currentLock = getLock(dbc, currentResource);
if (!currentLock.getEditionLock().isUnlocked() && !currentLock.isOwnedBy(dbc.currentUser())) {
// the resource is locked by a user different from the current user
CmsRequestContext context = dbc.getRequestContext();
me.addException(new CmsLockException(org.opencms.lock.Messages.get().container(
org.opencms.lock.Messages.ERR_SIBLING_LOCKED_2,
context.getSitePath(currentResource),
context.getSitePath(resource))));
}
}
if (!me.getExceptions().isEmpty()) {
throw me;
}
}
boolean removeAce = true;
if (resource.isFolder()) {
// check if the folder has any resources in it
Iterator<CmsResource> childResources = getVfsDriver(dbc).readChildResources(
dbc,
dbc.currentProject(),
resource,
true,
true).iterator();
CmsUUID projectId = CmsProject.ONLINE_PROJECT_ID;
if (dbc.currentProject().isOnlineProject()) {
projectId = CmsUUID.getOpenCmsUUID(); // HACK: to get an offline project id
}
// collect the names of the resources inside the folder, excluding the moved resources
StringBuffer errorResNames = new StringBuffer(128);
while (childResources.hasNext()) {
CmsResource errorRes = childResources.next();
if (errorRes.getState().isDeleted()) {
continue;
}
// if deleting offline, or not moved, or just renamed inside the deleted folder
// so, it may remain some orphan online entries for moved resources
// which will be fixed during the publishing of the moved resources
boolean error = !dbc.currentProject().isOnlineProject();
if (!error) {
try {
String originalPath = getVfsDriver(dbc).readResource(
dbc,
projectId,
errorRes.getRootPath(),
true).getRootPath();
error = originalPath.equals(errorRes.getRootPath())
|| originalPath.startsWith(resource.getRootPath());
} catch (CmsVfsResourceNotFoundException e) {
// ignore
}
}
if (error) {
if (errorResNames.length() != 0) {
errorResNames.append(", ");
}
errorResNames.append("[" + dbc.removeSiteRoot(errorRes.getRootPath()) + "]");
}
}
// the current implementation only deletes empty folders
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(errorResNames.toString())) {
throw new CmsVfsException(org.opencms.db.generic.Messages.get().container(
org.opencms.db.generic.Messages.ERR_DELETE_NONEMTY_FOLDER_2,
dbc.removeSiteRoot(resource.getRootPath()),
errorResNames.toString()));
}
}
// delete all collected resources
for (int i = 0; i < size; i++) {
CmsResource currentResource = resources.get(i);
// try to delete/remove the resource only if the user has write access to the resource
// check permissions only for the sibling, the resource it self was already checked or
// is to be removed without write permissions, ie. while deleting a folder
if (!currentResource.equals(resource)
&& (I_CmsPermissionHandler.PERM_ALLOWED != m_securityManager.hasPermissions(
dbc,
currentResource,
CmsPermissionSet.ACCESS_WRITE,
true,
CmsResourceFilter.ALL))) {
// no write access to sibling - must keep ACE (see below)
allSiblingsRemoved = false;
} else {
// write access to sibling granted
boolean existsOnline = (getVfsDriver(dbc).validateStructureIdExists(
dbc,
CmsProject.ONLINE_PROJECT_ID,
currentResource.getStructureId()) || !(currentResource.getState().equals(CmsResource.STATE_NEW)));
if (!existsOnline) {
// the resource does not exist online => remove the resource
// this means the resource is "new" (blue) in the offline project
// delete all properties of this resource
deleteAllProperties(dbc, currentResource.getRootPath());
if (currentResource.isFolder()) {
getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentResource);
} else {
// check labels
if (currentResource.isLabeled() && !labelResource(dbc, currentResource, null, 2)) {
// update the resource flags to "un label" the other siblings
int flags = currentResource.getFlags();
flags &= ~CmsResource.FLAG_LABELED;
currentResource.setFlags(flags);
}
getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentResource);
}
// ensure an exclusive lock is removed in the lock manager for a deleted new resource,
// otherwise it would "stick" in the lock manager, preventing other users from creating
// a file with the same name (issue with temp files in editor)
m_lockManager.removeDeletedResource(dbc, currentResource.getRootPath());
// delete relations
getVfsDriver(dbc).deleteRelations(
dbc,
dbc.currentProject().getUuid(),
currentResource,
CmsRelationFilter.TARGETS);
getVfsDriver(dbc).deleteUrlNameMappingEntries(
dbc,
false,
CmsUrlNameMappingFilter.ALL.filterStructureId(currentResource.getStructureId()));
} else {
// the resource exists online => mark the resource as deleted
// structure record is removed during next publish
// if one (or more) siblings are not removed, the ACE can not be removed
removeAce = false;
// set resource state to deleted
currentResource.setState(CmsResource.STATE_DELETED);
getVfsDriver(dbc).writeResourceState(
dbc,
dbc.currentProject(),
currentResource,
UPDATE_STRUCTURE,
false);
// update the project ID
getVfsDriver(dbc).writeLastModifiedProjectId(
dbc,
dbc.currentProject(),
dbc.currentProject().getUuid(),
currentResource);
// log it
log(dbc, new CmsLogEntry(
dbc,
currentResource.getStructureId(),
CmsLogEntryType.RESOURCE_DELETED,
new String[] {currentResource.getRootPath()}), true);
}
}
}
if ((resource.getSiblingCount() <= 1) || allSiblingsRemoved) {
if (removeAce) {
// remove the access control entries
getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId());
}
}
// flush all caches
m_monitor.clearAccessControlListCache();
m_monitor.flushCache(
CmsMemoryMonitor.CacheType.PROPERTY,
CmsMemoryMonitor.CacheType.PROPERTY_LIST,
CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_DELETED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources)));
}
/**
* Deletes an entry in the published resource table.<p>
*
* @param dbc the current database context
* @param resourceName The name of the resource to be deleted in the static export
* @param linkType the type of resource deleted (0= non-parameter, 1=parameter)
* @param linkParameter the parameters of the resource
*
* @throws CmsException if something goes wrong
*/
public void deleteStaticExportPublishedResource(
CmsDbContext dbc,
String resourceName,
int linkType,
String linkParameter) throws CmsException {
getProjectDriver(dbc).deleteStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter);
}
/**
* Deletes a user, where all permissions and resources attributes of the user
* were transfered to a replacement user, if given.<p>
*
* Only users, which are in the group "administrators" are granted.<p>
*
* @param dbc the current database context
* @param project the current project
* @param username the name of the user to be deleted
* @param replacementUsername the name of the user to be transfered, can be <code>null</code>
*
* @throws CmsException if operation was not successful
*/
public void deleteUser(CmsDbContext dbc, CmsProject project, String username, String replacementUsername)
throws CmsException {
// Test if the users exists
CmsUser user = readUser(dbc, username);
CmsUser replacementUser = null;
if (replacementUsername != null) {
replacementUser = readUser(dbc, replacementUsername);
}
CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
boolean withACEs = true;
if (replacementUser == null) {
withACEs = false;
replacementUser = readUser(dbc, OpenCms.getDefaultUsers().getUserDeletedResource());
}
boolean isVfsManager = m_securityManager.hasRole(dbc, replacementUser, CmsRole.VFS_MANAGER);
// iterate groups and roles
for (boolean readRoles = false; !readRoles; readRoles = !readRoles) {
Iterator<CmsGroup> itGroups = getGroupsOfUser(
dbc,
username,
"",
true,
readRoles,
true,
dbc.getRequestContext().getRemoteAddress()).iterator();
while (itGroups.hasNext()) {
CmsGroup group = itGroups.next();
if (!isVfsManager) {
// add replacement user to user groups
if (!userInGroup(dbc, replacementUser.getName(), group.getName(), readRoles)) {
addUserToGroup(dbc, replacementUser.getName(), group.getName(), readRoles);
}
}
// remove user from groups
if (userInGroup(dbc, username, group.getName(), readRoles)) {
// we need this additional check because removing a user from a group
// may also automatically remove him from other groups if the group was
// associated with a role.
removeUserFromGroup(dbc, username, group.getName(), readRoles);
}
}
}
// remove all locks set for the deleted user
m_lockManager.removeLocks(user.getId());
// offline
if (dbc.getProjectId().isNullUUID()) {
// offline project available
transferPrincipalResources(dbc, project, user.getId(), replacementUser.getId(), withACEs);
}
// online
transferPrincipalResources(dbc, onlineProject, user.getId(), replacementUser.getId(), withACEs);
getUserDriver(dbc).removeAccessControlEntriesForPrincipal(dbc, project, onlineProject, user.getId());
getHistoryDriver(dbc).writePrincipal(dbc, user);
getUserDriver(dbc).deleteUser(dbc, username);
// delete user from cache
m_monitor.clearUserCache(user);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_DELETE_USER);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
}
/**
* Destroys this driver manager and releases all allocated resources.<p>
*/
public void destroy() {
try {
if (m_projectDriver != null) {
try {
m_projectDriver.destroy();
} catch (Throwable t) {
LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_PROJECT_DRIVER_0), t);
}
m_projectDriver = null;
}
if (m_userDriver != null) {
try {
m_userDriver.destroy();
} catch (Throwable t) {
LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_USER_DRIVER_0), t);
}
m_userDriver = null;
}
if (m_vfsDriver != null) {
try {
m_vfsDriver.destroy();
} catch (Throwable t) {
LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_VFS_DRIVER_0), t);
}
m_vfsDriver = null;
}
if (m_historyDriver != null) {
try {
m_historyDriver.destroy();
} catch (Throwable t) {
LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_HISTORY_DRIVER_0), t);
}
m_historyDriver = null;
}
if (m_connectionPools != null) {
for (int i = 0; i < m_connectionPools.size(); i++) {
PoolingDriver driver = m_connectionPools.get(i);
String[] pools = driver.getPoolNames();
for (int j = 0; j < pools.length; j++) {
try {
driver.closePool(pools[j]);
if (CmsLog.INIT.isDebugEnabled()) {
CmsLog.INIT.debug(Messages.get().getBundle().key(
Messages.INIT_CLOSE_CONN_POOL_1,
pools[j]));
}
} catch (Throwable t) {
LOG.error(Messages.get().getBundle().key(Messages.LOG_CLOSE_CONN_POOL_ERROR_1, pools[j]), t);
}
}
}
m_connectionPools = null;
}
m_monitor.clearCache();
m_lockManager = null;
m_htmlLinkValidator = null;
} catch (Throwable t) {
// ignore
}
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(
Messages.INIT_DRIVER_MANAGER_DESTROY_1,
getClass().getName()));
}
org.opencms.db.jpa.CmsSqlManager.destroy();
}
/**
* Tests if a resource with the given resourceId does already exist in the Database.<p>
*
* @param dbc the current database context
* @param resourceId the resource id to test for
* @return true if a resource with the given id was found, false otherweise
* @throws CmsException if something goes wrong
*/
public boolean existsResourceId(CmsDbContext dbc, CmsUUID resourceId) throws CmsException {
return getVfsDriver(dbc).validateResourceIdExists(dbc, dbc.currentProject().getUuid(), resourceId);
}
/**
* Fills the given publish list with the the VFS resources that actually get published.<p>
*
* Please refer to the source code of this method for the rules on how to decide whether a
* new/changed/deleted <code>{@link CmsResource}</code> object can be published or not.<p>
*
* @param dbc the current database context
* @param publishList must be initialized with basic publish information (Project or direct publish operation),
* the given publish list will be filled with all new/changed/deleted files from the current
* (offline) project that will be actually published
*
* @throws CmsException if something goes wrong
*
* @see org.opencms.db.CmsPublishList
*/
public void fillPublishList(CmsDbContext dbc, CmsPublishList publishList) throws CmsException {
if (!publishList.isDirectPublish()) {
// when publishing a project
// all modified resources with the last change done in the current project are candidates if unlocked
List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree(
dbc,
dbc.currentProject().getUuid(),
CmsDriverManager.READ_IGNORE_PARENT,
CmsDriverManager.READ_IGNORE_TYPE,
CmsResource.STATE_UNCHANGED,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READMODE_INCLUDE_TREE
| CmsDriverManager.READMODE_INCLUDE_PROJECT
| CmsDriverManager.READMODE_EXCLUDE_STATE
| CmsDriverManager.READMODE_ONLY_FOLDERS);
publishList.addAll(filterResources(dbc, null, folderList), true);
List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree(
dbc,
dbc.currentProject().getUuid(),
CmsDriverManager.READ_IGNORE_PARENT,
CmsDriverManager.READ_IGNORE_TYPE,
CmsResource.STATE_UNCHANGED,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READMODE_INCLUDE_TREE
| CmsDriverManager.READMODE_INCLUDE_PROJECT
| CmsDriverManager.READMODE_EXCLUDE_STATE
| CmsDriverManager.READMODE_ONLY_FILES);
publishList.addAll(filterResources(dbc, publishList, fileList), true);
} else {
// this is a direct publish
Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator();
while (it.hasNext()) {
// iterate all resources in the direct publish list
CmsResource directPublishResource = it.next();
if (directPublishResource.isFolder()) {
// when publishing a folder directly,
// the folder and all modified resources within the tree below this folder
// and with the last change done in the current project are candidates if lockable
CmsLock lock = getLock(dbc, directPublishResource);
if (!directPublishResource.getState().isUnchanged() && lock.isLockableBy(dbc.currentUser())) {
try {
m_securityManager.checkPermissions(
dbc,
directPublishResource,
CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
false,
CmsResourceFilter.ALL);
publishList.add(directPublishResource, true);
} catch (CmsException e) {
// skip if not enough permissions
}
}
boolean shouldPublishDeletedSubResources = publishList.isUserPublishList()
&& directPublishResource.getState().isDeleted();
if (publishList.isPublishSubResources() || shouldPublishDeletedSubResources) {
addSubResources(dbc, publishList, directPublishResource);
}
} else if (directPublishResource.isFile() && !directPublishResource.getState().isUnchanged()) {
// when publishing a file directly this file is the only candidate
// if it is modified and lockable
CmsLock lock = getLock(dbc, directPublishResource);
if (lock.isLockableBy(dbc.currentUser())) {
// check permissions
try {
m_securityManager.checkPermissions(
dbc,
directPublishResource,
CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
false,
CmsResourceFilter.ALL);
publishList.add(directPublishResource, true);
} catch (CmsException e) {
// skip if not enough permissions
}
}
}
}
}
// Step 2: if desired, extend the list of files to publish with related siblings
if (publishList.isPublishSiblings()) {
List<CmsResource> publishFiles = publishList.getFileList();
int size = publishFiles.size();
// Improved: first calculate closure of all siblings, then filter and add them
Set<CmsResource> siblingsClosure = new HashSet<CmsResource>(publishFiles);
for (int i = 0; i < size; i++) {
CmsResource currentFile = publishFiles.get(i);
if (currentFile.getSiblingCount() > 1) {
siblingsClosure.addAll(readSiblings(dbc, currentFile, CmsResourceFilter.ALL_MODIFIED));
}
}
publishList.addAll(filterSiblings(dbc, publishList, siblingsClosure), true);
}
publishList.initialize();
}
/**
* Returns the list of access control entries of a resource given its name.<p>
*
* @param dbc the current database context
* @param resource the resource to read the access control entries for
* @param getInherited true if the result should include all access control entries inherited by parent folders
*
* @return a list of <code>{@link CmsAccessControlEntry}</code> objects defining all permissions for the given resource
*
* @throws CmsException if something goes wrong
*/
public List<CmsAccessControlEntry> getAccessControlEntries(
CmsDbContext dbc,
CmsResource resource,
boolean getInherited) throws CmsException {
// get the ACE of the resource itself
I_CmsUserDriver userDriver = getUserDriver(dbc);
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries(
dbc,
dbc.currentProject(),
resource.getResourceId(),
false);
// sort and check if we got the 'overwrite all' ace to stop looking up
boolean overwriteAll = sortAceList(ace);
// get the ACE of each parent folder
// Note: for the immediate parent, get non-inherited access control entries too,
// if the resource is not a folder
String parentPath = CmsResource.getParentFolder(resource.getRootPath());
int d = (resource.isFolder()) ? 1 : 0;
while (!overwriteAll && getInherited && (parentPath != null)) {
resource = vfsDriver.readFolder(dbc, dbc.currentProject().getUuid(), parentPath);
List<CmsAccessControlEntry> entries = userDriver.readAccessControlEntries(
dbc,
dbc.currentProject(),
resource.getResourceId(),
d > 0);
// sort and check if we got the 'overwrite all' ace to stop looking up
overwriteAll = sortAceList(entries);
for (Iterator<CmsAccessControlEntry> i = entries.iterator(); i.hasNext();) {
CmsAccessControlEntry e = i.next();
e.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED);
}
ace.addAll(entries);
parentPath = CmsResource.getParentFolder(resource.getRootPath());
d++;
}
return ace;
}
/**
* Returns the full access control list of a given resource.<p>
*
* @param dbc the current database context
* @param resource the resource
*
* @return the access control list of the resource
*
* @throws CmsException if something goes wrong
*/
public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource) throws CmsException {
return getAccessControlList(dbc, resource, false);
}
/**
* Returns the access control list of a given resource.<p>
*
* If <code>inheritedOnly</code> is set, only inherited access control entries
* are returned.<p>
*
* Note: For file resources, *all* permissions set at the immediate parent folder are inherited,
* not only these marked to inherit.
*
* @param dbc the current database context
* @param resource the resource
* @param inheritedOnly skip non-inherited entries if set
*
* @return the access control list of the resource
*
* @throws CmsException if something goes wrong
*/
public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource, boolean inheritedOnly)
throws CmsException {
return getAccessControlList(dbc, resource, inheritedOnly, resource.isFolder(), 0);
}
/**
* Returns the number of active connections managed by a pool.<p>
*
* @param dbPoolUrl the url of a pool
* @return the number of active connections
* @throws CmsDbException if something goes wrong
*/
public int getActiveConnections(String dbPoolUrl) throws CmsDbException {
try {
for (Iterator<PoolingDriver> i = m_connectionPools.iterator(); i.hasNext();) {
PoolingDriver d = i.next();
ObjectPool p = d.getConnectionPool(dbPoolUrl);
return p.getNumActive();
}
} catch (Exception exc) {
CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl);
throw new CmsDbException(message, exc);
}
CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl);
throw new CmsDbException(message);
}
/**
* Returns all projects which are owned by the current user or which are
* accessible by the current user.<p>
*
* @param dbc the current database context
* @param orgUnit the organizational unit to search project in
* @param includeSubOus if to include sub organizational units
*
* @return a list of objects of type <code>{@link CmsProject}</code>
*
* @throws CmsException if something goes wrong
*/
public List<CmsProject> getAllAccessibleProjects(
CmsDbContext dbc,
CmsOrganizationalUnit orgUnit,
boolean includeSubOus) throws CmsException {
Set<CmsProject> projects = new HashSet<CmsProject>();
// get the ous where the user has the project manager role
List<CmsOrganizationalUnit> ous = getOrgUnitsForRole(
dbc,
CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()),
includeSubOus);
// get the groups of the user if needed
Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>();
Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator();
while (itGroups.hasNext()) {
CmsGroup group = itGroups.next();
userGroupIds.add(group.getId());
}
// TODO: this could be optimize if this method would have an additional parameter 'includeSubOus'
// get all projects that might come in question
projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName()));
// filter hidden and not accessible projects
Iterator<CmsProject> itProjects = projects.iterator();
while (itProjects.hasNext()) {
CmsProject project = itProjects.next();
boolean accessible = true;
// if hidden
accessible = accessible && !project.isHidden();
if (!includeSubOus) {
// if not exact in the given ou
accessible = accessible && project.getOuFqn().equals(orgUnit.getName());
} else {
// if not in the given ou
accessible = accessible && project.getOuFqn().startsWith(orgUnit.getName());
}
if (!accessible) {
itProjects.remove();
continue;
}
accessible = false;
// online project
accessible = accessible || project.isOnlineProject();
// if owner
accessible = accessible || project.getOwnerId().equals(dbc.currentUser().getId());
// project managers
Iterator<CmsOrganizationalUnit> itOus = ous.iterator();
while (!accessible && itOus.hasNext()) {
CmsOrganizationalUnit ou = itOus.next();
// for project managers check visibility
accessible = accessible || project.getOuFqn().startsWith(ou.getName());
}
if (!accessible) {
// if direct user or manager of project
CmsUUID groupId = null;
if (userGroupIds.contains(project.getGroupId())) {
groupId = project.getGroupId();
} else if (userGroupIds.contains(project.getManagerGroupId())) {
groupId = project.getManagerGroupId();
}
if (groupId != null) {
String oufqn = readGroup(dbc, groupId).getOuFqn();
accessible = accessible || (oufqn.startsWith(dbc.getRequestContext().getOuFqn()));
}
}
if (!accessible) {
// remove not accessible project
itProjects.remove();
}
}
List<CmsProject> accessibleProjects = new ArrayList<CmsProject>(projects);
// sort the list of projects based on the project name
Collections.sort(accessibleProjects);
// ensure the online project is in first place
CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
if (accessibleProjects.contains(onlineProject)) {
accessibleProjects.remove(onlineProject);
}
accessibleProjects.add(0, onlineProject);
return accessibleProjects;
}
/**
* Returns a list with all projects from history.<p>
*
* @param dbc the current database context
*
* @return list of <code>{@link CmsHistoryProject}</code> objects
* with all projects from history.
*
* @throws CmsException if operation was not successful
*/
public List<CmsHistoryProject> getAllHistoricalProjects(CmsDbContext dbc) throws CmsException {
// user is allowed to access all existing projects for the ous he has the project_manager role
Set<CmsOrganizationalUnit> manOus = new HashSet<CmsOrganizationalUnit>(getOrgUnitsForRole(
dbc,
CmsRole.PROJECT_MANAGER,
true));
List<CmsHistoryProject> projects = getHistoryDriver(dbc).readProjects(dbc);
Iterator<CmsHistoryProject> itProjects = projects.iterator();
while (itProjects.hasNext()) {
CmsHistoryProject project = itProjects.next();
if (project.isHidden()) {
// project is hidden
itProjects.remove();
continue;
}
if (!project.getOuFqn().startsWith(dbc.currentUser().getOuFqn())) {
// project is not visible from the users ou
itProjects.remove();
continue;
}
CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, project.getOuFqn());
if (manOus.contains(ou)) {
// user is project manager for this project
continue;
} else if (project.getOwnerId().equals(dbc.currentUser().getId())) {
// user is owner of the project
continue;
} else {
boolean found = false;
Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator();
while (itGroups.hasNext()) {
CmsGroup group = itGroups.next();
if (project.getManagerGroupId().equals(group.getId())) {
found = true;
break;
}
}
if (found) {
// user is member of the manager group of the project
continue;
}
}
itProjects.remove();
}
return projects;
}
/**
* Returns all projects which are owned by the current user or which are manageable
* for the group of the user.<p>
*
* @param dbc the current database context
* @param orgUnit the organizational unit to search project in
* @param includeSubOus if to include sub organizational units
*
* @return a list of objects of type <code>{@link CmsProject}</code>
*
* @throws CmsException if operation was not successful
*/
public List<CmsProject> getAllManageableProjects(
CmsDbContext dbc,
CmsOrganizationalUnit orgUnit,
boolean includeSubOus) throws CmsException {
Set<CmsProject> projects = new HashSet<CmsProject>();
// get the ous where the user has the project manager role
List<CmsOrganizationalUnit> ous = getOrgUnitsForRole(
dbc,
CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()),
includeSubOus);
// get the groups of the user if needed
Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>();
Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator();
while (itGroups.hasNext()) {
CmsGroup group = itGroups.next();
userGroupIds.add(group.getId());
}
// TODO: this could be optimize if this method would have an additional parameter 'includeSubOus'
// get all projects that might come in question
projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName()));
// filter hidden and not manageable projects
Iterator<CmsProject> itProjects = projects.iterator();
while (itProjects.hasNext()) {
CmsProject project = itProjects.next();
boolean manageable = true;
// if online
manageable = manageable && !project.isOnlineProject();
// if hidden
manageable = manageable && !project.isHidden();
if (!includeSubOus) {
// if not exact in the given ou
manageable = manageable && project.getOuFqn().equals(orgUnit.getName());
} else {
// if not in the given ou
manageable = manageable && project.getOuFqn().startsWith(orgUnit.getName());
}
if (!manageable) {
itProjects.remove();
continue;
}
manageable = false;
// if owner
manageable = manageable || project.getOwnerId().equals(dbc.currentUser().getId());
// project managers
Iterator<CmsOrganizationalUnit> itOus = ous.iterator();
while (!manageable && itOus.hasNext()) {
CmsOrganizationalUnit ou = itOus.next();
// for project managers check visibility
manageable = manageable || project.getOuFqn().startsWith(ou.getName());
}
if (!manageable) {
// if manager of project
if (userGroupIds.contains(project.getManagerGroupId())) {
String oufqn = readGroup(dbc, project.getManagerGroupId()).getOuFqn();
manageable = manageable || (oufqn.startsWith(dbc.getRequestContext().getOuFqn()));
}
}
if (!manageable) {
// remove not accessible project
itProjects.remove();
}
}
List<CmsProject> manageableProjects = new ArrayList<CmsProject>(projects);
// sort the list of projects based on the project name
Collections.sort(manageableProjects);
// ensure the online project is not in the list
CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
if (manageableProjects.contains(onlineProject)) {
manageableProjects.remove(onlineProject);
}
return manageableProjects;
}
/**
* Returns all child groups of a group.<p>
*
* @param dbc the current database context
* @param group the group to get the child for
* @param includeSubChildren if set also returns all sub-child groups of the given group
*
* @return a list of all child <code>{@link CmsGroup}</code> objects
*
* @throws CmsException if operation was not successful
*/
public List<CmsGroup> getChildren(CmsDbContext dbc, CmsGroup group, boolean includeSubChildren) throws CmsException {
if (!includeSubChildren) {
return getUserDriver(dbc).readChildGroups(dbc, group.getName());
}
Set<CmsGroup> allChildren = new TreeSet<CmsGroup>();
// iterate all child groups
Iterator<CmsGroup> it = getUserDriver(dbc).readChildGroups(dbc, group.getName()).iterator();
while (it.hasNext()) {
CmsGroup child = it.next();
// add the group itself
allChildren.add(child);
// now get all sub-children for each group
allChildren.addAll(getChildren(dbc, child, true));
}
return new ArrayList<CmsGroup>(allChildren);
}
/**
* Returns the date when the resource was last visited by the user.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param user the user to check the date
* @param resource the resource to check the date
*
* @return the date when the resource was last visited by the user
*
* @throws CmsException if something goes wrong
*/
public long getDateLastVisitedBy(CmsDbContext dbc, String poolName, CmsUser user, CmsResource resource)
throws CmsException {
return m_subscriptionDriver.getDateLastVisitedBy(dbc, poolName, user, resource);
}
/**
* Returns all groups of the given organizational unit.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to get the groups for
* @param includeSubOus if all groups of sub-organizational units should be retrieved too
* @param readRoles if to read roles or groups
*
* @return all <code>{@link CmsGroup}</code> objects in the organizational unit
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
* @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
*/
public List<CmsGroup> getGroups(
CmsDbContext dbc,
CmsOrganizationalUnit orgUnit,
boolean includeSubOus,
boolean readRoles) throws CmsException {
return getUserDriver(dbc).getGroups(dbc, orgUnit, includeSubOus, readRoles);
}
/**
* Returns the groups of an user filtered by the specified IP address.<p>
*
* @param dbc the current database context
* @param username the name of the user
* @param readRoles if to read roles or groups
*
* @return the groups of the given user, as a list of {@link CmsGroup} objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsGroup> getGroupsOfUser(CmsDbContext dbc, String username, boolean readRoles) throws CmsException {
return getGroupsOfUser(dbc, username, "", true, readRoles, false, dbc.getRequestContext().getRemoteAddress());
}
/**
* Returns the groups of an user filtered by the specified IP address.<p>
*
* @param dbc the current database context
* @param username the name of the user
* @param ouFqn the fully qualified name of the organizational unit to restrict the result set for
* @param includeChildOus include groups of child organizational units
* @param readRoles if to read roles or groups
* @param directGroupsOnly if set only the direct assigned groups will be returned, if not also indirect groups
* @param remoteAddress the IP address to filter the groups in the result list
*
* @return a list of <code>{@link CmsGroup}</code> objects
*
* @throws CmsException if operation was not successful
*/
public List<CmsGroup> getGroupsOfUser(
CmsDbContext dbc,
String username,
String ouFqn,
boolean includeChildOus,
boolean readRoles,
boolean directGroupsOnly,
String remoteAddress) throws CmsException {
CmsUser user = readUser(dbc, username);
String prefix = ouFqn + "_" + includeChildOus + "_" + directGroupsOnly + "_" + readRoles + "_" + remoteAddress;
String cacheKey = m_keyGenerator.getCacheKeyForUserGroups(prefix, dbc, user);
List<CmsGroup> groups = m_monitor.getCachedUserGroups(cacheKey);
if (groups == null) {
// get all groups of the user
List<CmsGroup> directGroups = getUserDriver(dbc).readGroupsOfUser(
dbc,
user.getId(),
readRoles ? "" : ouFqn,
readRoles ? true : includeChildOus,
remoteAddress,
readRoles);
Set<CmsGroup> allGroups = new HashSet<CmsGroup>();
if (!readRoles) {
allGroups.addAll(directGroups);
}
if (!directGroupsOnly) {
if (!readRoles) {
// now get all parents of the groups
for (int i = 0; i < directGroups.size(); i++) {
CmsGroup parent = getParent(dbc, directGroups.get(i).getName());
while ((parent != null) && (!allGroups.contains(parent))) {
if (parent.getOuFqn().startsWith(ouFqn)) {
allGroups.add(parent);
}
// read next parent group
parent = getParent(dbc, parent.getName());
}
}
}
}
if (readRoles) {
// for each for role
for (int i = 0; i < directGroups.size(); i++) {
CmsGroup group = directGroups.get(i);
CmsRole role = CmsRole.valueOf(group);
if (!includeChildOus && role.getOuFqn().equals(ouFqn)) {
allGroups.add(group);
}
if (includeChildOus && role.getOuFqn().startsWith(ouFqn)) {
allGroups.add(group);
}
if (directGroupsOnly) {
continue;
}
// get the child roles
Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator();
while (itChildRoles.hasNext()) {
CmsRole childRole = itChildRoles.next();
if (childRole.isSystemRole()) {
// include system roles only
allGroups.add(readGroup(dbc, childRole.getGroupName()));
}
}
if (includeChildOus) {
// if needed include the roles of child ous
Iterator<CmsOrganizationalUnit> itSubOus = getOrganizationalUnits(
dbc,
readOrganizationalUnit(dbc, group.getOuFqn()),
true).iterator();
while (itSubOus.hasNext()) {
CmsOrganizationalUnit subOu = itSubOus.next();
// add role in child ou
try {
allGroups.add(readGroup(dbc, role.forOrgUnit(subOu.getName()).getGroupName()));
} catch (CmsDbEntryNotFoundException e) {
// ignore, this may happen while deleting an orgunit
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
// add child roles in child ous
itChildRoles = role.getChildren(true).iterator();
while (itChildRoles.hasNext()) {
CmsRole childRole = itChildRoles.next();
try {
allGroups.add(readGroup(dbc, childRole.forOrgUnit(subOu.getName()).getGroupName()));
} catch (CmsDbEntryNotFoundException e) {
// ignore, this may happen while deleting an orgunit
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
}
}
}
}
// make group list unmodifiable for caching
groups = Collections.unmodifiableList(new ArrayList<CmsGroup>(allGroups));
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheUserGroups(cacheKey, groups);
}
}
return groups;
}
/**
* Returns the history driver.<p>
*
* @return the history driver
*/
public I_CmsHistoryDriver getHistoryDriver() {
return m_historyDriver;
}
/**
* Returns the history driver for a given database context.<p>
*
* @param dbc the database context
* @return the history driver for the database context
*/
public I_CmsHistoryDriver getHistoryDriver(CmsDbContext dbc) {
if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
return m_historyDriver;
}
I_CmsHistoryDriver driver = dbc.getHistoryDriver(dbc.getProjectId());
return driver != null ? driver : m_historyDriver;
}
/**
* Returns the number of idle connections managed by a pool.<p>
*
* @param dbPoolUrl the url of a pool
* @return the number of idle connections
* @throws CmsDbException if something goes wrong
*/
public int getIdleConnections(String dbPoolUrl) throws CmsDbException {
try {
for (Iterator<PoolingDriver> i = m_connectionPools.iterator(); i.hasNext();) {
PoolingDriver d = i.next();
ObjectPool p = d.getConnectionPool(dbPoolUrl);
return p.getNumIdle();
}
} catch (Exception exc) {
CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl);
throw new CmsDbException(message, exc);
}
CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl);
throw new CmsDbException(message);
}
/**
* Returns the lock state of a resource.<p>
*
* @param dbc the current database context
* @param resource the resource to return the lock state for
*
* @return the lock state of the resource
*
* @throws CmsException if something goes wrong
*/
public CmsLock getLock(CmsDbContext dbc, CmsResource resource) throws CmsException {
return m_lockManager.getLock(dbc, resource);
}
/**
* Returns all locked resources in a given folder.<p>
*
* @param dbc the current database context
* @param resource the folder to search in
* @param filter the lock filter
*
* @return a list of locked resource paths (relative to current site)
*
* @throws CmsException if the current project is locked
*/
public List<String> getLockedResources(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter)
throws CmsException {
List<String> lockedResources = new ArrayList<String>();
// get locked resources
Iterator<CmsLock> it = m_lockManager.getLocks(dbc, resource.getRootPath(), filter).iterator();
while (it.hasNext()) {
CmsLock lock = it.next();
lockedResources.add(dbc.removeSiteRoot(lock.getResourceName()));
}
Collections.sort(lockedResources);
return lockedResources;
}
/**
* Returns all locked resources in a given folder.<p>
*
* @param dbc the current database context
* @param resource the folder to search in
* @param filter the lock filter
*
* @return a list of locked resources
*
* @throws CmsException if the current project is locked
*/
public List<CmsResource> getLockedResourcesObjects(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter)
throws CmsException {
return m_lockManager.getLockedResources(dbc, resource, filter);
}
/**
* Returns all locked resources in a given folder, but uses a cache for resource lookups.<p>
*
* @param dbc the current database context
* @param resource the folder to search in
* @param filter the lock filter
* @param cache the cache to use for resource lookups
*
* @return a list of locked resources
*
* @throws CmsException if the current project is locked
*/
public List<CmsResource> getLockedResourcesObjectsWithCache(
CmsDbContext dbc,
CmsResource resource,
CmsLockFilter filter,
Map<String, CmsResource> cache) throws CmsException {
return m_lockManager.getLockedResourcesWithCache(dbc, resource, filter, cache);
}
/**
* Returns all log entries matching the given filter.<p>
*
* @param dbc the current db context
* @param filter the filter to match the log entries
*
* @return all log entries matching the given filter
*
* @throws CmsException if something goes wrong
*
* @see CmsSecurityManager#getLogEntries(CmsRequestContext, CmsLogFilter)
*/
public List<CmsLogEntry> getLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException {
updateLog(dbc);
return m_projectDriver.readLog(dbc, filter);
}
/**
* Returns the next publish tag for the published historical resources.<p>
*
* @param dbc the current database context
*
* @return the next available publish tag
*/
public int getNextPublishTag(CmsDbContext dbc) {
return getHistoryDriver(dbc).readNextPublishTag(dbc);
}
/**
* Returns all child organizational units of the given parent organizational unit including
* hierarchical deeper organization units if needed.<p>
*
* @param dbc the current db context
* @param parent the parent organizational unit, or <code>null</code> for the root
* @param includeChildren if hierarchical deeper organization units should also be returned
*
* @return a list of <code>{@link CmsOrganizationalUnit}</code> objects
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#getOrganizationalUnits(CmsObject, String, boolean)
*/
public List<CmsOrganizationalUnit> getOrganizationalUnits(
CmsDbContext dbc,
CmsOrganizationalUnit parent,
boolean includeChildren) throws CmsException {
if (parent == null) {
throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PARENT_ORGUNIT_NULL_0));
}
return getUserDriver(dbc).getOrganizationalUnits(dbc, parent, includeChildren);
}
/**
* Returns all the organizational units for which the current user has the given role.<p>
*
* @param dbc the current database context
* @param role the role to check
* @param includeSubOus if sub organizational units should be included in the search
*
* @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsOrganizationalUnit> getOrgUnitsForRole(CmsDbContext dbc, CmsRole role, boolean includeSubOus)
throws CmsException {
String ouFqn = role.getOuFqn();
if (ouFqn == null) {
ouFqn = "";
role = role.forOrgUnit("");
}
CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouFqn);
List<CmsOrganizationalUnit> orgUnits = new ArrayList<CmsOrganizationalUnit>();
if (m_securityManager.hasRole(dbc, dbc.currentUser(), role)) {
orgUnits.add(ou);
}
if (includeSubOus) {
Iterator<CmsOrganizationalUnit> it = getOrganizationalUnits(dbc, ou, true).iterator();
while (it.hasNext()) {
CmsOrganizationalUnit orgUnit = it.next();
if (m_securityManager.hasRole(dbc, dbc.currentUser(), role.forOrgUnit(orgUnit.getName()))) {
orgUnits.add(orgUnit);
}
}
}
return orgUnits;
}
/**
* Returns the parent group of a group.<p>
*
* @param dbc the current database context
* @param groupname the name of the group
*
* @return group the parent group or <code>null</code>
*
* @throws CmsException if operation was not successful
*/
public CmsGroup getParent(CmsDbContext dbc, String groupname) throws CmsException {
CmsGroup group = readGroup(dbc, groupname);
if (group.getParentId().isNullUUID()) {
return null;
}
// try to read from cache
CmsGroup parent = m_monitor.getCachedGroup(group.getParentId().toString());
if (parent == null) {
parent = getUserDriver(dbc).readGroup(dbc, group.getParentId());
m_monitor.cacheGroup(parent);
}
return parent;
}
/**
* Returns the set of permissions of the current user for a given resource.<p>
*
* @param dbc the current database context
* @param resource the resource
* @param user the user
*
* @return bit set with allowed permissions
*
* @throws CmsException if something goes wrong
*/
public CmsPermissionSetCustom getPermissions(CmsDbContext dbc, CmsResource resource, CmsUser user)
throws CmsException {
CmsAccessControlList acList = getAccessControlList(dbc, resource, false);
return acList.getPermissions(user, getGroupsOfUser(dbc, user.getName(), false), getRolesForUser(dbc, user));
}
/**
* Returns the project driver.<p>
*
* @return the project driver
*/
public I_CmsProjectDriver getProjectDriver() {
return m_projectDriver;
}
/**
* Returns the project driver for a given DB context.<p>
*
* @param dbc the database context
*
* @return the project driver for the database context
*/
public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc) {
if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
return m_projectDriver;
}
I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId());
return driver != null ? driver : m_projectDriver;
}
/**
* Returns either the project driver for the DB context (if it has one) or a default project driver.<p>
*
* @param dbc the DB context
* @param defaultDriver the driver which should be returned if there is no project driver for the DB context
*
* @return either the project driver for the DB context, or the default driver
*/
public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc, I_CmsProjectDriver defaultDriver) {
if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
return defaultDriver;
}
I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId());
return driver != null ? driver : defaultDriver;
}
/**
* Returns the uuid id for the given id.<p>
*
* TODO: remove this method as soon as possible
*
* @param dbc the current database context
* @param id the old project id
*
* @return the new uuid for the given id
*
* @throws CmsException if something goes wrong
*/
public CmsUUID getProjectId(CmsDbContext dbc, int id) throws CmsException {
Iterator<CmsProject> itProjects = getAllAccessibleProjects(dbc, readOrganizationalUnit(dbc, ""), true).iterator();
while (itProjects.hasNext()) {
CmsProject project = itProjects.next();
if (project.getUuid().hashCode() == id) {
return project.getUuid();
}
}
return null;
}
/**
* Returns the configuration read from the <code>opencms.properties</code> file.<p>
*
* @return the configuration read from the <code>opencms.properties</code> file
*/
public CmsParameterConfiguration getPropertyConfiguration() {
return m_propertyConfiguration;
}
/**
* Returns a new publish list that contains the unpublished resources related
* to all resources in the given publish list, the related resources exclude
* all resources in the given publish list and also locked (by other users) resources.<p>
*
* @param dbc the current database context
* @param publishList the publish list to exclude from result
* @param filter the relation filter to use to get the related resources
*
* @return a new publish list that contains the related resources
*
* @throws CmsException if something goes wrong
*
* @see org.opencms.publish.CmsPublishManager#getRelatedResourcesToPublish(CmsObject, CmsPublishList)
*/
public CmsPublishList getRelatedResourcesToPublish(
CmsDbContext dbc,
CmsPublishList publishList,
CmsRelationFilter filter) throws CmsException {
Map<String, CmsResource> relations = new HashMap<String, CmsResource>();
// check if progress should be set in the thread
CmsProgressThread thread = null;
if (Thread.currentThread() instanceof CmsProgressThread) {
thread = (CmsProgressThread)Thread.currentThread();
}
// get all resources to publish
List<CmsResource> publishResources = publishList.getAllResources();
Iterator<CmsResource> itCheckList = publishResources.iterator();
// iterate over them
int count = 0;
while (itCheckList.hasNext()) {
// set progress in thread
count++;
if (thread != null) {
if (thread.isInterrupted()) {
throw new CmsIllegalStateException(org.opencms.workplace.commons.Messages.get().container(
org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0));
}
thread.setProgress((count * 20) / publishResources.size());
thread.setDescription(org.opencms.workplace.commons.Messages.get().getBundle().key(
org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP1_2,
new Integer(count),
new Integer(publishResources.size())));
}
CmsResource checkResource = itCheckList.next();
// get and iterate over all related resources
Iterator<CmsRelation> itRelations = getRelationsForResource(dbc, checkResource, filter).iterator();
while (itRelations.hasNext()) {
CmsRelation relation = itRelations.next();
try {
// get the target of the relation, see CmsRelation#getTarget(CmsObject, CmsResourceFilter)
CmsResource target;
try {
// first look up by id
target = readResource(dbc, relation.getTargetId(), CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
// then look up by name, but from the root site
String storedSiteRoot = dbc.getRequestContext().getSiteRoot();
try {
dbc.getRequestContext().setSiteRoot("");
target = readResource(dbc, relation.getTargetPath(), CmsResourceFilter.ALL);
} finally {
dbc.getRequestContext().setSiteRoot(storedSiteRoot);
}
}
CmsLock lock = getLock(dbc, target);
// just add resources that may come in question
if (!publishResources.contains(target) // is not in the original list
&& !relations.containsKey(target.getRootPath()) // has not been already added by another relation
&& !target.getState().isUnchanged() // has been changed
&& lock.isLockableBy(dbc.currentUser())) { // is lockable by current user
relations.put(target.getRootPath(), target);
// now check the folder structure
CmsResource parent = getVfsDriver(dbc).readParentFolder(
dbc,
dbc.currentProject().getUuid(),
target.getStructureId());
while ((parent != null) && parent.getState().isNew()) {
// just add resources that may come in question
if (!publishResources.contains(parent) // is not in the original list
&& !relations.containsKey(parent.getRootPath())) { // has not been already added by another relation
relations.put(parent.getRootPath(), parent);
}
parent = getVfsDriver(dbc).readParentFolder(
dbc,
dbc.currentProject().getUuid(),
parent.getStructureId());
}
}
} catch (CmsVfsResourceNotFoundException e) {
// ignore broken links
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
}
CmsPublishList ret = new CmsPublishList(publishList.getDirectPublishResources(), false, false);
ret.addAll(relations.values(), false);
ret.initialize();
return ret;
}
/**
* Returns all relations for the given resource matching the given filter.<p>
*
* @param dbc the current db context
* @param resource the resource to retrieve the relations for
* @param filter the filter to match the relation
*
* @return all relations for the given resource matching the given filter
*
* @throws CmsException if something goes wrong
*
* @see CmsSecurityManager#getRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter)
*/
public List<CmsRelation> getRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter)
throws CmsException {
CmsUUID projectId = getProjectIdForContext(dbc);
return getVfsDriver(dbc).readRelations(dbc, projectId, resource, filter);
}
/**
* Returns the list of organizational units the given resource belongs to.<p>
*
* @param dbc the current database context
* @param resource the resource
*
* @return list of {@link CmsOrganizationalUnit} objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsOrganizationalUnit> getResourceOrgUnits(CmsDbContext dbc, CmsResource resource) throws CmsException {
List<CmsOrganizationalUnit> result = getVfsDriver(dbc).getResourceOus(
dbc,
dbc.currentProject().getUuid(),
resource);
return result;
}
/**
* Returns all resources of the given organizational unit.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to get all resources for
*
* @return all <code>{@link CmsResource}</code> objects in the organizational unit
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
* @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
* @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
*/
public List<CmsResource> getResourcesForOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit)
throws CmsException {
return getUserDriver(dbc).getResourcesForOrganizationalUnit(dbc, orgUnit);
}
/**
* Returns all resources associated to a given principal via an ACE with the given permissions.<p>
*
* If the <code>includeAttr</code> flag is set it returns also all resources associated to
* a given principal through some of following attributes.<p>
*
* <ul>
* <li>User Created</li>
* <li>User Last Modified</li>
* </ul><p>
*
* @param dbc the current database context
* @param project the to read the entries from
* @param principalId the id of the principal
* @param permissions a set of permissions to match, can be <code>null</code> for all ACEs
* @param includeAttr a flag to include resources associated by attributes
*
* @return a set of <code>{@link CmsResource}</code> objects
*
* @throws CmsException if something goes wrong
*/
public Set<CmsResource> getResourcesForPrincipal(
CmsDbContext dbc,
CmsProject project,
CmsUUID principalId,
CmsPermissionSet permissions,
boolean includeAttr) throws CmsException {
Set<CmsResource> resources = new HashSet<CmsResource>(getVfsDriver(dbc).readResourcesForPrincipalACE(
dbc,
project,
principalId));
if (permissions != null) {
Iterator<CmsResource> itRes = resources.iterator();
while (itRes.hasNext()) {
CmsAccessControlEntry ace = readAccessControlEntry(dbc, itRes.next(), principalId);
if ((ace.getPermissions().getPermissions() & permissions.getPermissions()) != permissions.getPermissions()) {
// remove if permissions does not match
itRes.remove();
}
}
}
if (includeAttr) {
resources.addAll(getVfsDriver(dbc).readResourcesForPrincipalAttr(dbc, project, principalId));
}
return resources;
}
/**
* Collects the groups which constitute a given role.<p>
*
* @param dbc the database context
* @param roleGroupName the group related to the role
* @param directUsersOnly if true, only the group belonging to the entry itself wil
*
* @return the set of groups which constitute the role
*
* @throws CmsException
*/
public Set<CmsGroup> getRoleGroups(CmsDbContext dbc, String roleGroupName, boolean directUsersOnly)
throws CmsException {
return getRoleGroupsImpl(dbc, roleGroupName, directUsersOnly, new HashMap<String, Set<CmsGroup>>());
}
/**
* Collects the groups which constitute a given role.<p>
*
* @param dbc the database context
* @param roleGroupName the group related to the role
* @param directUsersOnly if true, only the group belonging to the entry itself wil
* @param accumulator a map for memoizing return values of recursive calls
*
* @return the set of groups which constitute the role
*
* @throws CmsException
*/
public Set<CmsGroup> getRoleGroupsImpl(
CmsDbContext dbc,
String roleGroupName,
boolean directUsersOnly,
Map<String, Set<CmsGroup>> accumulator) throws CmsException {
Set<CmsGroup> result = new HashSet<CmsGroup>();
if (accumulator.get(roleGroupName) != null) {
return accumulator.get(roleGroupName);
}
CmsGroup group = readGroup(dbc, roleGroupName); // check that the group really exists
if ((group == null) || (!group.isRole())) {
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, roleGroupName));
}
result.add(group);
if (!directUsersOnly) {
CmsRole role = CmsRole.valueOf(group);
if (role.getParentRole() != null) {
try {
String parentGroup = role.getParentRole().getGroupName();
// iterate the parent roles
result.addAll(getRoleGroupsImpl(dbc, parentGroup, directUsersOnly, accumulator));
} catch (CmsDbEntryNotFoundException e) {
// ignore, this may happen while deleting an orgunit
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn());
if (parentOu != null) {
// iterate the parent ou's
result.addAll(getRoleGroupsImpl(dbc, parentOu + group.getSimpleName(), directUsersOnly, accumulator));
}
}
accumulator.put(roleGroupName, result);
return result;
}
/**
* Returns all roles the given user has for the given resource.<p>
*
* @param dbc the current database context
* @param user the user to check
* @param resource the resource to check the roles for
*
* @return a list of {@link CmsRole} objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsRole> getRolesForResource(CmsDbContext dbc, CmsUser user, CmsResource resource) throws CmsException {
// guest user has no role
if (user.isGuestUser()) {
return Collections.emptyList();
}
// try to read from cache
String key = user.getId().toString() + resource.getRootPath();
List<CmsRole> result = m_monitor.getCachedRoleList(key);
if (result != null) {
return result;
}
result = new ArrayList<CmsRole>();
Iterator<CmsOrganizationalUnit> itOus = getResourceOrgUnits(dbc, resource).iterator();
while (itOus.hasNext()) {
CmsOrganizationalUnit ou = itOus.next();
// read all roles of the current user
List<CmsGroup> groups = new ArrayList<CmsGroup>(getGroupsOfUser(
dbc,
user.getName(),
ou.getName(),
false,
true,
false,
dbc.getRequestContext().getRemoteAddress()));
// check the roles applying to the given resource
Iterator<CmsGroup> it = groups.iterator();
while (it.hasNext()) {
CmsGroup group = it.next();
CmsRole givenRole = CmsRole.valueOf(group).forOrgUnit(null);
if (givenRole.isOrganizationalUnitIndependent() || result.contains(givenRole)) {
// skip already added roles
continue;
}
result.add(givenRole);
}
}
result = Collections.unmodifiableList(result);
m_monitor.cacheRoleList(key, result);
return result;
}
/**
* Returns all roles the given user has independent of the resource.<p>
*
* @param dbc the current database context
* @param user the user to check
*
* @return a list of {@link CmsRole} objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsRole> getRolesForUser(CmsDbContext dbc, CmsUser user) throws CmsException {
// guest user has no role
if (user.isGuestUser()) {
return Collections.emptyList();
}
// try to read from cache
String key = user.getId().toString();
List<CmsRole> result = m_monitor.getCachedRoleList(key);
if (result != null) {
return result;
}
result = new ArrayList<CmsRole>();
// read all roles of the current user
List<CmsGroup> groups = new ArrayList<CmsGroup>(getGroupsOfUser(
dbc,
user.getName(),
"",
true,
true,
false,
dbc.getRequestContext().getRemoteAddress()));
// check the roles applying to the given resource
Iterator<CmsGroup> it = groups.iterator();
while (it.hasNext()) {
CmsGroup group = it.next();
CmsRole givenRole = CmsRole.valueOf(group);
givenRole = givenRole.forOrgUnit(null);
if (!result.contains(givenRole)) {
result.add(givenRole);
}
}
result = Collections.unmodifiableList(result);
m_monitor.cacheRoleList(key, result);
return result;
}
/**
* Returns the security manager this driver manager belongs to.<p>
*
* @return the security manager this driver manager belongs to
*/
public CmsSecurityManager getSecurityManager() {
return m_securityManager;
}
/**
* Returns an instance of the common sql manager.<p>
*
* @return an instance of the common sql manager
*/
public CmsSqlManager getSqlManager() {
return m_sqlManager;
}
/**
* Returns the subscription driver of this driver manager.<p>
*
* @return a subscription driver
*/
public I_CmsSubscriptionDriver getSubscriptionDriver() {
return m_subscriptionDriver;
}
/**
* Returns the user driver.<p>
*
* @return the user driver
*/
public I_CmsUserDriver getUserDriver() {
return m_userDriver;
}
/**
* Returns the user driver for a given database context.<p>
*
* @param dbc the database context
*
* @return the user driver for the database context
*/
public I_CmsUserDriver getUserDriver(CmsDbContext dbc) {
if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
return m_userDriver;
}
I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId());
return driver != null ? driver : m_userDriver;
}
/**
* Returns either the user driver for the given DB context (if it has one) or a default value instead.<p>
*
* @param dbc the DB context
* @param defaultDriver the driver that should be returned if no driver for the DB context was found
*
* @return either the user driver for the DB context, or <code>defaultDriver</code> if none were found
*/
public I_CmsUserDriver getUserDriver(CmsDbContext dbc, I_CmsUserDriver defaultDriver) {
if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
return defaultDriver;
}
I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId());
return driver != null ? driver : defaultDriver;
}
/**
* Returns all direct users of the given organizational unit.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to get all users for
* @param recursive if all groups of sub-organizational units should be retrieved too
*
* @return all <code>{@link CmsUser}</code> objects in the organizational unit
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
* @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
*/
public List<CmsUser> getUsers(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, boolean recursive)
throws CmsException {
return getUserDriver(dbc).getUsers(dbc, orgUnit, recursive);
}
/**
* Returns a list of users in a group.<p>
*
* @param dbc the current database context
* @param groupname the name of the group to list users from
* @param includeOtherOuUsers include users of other organizational units
* @param directUsersOnly if set only the direct assigned users will be returned,
* if not also indirect users, ie. members of parent roles,
* this parameter only works with roles
* @param readRoles if to read roles or groups
*
* @return all <code>{@link CmsUser}</code> objects in the group
*
* @throws CmsException if operation was not successful
*/
public List<CmsUser> getUsersOfGroup(
CmsDbContext dbc,
String groupname,
boolean includeOtherOuUsers,
boolean directUsersOnly,
boolean readRoles) throws CmsException {
return internalUsersOfGroup(
dbc,
CmsOrganizationalUnit.getParentFqn(groupname),
groupname,
includeOtherOuUsers,
directUsersOnly,
readRoles);
}
/**
* Returns the given user's publish list.<p>
*
* @param dbc the database context
* @param userId the user's id
*
* @return the given user's publish list
*
* @throws CmsDataAccessException if something goes wrong
*/
public List<CmsResource> getUsersPubList(CmsDbContext dbc, CmsUUID userId) throws CmsDataAccessException {
updateLog(dbc);
return m_projectDriver.getUsersPubList(dbc, userId);
}
/**
* Returns all direct users of the given organizational unit, without their additional info.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to get all users for
* @param recursive if all groups of sub-organizational units should be retrieved too
*
* @return all <code>{@link CmsUser}</code> objects in the organizational unit
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
* @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
*/
public List<CmsUser> getUsersWithoutAdditionalInfo(
CmsDbContext dbc,
CmsOrganizationalUnit orgUnit,
boolean recursive) throws CmsException {
return getUserDriver(dbc).getUsersWithoutAdditionalInfo(dbc, orgUnit, recursive);
}
/**
* Returns the VFS driver.<p>
*
* @return the VFS driver
*/
public I_CmsVfsDriver getVfsDriver() {
return m_vfsDriver;
}
/**
* Returns the VFS driver for the given database context.<p>
*
* @param dbc the database context
*
* @return a VFS driver
*/
public I_CmsVfsDriver getVfsDriver(CmsDbContext dbc) {
if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
return m_vfsDriver;
}
I_CmsVfsDriver driver = dbc.getVfsDriver(dbc.getProjectId());
return driver != null ? driver : m_vfsDriver;
}
/**
* Writes a vector of access control entries as new access control entries of a given resource.<p>
*
* Already existing access control entries of this resource are removed before.
* Access is granted, if:<p>
* <ul>
* <li>the current user has control permission on the resource</li>
* </ul>
*
* @param dbc the current database context
* @param resource the resource
* @param acEntries a list of <code>{@link CmsAccessControlEntry}</code> objects
*
* @throws CmsException if something goes wrong
*/
public void importAccessControlEntries(CmsDbContext dbc, CmsResource resource, List<CmsAccessControlEntry> acEntries)
throws CmsException {
I_CmsUserDriver userDriver = getUserDriver(dbc);
userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId());
Iterator<CmsAccessControlEntry> i = acEntries.iterator();
while (i.hasNext()) {
userDriver.writeAccessControlEntry(dbc, dbc.currentProject(), i.next());
}
m_monitor.clearAccessControlListCache();
}
/**
* Creates a new user by import.<p>
*
* @param dbc the current database context
* @param id the id of the user
* @param name the new name for the user
* @param password the new password for the user (already encrypted)
* @param firstname the firstname of the user
* @param lastname the lastname of the user
* @param email the email of the user
* @param flags the flags for a user (for example <code>{@link I_CmsPrincipal#FLAG_ENABLED}</code>)
* @param dateCreated the creation date
* @param additionalInfos the additional user infos
*
* @return the imported user
*
* @throws CmsException if something goes wrong
*/
public CmsUser importUser(
CmsDbContext dbc,
String id,
String name,
String password,
String firstname,
String lastname,
String email,
int flags,
long dateCreated,
Map<String, Object> additionalInfos) throws CmsException {
// no space before or after the name
name = name.trim();
// check the user name
String userName = CmsOrganizationalUnit.getSimpleName(name);
OpenCms.getValidationHandler().checkUserName(userName);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) {
throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName));
}
// check the ou
CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
// check webuser ou
if (ou.hasFlagWebuser() && ((flags & I_CmsPrincipal.FLAG_USER_WEBUSER) == 0)) {
flags += I_CmsPrincipal.FLAG_USER_WEBUSER;
}
CmsUser newUser = getUserDriver(dbc).createUser(
dbc,
new CmsUUID(id),
name,
password,
firstname,
lastname,
email,
0,
flags,
dateCreated,
additionalInfos);
return newUser;
}
/**
* Increments a counter and returns its value before incrementing.<p>
*
* @param dbc the current database context
* @param name the name of the counter which should be incremented
*
* @return the value of the counter
*
* @throws CmsException if something goes wrong
*/
public int incrementCounter(CmsDbContext dbc, String name) throws CmsException {
return getVfsDriver(dbc).incrementCounter(dbc, name);
}
/**
* Initializes the driver and sets up all required modules and connections.<p>
*
* @param configurationManager the configuration manager
* @param dbContextFactory the db context factory
*
* @throws CmsException if something goes wrong
* @throws Exception if something goes wrong
*/
public void init(CmsConfigurationManager configurationManager, I_CmsDbContextFactory dbContextFactory)
throws CmsException, Exception {
// initialize the access-module.
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE4_0));
}
// store local reference to the memory monitor to avoid multiple lookups through the OpenCms singelton
m_monitor = OpenCms.getMemoryMonitor();
CmsSystemConfiguration systemConfiguation = (CmsSystemConfiguration)configurationManager.getConfiguration(CmsSystemConfiguration.class);
CmsCacheSettings settings = systemConfiguation.getCacheSettings();
// initialize the key generator
m_keyGenerator = (I_CmsCacheKey)Class.forName(settings.getCacheKeyGenerator()).newInstance();
// initialize the HTML link validator
m_htmlLinkValidator = new CmsRelationSystemValidator(this);
// fills the defaults if needed
CmsDbContext dbc1 = dbContextFactory.getDbContext();
getUserDriver().fillDefaults(dbc1);
getProjectDriver().fillDefaults(dbc1);
// set the driver manager in the publish engine
m_publishEngine.setDriverManager(this);
// create the root organizational unit if needed
CmsDbContext dbc2 = dbContextFactory.getDbContext(new CmsRequestContext(
readUser(dbc1, OpenCms.getDefaultUsers().getUserAdmin()),
readProject(dbc1, CmsProject.ONLINE_PROJECT_ID),
null,
"",
null,
null,
null,
0,
null,
null,
""));
dbc1.clear();
getUserDriver().createRootOrganizationalUnit(dbc2);
dbc2.clear();
}
/**
* Checks if the specified resource is inside the current project.<p>
*
* The project "view" is determined by a set of path prefixes.
* If the resource starts with any one of this prefixes, it is considered to
* be "inside" the project.<p>
*
* @param dbc the current database context
* @param resourcename the specified resource name (full path)
*
* @return <code>true</code>, if the specified resource is inside the current project
*/
public boolean isInsideCurrentProject(CmsDbContext dbc, String resourcename) {
List<String> projectResources = null;
try {
projectResources = readProjectResources(dbc, dbc.currentProject());
} catch (CmsException e) {
if (LOG.isErrorEnabled()) {
LOG.error(
Messages.get().getBundle().key(
Messages.LOG_CHECK_RESOURCE_INSIDE_CURRENT_PROJECT_2,
resourcename,
dbc.currentProject().getName()),
e);
}
return false;
}
return CmsProject.isInsideProject(projectResources, resourcename);
}
/**
* Checks whether the subscription driver is available.<p>
*
* @return true if the subscription driver is available
*/
public boolean isSubscriptionDriverAvailable() {
return m_subscriptionDriver != null;
}
/**
* Checks if a project is the tempfile project.<p>
* @param project the project to test
* @return true if the project is the tempfile project
*/
public boolean isTempfileProject(CmsProject project) {
return project.getName().equals("tempFileProject");
}
/**
* Checks if one of the resources (except the resource itself)
* is a sibling in a "labeled" site folder.<p>
*
* This method is used when creating a new sibling
* (use the <code>newResource</code> parameter & <code>action = 1</code>)
* or deleting/importing a resource (call with <code>action = 2</code>).<p>
*
* @param dbc the current database context
* @param resource the resource
* @param newResource absolute path for a resource sibling which will be created
* @param action the action which has to be performed (1: create VFS link, 2: all other actions)
*
* @return <code>true</code> if the flag should be set for the resource, otherwise <code>false</code>
*
* @throws CmsDataAccessException if something goes wrong
*/
public boolean labelResource(CmsDbContext dbc, CmsResource resource, String newResource, int action)
throws CmsDataAccessException {
// get the list of labeled site folders from the runtime property
List<String> labeledSites = OpenCms.getWorkplaceManager().getLabelSiteFolders();
if (labeledSites.size() == 0) {
// no labeled sites defined, just return false
return false;
}
if (action == 1) {
// CASE 1: a new resource is created, check the sites
if (!resource.isLabeled()) {
// source isn't labeled yet, so check!
boolean linkInside = false;
boolean sourceInside = false;
for (int i = 0; i < labeledSites.size(); i++) {
String curSite = labeledSites.get(i);
if (newResource.startsWith(curSite)) {
// the link lies in a labeled site
linkInside = true;
}
if (resource.getRootPath().startsWith(curSite)) {
// the source lies in a labeled site
sourceInside = true;
}
if (linkInside && sourceInside) {
break;
}
}
// return true when either source or link is in labeled site, otherwise false
return (linkInside != sourceInside);
}
// resource is already labeled
return false;
} else {
// CASE 2: the resource will be deleted or created (import)
// check if at least one of the other siblings resides inside a "labeled site"
// and if at least one of the other siblings resides outside a "labeled site"
boolean isInside = false;
boolean isOutside = false;
// check if one of the other vfs links lies in a labeled site folder
List<CmsResource> siblings = getVfsDriver(dbc).readSiblings(
dbc,
dbc.currentProject().getUuid(),
resource,
false);
updateContextDates(dbc, siblings);
Iterator<CmsResource> i = siblings.iterator();
while (i.hasNext() && (!isInside || !isOutside)) {
CmsResource currentResource = i.next();
if (currentResource.equals(resource)) {
// dont't check the resource itself!
continue;
}
String curPath = currentResource.getRootPath();
boolean curInside = false;
for (int k = 0; k < labeledSites.size(); k++) {
if (curPath.startsWith(labeledSites.get(k))) {
// the link is in the labeled site
isInside = true;
curInside = true;
break;
}
}
if (!curInside) {
// the current link was not found in labeled site, so it is outside
isOutside = true;
}
}
// now check the new resource name if present
if (newResource != null) {
boolean curInside = false;
for (int k = 0; k < labeledSites.size(); k++) {
if (newResource.startsWith(labeledSites.get(k))) {
// the new resource is in the labeled site
isInside = true;
curInside = true;
break;
}
}
if (!curInside) {
// the new resource was not found in labeled site, so it is outside
isOutside = true;
}
}
return (isInside && isOutside);
}
}
/**
* Returns the user, who had locked the resource.<p>
*
* A user can lock a resource, so he is the only one who can write this
* resource. This methods checks, if a resource was locked.
*
* @param dbc the current database context
* @param resource the resource
*
* @return the user, who had locked the resource
*
* @throws CmsException will be thrown, if the user has not the rights for this resource
*/
public CmsUser lockedBy(CmsDbContext dbc, CmsResource resource) throws CmsException {
return readUser(dbc, m_lockManager.getLock(dbc, resource).getEditionLock().getUserId());
}
/**
* Locks a resource.<p>
*
* The <code>type</code> parameter controls what kind of lock is used.<br>
* Possible values for this parameter are: <br>
* <ul>
* <li><code>{@link org.opencms.lock.CmsLockType#EXCLUSIVE}</code></li>
* <li><code>{@link org.opencms.lock.CmsLockType#TEMPORARY}</code></li>
* <li><code>{@link org.opencms.lock.CmsLockType#PUBLISH}</code></li>
* </ul><p>
*
* @param dbc the current database context
* @param resource the resource to lock
* @param type type of the lock
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#lockResource(String)
* @see CmsObject#lockResourceTemporary(String)
* @see org.opencms.file.types.I_CmsResourceType#lockResource(CmsObject, CmsSecurityManager, CmsResource, CmsLockType)
*/
public void lockResource(CmsDbContext dbc, CmsResource resource, CmsLockType type) throws CmsException {
// update the resource cache
m_monitor.clearResourceCache();
CmsProject project = dbc.currentProject();
// add the resource to the lock dispatcher
m_lockManager.addResource(dbc, resource, dbc.currentUser(), project, type);
if (!resource.getState().isUnchanged() && !resource.getState().isKeep()) {
// update the project flag of a modified resource as "last modified inside the current project"
getVfsDriver(dbc).writeLastModifiedProjectId(dbc, project, project.getUuid(), resource);
}
// we must also clear the permission cache
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION);
// fire resource modification event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(NOTHING_CHANGED));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Adds the given log entry to the current user's log.<p>
*
* This operation works only on memory, to get the log entries actually
* written to DB you have to call the {@link #updateLog(CmsDbContext)} method.<p>
*
* @param dbc the current database context
* @param logEntry the log entry to create
* @param force forces the log entry to be counted,
* if not only the first log entry in a transaction will be taken into account
*/
public void log(CmsDbContext dbc, CmsLogEntry logEntry, boolean force) {
if (dbc == null) {
return;
}
// check log level
if (!logEntry.getType().isActive()) {
// do not log inactive entries
return;
}
// if not forcing
if (!force) {
// operation already logged
boolean abort = (dbc.getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null);
// disabled logging from outside
abort |= (dbc.getRequestContext().getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null);
if (abort) {
return;
}
}
// prevent several entries for the same operation
dbc.setAttribute(CmsLogEntry.ATTR_LOG_ENTRY, Boolean.TRUE);
// keep it for later
m_log.add(logEntry);
}
/**
* Attempts to authenticate a user into OpenCms with the given password.<p>
*
* @param dbc the current database context
* @param userName the name of the user to be logged in
* @param password the password of the user
* @param remoteAddress the ip address of the request
*
* @return the logged in user
*
* @throws CmsAuthentificationException if the login was not successful
* @throws CmsDataAccessException in case of errors accessing the database
* @throws CmsPasswordEncryptionException in case of errors encrypting the users password
*/
public CmsUser loginUser(CmsDbContext dbc, String userName, String password, String remoteAddress)
throws CmsAuthentificationException, CmsDataAccessException, CmsPasswordEncryptionException {
if (CmsStringUtil.isEmptyOrWhitespaceOnly(password)) {
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, userName));
}
CmsUser newUser;
try {
// read the user from the driver to avoid the cache
newUser = getUserDriver(dbc).readUser(dbc, userName, password, remoteAddress);
} catch (CmsDbEntryNotFoundException e) {
// this indicates that the username / password combination does not exist
// any other exception indicates database issues, these are not catched here
// check if a user with this name exists at all
CmsUser user = null;
try {
user = readUser(dbc, userName);
} catch (CmsDataAccessException e2) {
// apparently this user does not exist in the database
}
if (user != null) {
if (dbc.currentUser().isGuestUser()) {
// add an invalid login attempt for this user to the storage
OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress);
}
OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress);
throw new CmsAuthentificationException(org.opencms.security.Messages.get().container(
org.opencms.security.Messages.ERR_LOGIN_FAILED_2,
userName,
remoteAddress), e);
} else {
String userOu = CmsOrganizationalUnit.getParentFqn(userName);
if (userOu != null) {
String parentOu = CmsOrganizationalUnit.getParentFqn(userOu);
if (parentOu != null) {
// try a higher level ou
String uName = CmsOrganizationalUnit.getSimpleName(userName);
return loginUser(dbc, parentOu + uName, password, remoteAddress);
}
}
throw new CmsAuthentificationException(org.opencms.security.Messages.get().container(
org.opencms.security.Messages.ERR_LOGIN_FAILED_NO_USER_2,
userName,
remoteAddress), e);
}
}
// check if the "enabled" flag is set for the user
if (!newUser.isEnabled()) {
// user is disabled, throw a securiy exception
throw new CmsAuthentificationException(org.opencms.security.Messages.get().container(
org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2,
userName,
remoteAddress));
}
if (dbc.currentUser().isGuestUser()) {
// check if this account is temporarily disabled because of too many invalid login attempts
// this will throw an exception if the test fails
OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress);
// test successful, remove all previous invalid login attempts for this user from the storage
OpenCms.getLoginManager().removeInvalidLogins(userName, remoteAddress);
}
if (!m_securityManager.hasRole(
dbc,
newUser,
CmsRole.ADMINISTRATOR.forOrgUnit(dbc.getRequestContext().getOuFqn()))) {
// new user is not Administrator, check if login is currently allowed
OpenCms.getLoginManager().checkLoginAllowed();
}
m_monitor.clearUserCache(newUser);
// set the last login time to the current time
newUser.setLastlogin(System.currentTimeMillis());
dbc.setAttribute(ATTRIBUTE_LOGIN, newUser.getName());
// write the changed user object back to the user driver
getUserDriver(dbc).writeUser(dbc, newUser);
// update cache
m_monitor.cacheUser(newUser);
// invalidate all user dependent caches
m_monitor.flushCache(
CmsMemoryMonitor.CacheType.ACL,
CmsMemoryMonitor.CacheType.GROUP,
CmsMemoryMonitor.CacheType.ORG_UNIT,
CmsMemoryMonitor.CacheType.USERGROUPS,
CmsMemoryMonitor.CacheType.USER_LIST,
CmsMemoryMonitor.CacheType.PERMISSION,
CmsMemoryMonitor.CacheType.RESOURCE_LIST);
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, newUser.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_NAME, newUser.getName());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
// return the user object read from the driver
return newUser;
}
/**
* Lookup and read the user or group with the given UUID.<p>
*
* @param dbc the current database context
* @param principalId the UUID of the principal to lookup
*
* @return the principal (group or user) if found, otherwise <code>null</code>
*/
public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, CmsUUID principalId) {
try {
CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalId);
if (group != null) {
return group;
}
} catch (Exception e) {
// ignore this exception
}
try {
CmsUser user = readUser(dbc, principalId);
if (user != null) {
return user;
}
} catch (Exception e) {
// ignore this exception
}
return null;
}
/**
* Lookup and read the user or group with the given name.<p>
*
* @param dbc the current database context
* @param principalName the name of the principal to lookup
*
* @return the principal (group or user) if found, otherwise <code>null</code>
*/
public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, String principalName) {
try {
CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalName);
if (group != null) {
return group;
}
} catch (Exception e) {
// ignore this exception
}
try {
CmsUser user = readUser(dbc, principalName);
if (user != null) {
return user;
}
} catch (Exception e) {
// ignore this exception
}
return null;
}
/**
* Mark the given resource as visited by the user.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param resource the resource to mark as visited
* @param user the user that visited the resource
*
* @throws CmsException if something goes wrong
*/
public void markResourceAsVisitedBy(CmsDbContext dbc, String poolName, CmsResource resource, CmsUser user)
throws CmsException {
getSubscriptionDriver().markResourceAsVisitedBy(dbc, poolName, resource, user);
}
/**
* Moves a resource.<p>
*
* You must ensure that the parent of the destination path is an absolute, valid and
* existing VFS path. Relative paths from the source are not supported.<p>
*
* The moved resource will always be locked to the current user
* after the move operation.<p>
*
* In case the target resource already exists, it will be overwritten with the
* source resource if possible.<p>
*
* @param dbc the current database context
* @param source the resource to move
* @param destination the name of the move destination with complete path
* @param internal if set nothing more than the path is modified
*
* @throws CmsException if something goes wrong
*
* @see CmsSecurityManager#moveResource(CmsRequestContext, CmsResource, String)
*/
public void moveResource(CmsDbContext dbc, CmsResource source, String destination, boolean internal)
throws CmsException {
CmsFolder destinationFolder = readFolder(dbc, CmsResource.getParentFolder(destination), CmsResourceFilter.ALL);
m_securityManager.checkPermissions(
dbc,
destinationFolder,
CmsPermissionSet.ACCESS_WRITE,
false,
CmsResourceFilter.ALL);
if (source.isFolder()) {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
}
getVfsDriver(dbc).moveResource(dbc, dbc.getRequestContext().getCurrentProject().getUuid(), source, destination);
if (!internal) {
CmsResourceState newState = CmsResource.STATE_CHANGED;
if (source.getState().isNew()) {
newState = CmsResource.STATE_NEW;
} else if (source.getState().isDeleted()) {
newState = CmsResource.STATE_DELETED;
}
source.setState(newState);
// safe since this operation always uses the ids instead of the resource path
getVfsDriver(dbc).writeResourceState(
dbc,
dbc.currentProject(),
source,
CmsDriverManager.UPDATE_STRUCTURE_STATE,
false);
// log it
log(
dbc,
new CmsLogEntry(dbc, source.getStructureId(), CmsLogEntryType.RESOURCE_MOVED, new String[] {
source.getRootPath(),
destination}),
false);
}
CmsResource destRes = readResource(dbc, destination, CmsResourceFilter.ALL);
// move lock
m_lockManager.moveResource(source.getRootPath(), destRes.getRootPath());
// flush all relevant caches
m_monitor.clearAccessControlListCache();
m_monitor.flushCache(
CmsMemoryMonitor.CacheType.PROPERTY,
CmsMemoryMonitor.CacheType.PROPERTY_LIST,
CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
List<CmsResource> resources = new ArrayList<CmsResource>(4);
// source
resources.add(source);
try {
resources.add(readFolder(dbc, CmsResource.getParentFolder(source.getRootPath()), CmsResourceFilter.ALL));
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug(e);
}
}
// destination
resources.add(destRes);
resources.add(destinationFolder);
// fire the events
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_MOVED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources)));
}
/**
* Moves a resource to the "lost and found" folder.<p>
*
* The method can also be used to check get the name of a resource
* in the "lost and found" folder only without actually moving the
* the resource. To do this, the <code>returnNameOnly</code> flag
* must be set to <code>true</code>.<p>
*
* @param dbc the current database context
* @param resource the resource to apply this operation to
* @param returnNameOnly if <code>true</code>, only the name of the resource in the "lost and found"
* folder is returned, the move operation is not really performed
*
* @return the name of the resource inside the "lost and found" folder
*
* @throws CmsException if something goes wrong
* @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0
*
* @see CmsObject#moveToLostAndFound(String)
* @see CmsObject#getLostAndFoundName(String)
*/
public String moveToLostAndFound(CmsDbContext dbc, CmsResource resource, boolean returnNameOnly)
throws CmsException, CmsIllegalArgumentException {
String resourcename = dbc.removeSiteRoot(resource.getRootPath());
String siteRoot = dbc.getRequestContext().getSiteRoot();
dbc.getRequestContext().setSiteRoot("");
String destination = CmsDriverManager.LOST_AND_FOUND_FOLDER + resourcename;
// create the required folders if necessary
try {
// collect all folders...
String folderPath = CmsResource.getParentFolder(destination);
folderPath = folderPath.substring(1, folderPath.length() - 1); // cut out leading and trailing '/'
Iterator<String> folders = CmsStringUtil.splitAsList(folderPath, '/').iterator();
// ...now create them....
folderPath = "/";
while (folders.hasNext()) {
folderPath += folders.next().toString() + "/";
try {
readFolder(dbc, folderPath, CmsResourceFilter.IGNORE_EXPIRATION);
} catch (Exception e1) {
if (returnNameOnly) {
// we can use the original name without risk, and we do not need to recreate the parent folders
break;
}
// the folder is not existing, so create it
createResource(
dbc,
folderPath,
CmsResourceTypeFolder.RESOURCE_TYPE_ID,
null,
new ArrayList<CmsProperty>());
}
}
// check if this resource name does already exist
// if so add a postfix to the name
String des = destination;
int postfix = 1;
boolean found = true;
while (found) {
try {
// try to read the file.....
found = true;
readResource(dbc, des, CmsResourceFilter.ALL);
// ....it's there, so add a postfix and try again
String path = destination.substring(0, destination.lastIndexOf('/') + 1);
String filename = destination.substring(destination.lastIndexOf('/') + 1, destination.length());
des = path;
if (filename.lastIndexOf('.') > 0) {
des += filename.substring(0, filename.lastIndexOf('.'));
} else {
des += filename;
}
des += "_" + postfix;
if (filename.lastIndexOf('.') > 0) {
des += filename.substring(filename.lastIndexOf('.'), filename.length());
}
postfix++;
} catch (CmsException e3) {
// the file does not exist, so we can use this filename
found = false;
}
}
destination = des;
if (!returnNameOnly) {
// do not use the move semantic here! to prevent links pointing to the lost & found folder
copyResource(dbc, resource, destination, CmsResource.COPY_AS_SIBLING);
deleteResource(dbc, resource, CmsResource.DELETE_PRESERVE_SIBLINGS);
}
} catch (CmsException e2) {
throw e2;
} finally {
// set the site root to the old value again
dbc.getRequestContext().setSiteRoot(siteRoot);
}
return destination;
}
/**
* Gets a new driver instance.<p>
*
* @param dbc the database context
* @param configurationManager the configuration manager
* @param driverName the driver name
* @param successiveDrivers the list of successive drivers
*
* @return the driver object
* @throws CmsInitException if the selected driver could not be initialized
*/
public Object newDriverInstance(
CmsDbContext dbc,
CmsConfigurationManager configurationManager,
String driverName,
List<String> successiveDrivers) throws CmsInitException {
Class<?> driverClass = null;
I_CmsDriver driver = null;
try {
// try to get the class
driverClass = Class.forName(driverName);
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName));
}
// try to create a instance
driver = (I_CmsDriver)driverClass.newInstance();
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName));
}
// invoke the init-method of this access class
driver.init(dbc, configurationManager, successiveDrivers, this);
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_0));
}
} catch (Throwable t) {
CmsMessageContainer message = Messages.get().container(Messages.ERR_ERROR_INITIALIZING_DRIVER_1, driverName);
if (LOG.isErrorEnabled()) {
LOG.error(message.key(), t);
}
throw new CmsInitException(message, t);
}
return driver;
}
/**
* Method to create a new instance of a driver.<p>
*
* @param configuration the configurations from the propertyfile
* @param driverName the class name of the driver
* @param driverPoolUrl the pool url for the driver
* @return an initialized instance of the driver
* @throws CmsException if something goes wrong
*/
public Object newDriverInstance(CmsParameterConfiguration configuration, String driverName, String driverPoolUrl)
throws CmsException {
Class<?>[] initParamClasses = {CmsParameterConfiguration.class, String.class, CmsDriverManager.class};
Object[] initParams = {configuration, driverPoolUrl, this};
Class<?> driverClass = null;
Object driver = null;
try {
// try to get the class
driverClass = Class.forName(driverName);
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName));
}
// try to create a instance
driver = driverClass.newInstance();
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName));
}
// invoke the init-method of this access class
driver.getClass().getMethod("init", initParamClasses).invoke(driver, initParams);
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_1, driverPoolUrl));
}
} catch (Exception exc) {
CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_DRIVER_MANAGER_1);
if (LOG.isFatalEnabled()) {
LOG.fatal(message.key(), exc);
}
throw new CmsDbException(message, exc);
}
return driver;
}
/**
* Method to create a new instance of a pool.<p>
*
* @param configuration the configurations from the propertyfile
* @param poolName the configuration name of the pool
*
* @throws CmsInitException if the pools could not be initialized
*/
public void newPoolInstance(CmsParameterConfiguration configuration, String poolName) throws CmsInitException {
PoolingDriver driver;
try {
driver = CmsDbPool.createDriverManagerConnectionPool(configuration, poolName);
} catch (Exception e) {
CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_CONN_POOL_1, poolName);
if (LOG.isErrorEnabled()) {
LOG.error(message.key(), e);
}
throw new CmsInitException(message, e);
}
m_connectionPools.add(driver);
}
/**
* Publishes the given publish job.<p>
*
* @param cms the cms context
* @param dbc the db context
* @param publishList the list of resources to publish
* @param report the report to write to
*
* @throws CmsException if something goes wrong
*/
public void publishJob(CmsObject cms, CmsDbContext dbc, CmsPublishList publishList, I_CmsReport report)
throws CmsException {
try {
// check state and lock
List<CmsResource> allResources = new ArrayList<CmsResource>(publishList.getFolderList());
allResources.addAll(publishList.getDeletedFolderList());
allResources.addAll(publishList.getFileList());
Iterator<CmsResource> itResources = allResources.iterator();
while (itResources.hasNext()) {
CmsResource resource = itResources.next();
try {
resource = readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL);
} catch (CmsVfsResourceNotFoundException e) {
continue;
}
if (resource.getState().isUnchanged()) {
// remove files that were published by a concurrent job
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(
Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
publishList.remove(resource);
unlockResource(dbc, resource, true, true);
continue;
}
CmsLock lock = m_lockManager.getLock(dbc, resource, false);
if (!lock.getSystemLock().isPublish()) {
// remove files that are not locked for publishing
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.get().getBundle().key(
Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
publishList.remove(resource);
continue;
}
}
CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
// clear the cache
m_monitor.clearCache();
int publishTag = getNextPublishTag(dbc);
getProjectDriver(dbc).publishProject(dbc, report, onlineProject, publishList, publishTag);
// iterate the initialized module action instances
Iterator<String> i = OpenCms.getModuleManager().getModuleNames().iterator();
while (i.hasNext()) {
CmsModule module = OpenCms.getModuleManager().getModule(i.next());
if ((module != null) && (module.getActionInstance() != null)) {
module.getActionInstance().publishProject(cms, publishList, publishTag, report);
}
}
boolean temporaryProject = (cms.getRequestContext().getCurrentProject().getType() == CmsProject.PROJECT_TYPE_TEMPORARY);
// the project was stored in the history tables for history
// it will be deleted if the project_flag is PROJECT_TYPE_TEMPORARY
if ((temporaryProject) && (!publishList.isDirectPublish())) {
try {
getProjectDriver(dbc).deleteProject(dbc, dbc.currentProject());
} catch (CmsException e) {
LOG.error(Messages.get().getBundle().key(
Messages.LOG_DELETE_TEMP_PROJECT_FAILED_1,
cms.getRequestContext().getCurrentProject().getName()));
}
// if project was temporary set context to online project
cms.getRequestContext().setCurrentProject(onlineProject);
}
} finally {
// clear the cache again
m_monitor.clearCache();
}
}
/**
* Publishes the resources of a specified publish list.<p>
*
* @param cms the current request context
* @param dbc the current database context
* @param publishList a publish list
* @param report an instance of <code>{@link I_CmsReport}</code> to print messages
*
* @throws CmsException if something goes wrong
*
* @see #fillPublishList(CmsDbContext, CmsPublishList)
*/
public synchronized void publishProject(
CmsObject cms,
CmsDbContext dbc,
CmsPublishList publishList,
I_CmsReport report) throws CmsException {
// check the parent folders
checkParentFolders(dbc, publishList);
ensureSubResourcesOfMovedFoldersPublished(cms, dbc, publishList);
try {
// fire an event that a project is to be published
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_REPORT, report);
eventData.put(I_CmsEventListener.KEY_PUBLISHLIST, publishList);
eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
CmsEvent beforePublishEvent = new CmsEvent(I_CmsEventListener.EVENT_BEFORE_PUBLISH_PROJECT, eventData);
OpenCms.fireCmsEvent(beforePublishEvent);
} catch (Throwable t) {
if (report != null) {
report.addError(t);
report.println(t);
}
if (LOG.isErrorEnabled()) {
LOG.error(t.getLocalizedMessage(), t);
}
}
// lock all resources with the special publish lock
Iterator<CmsResource> itResources = new ArrayList<CmsResource>(publishList.getAllResources()).iterator();
while (itResources.hasNext()) {
CmsResource resource = itResources.next();
CmsLock lock = m_lockManager.getLock(dbc, resource, false);
if (lock.getSystemLock().isUnlocked() && lock.isLockableBy(dbc.currentUser())) {
if (getLock(dbc, resource).getEditionLock().isNullLock()) {
lockResource(dbc, resource, CmsLockType.PUBLISH);
} else {
changeLock(dbc, resource, CmsLockType.PUBLISH);
}
} else if (lock.getSystemLock().isPublish()) {
if (LOG.isWarnEnabled()) {
LOG.warn(Messages.get().getBundle().key(
Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
// remove files that are already waiting to be published
publishList.remove(resource);
continue;
} else {
// this is needed to fix TestPublishIsssues#testPublishScenarioE
changeLock(dbc, resource, CmsLockType.PUBLISH);
}
// now re-check the lock state
lock = m_lockManager.getLock(dbc, resource, false);
if (!lock.getSystemLock().isPublish()) {
if (report != null) {
report.println(
Messages.get().container(
Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
dbc.removeSiteRoot(resource.getRootPath())),
I_CmsReport.FORMAT_WARNING);
}
if (LOG.isWarnEnabled()) {
LOG.warn(Messages.get().getBundle().key(
Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
// remove files that could not be locked
publishList.remove(resource);
}
}
// enqueue the publish job
CmsException enqueueException = null;
try {
m_publishEngine.enqueuePublishJob(cms, publishList, report);
} catch (CmsException exc) {
enqueueException = exc;
}
// if an exception was raised, remove the publish locks
// and throw the exception again
if (enqueueException != null) {
itResources = publishList.getAllResources().iterator();
while (itResources.hasNext()) {
CmsResource resource = itResources.next();
CmsLock lock = m_lockManager.getLock(dbc, resource, false);
if (lock.getSystemLock().isPublish()
&& lock.getSystemLock().isOwnedInProjectBy(
cms.getRequestContext().getCurrentUser(),
cms.getRequestContext().getCurrentProject())) {
unlockResource(dbc, resource, true, true);
}
}
throw enqueueException;
}
}
/**
* Transfers the new URL name mappings (if any) for a given resource to the online project.<p>
*
* @param dbc the current database context
* @param res the resource whose new URL name mappings should be transferred to the online project
*
* @throws CmsDataAccessException if something goes wrong
*/
public void publishUrlNameMapping(CmsDbContext dbc, CmsResource res) throws CmsDataAccessException {
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
if (res.getState().isDeleted()) {
// remove both offline and online mappings
CmsUrlNameMappingFilter idFilter = CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId());
vfsDriver.deleteUrlNameMappingEntries(dbc, true, idFilter);
vfsDriver.deleteUrlNameMappingEntries(dbc, false, idFilter);
} else {
// copy the new entries to the online table
List<CmsUrlNameMappingEntry> entries = vfsDriver.readUrlNameMappingEntries(
dbc,
false,
CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterState(
CmsUrlNameMappingEntry.MAPPING_STATUS_NEW));
if (!entries.isEmpty()) {
long now = System.currentTimeMillis();
for (CmsUrlNameMappingEntry entry : entries) {
CmsUrlNameMappingFilter nameFilter = CmsUrlNameMappingFilter.ALL.filterName(entry.getName());
vfsDriver.deleteUrlNameMappingEntries(dbc, true, nameFilter);
vfsDriver.deleteUrlNameMappingEntries(dbc, false, nameFilter);
CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry(
entry.getName(),
entry.getStructureId(),
CmsUrlNameMappingEntry.MAPPING_STATUS_PUBLISHED,
now,
entry.getLocale());
vfsDriver.addUrlNameMappingEntry(dbc, true, newEntry);
vfsDriver.addUrlNameMappingEntry(dbc, false, newEntry);
}
}
}
}
/**
* Reads an access control entry from the cms.<p>
*
* The access control entries of a resource are readable by everyone.
*
* @param dbc the current database context
* @param resource the resource
* @param principal the id of a group or a user any other entity
* @return an access control entry that defines the permissions of the entity for the given resource
* @throws CmsException if something goes wrong
*/
public CmsAccessControlEntry readAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal)
throws CmsException {
return getUserDriver(dbc).readAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal);
}
/**
* Reads all versions of the given resource.<br>
*
* This method returns a list with the history of the given resource, i.e.
* the historical resource entries, independent of the project they were attached to.<br>
*
* The reading excludes the file content.<p>
*
* @param dbc the current database context
* @param resource the resource to read the history for
*
* @return a list of file headers, as <code>{@link I_CmsHistoryResource}</code> objects
*
* @throws CmsException if something goes wrong
*/
public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsResource resource)
throws CmsException {
// read the historical resources
List<I_CmsHistoryResource> versions = getHistoryDriver(dbc).readAllAvailableVersions(
dbc,
resource.getStructureId());
if ((versions.size() > OpenCms.getSystemInfo().getHistoryVersions())
&& (OpenCms.getSystemInfo().getHistoryVersions() > -1)) {
return versions.subList(0, OpenCms.getSystemInfo().getHistoryVersions());
}
return versions;
}
/**
* Reads all property definitions for the given mapping type.<p>
*
* @param dbc the current database context
*
* @return a list with the <code>{@link CmsPropertyDefinition}</code> objects (may be empty)
*
* @throws CmsException if something goes wrong
*/
public List<CmsPropertyDefinition> readAllPropertyDefinitions(CmsDbContext dbc) throws CmsException {
List<CmsPropertyDefinition> result = getVfsDriver(dbc).readPropertyDefinitions(
dbc,
dbc.currentProject().getUuid());
Collections.sort(result);
return result;
}
/**
* Returns all resources subscribed by the given user or group.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param principal the principal to read the subscribed resources
*
* @return all resources subscribed by the given user or group
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readAllSubscribedResources(CmsDbContext dbc, String poolName, CmsPrincipal principal)
throws CmsException {
List<CmsResource> result = getSubscriptionDriver().readAllSubscribedResources(dbc, poolName, principal);
result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT);
return result;
}
/**
* Selects the best url name for a given resource and locale.<p>
*
* @param dbc the database context
* @param id the resource's structure id
* @param locale the requested locale
* @param defaultLocales the default locales to use if the locale isn't available
*
* @return the URL name which was found
*
* @throws CmsDataAccessException if the database operation failed
*/
public String readBestUrlName(CmsDbContext dbc, CmsUUID id, Locale locale, List<Locale> defaultLocales)
throws CmsDataAccessException {
List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
dbc,
dbc.currentProject().isOnlineProject(),
CmsUrlNameMappingFilter.ALL.filterStructureId(id));
if (entries.isEmpty()) {
return null;
}
ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create();
for (CmsUrlNameMappingEntry entry : entries) {
entriesByLocale.put(entry.getLocale(), entry);
}
List<CmsUrlNameMappingEntry> lastEntries = new ArrayList<CmsUrlNameMappingEntry>();
Comparator<CmsUrlNameMappingEntry> dateChangedComparator = new UrlNameMappingComparator();
for (String localeKey : entriesByLocale.keySet()) {
// for each locale select the latest mapping entry
CmsUrlNameMappingEntry latestEntryForLocale = Collections.max(
entriesByLocale.get(localeKey),
dateChangedComparator);
lastEntries.add(latestEntryForLocale);
}
CmsLocaleManager localeManager = OpenCms.getLocaleManager();
List<Locale> availableLocales = new ArrayList<Locale>();
for (CmsUrlNameMappingEntry entry : lastEntries) {
availableLocales.add(CmsLocaleManager.getLocale(entry.getLocale()));
}
Locale bestLocale = localeManager.getBestMatchingLocale(locale, defaultLocales, availableLocales);
String bestLocaleStr = bestLocale.getLanguage();
for (CmsUrlNameMappingEntry entry : lastEntries) {
if (entry.getLocale().equals(bestLocaleStr)) {
return entry.getName();
}
}
return null;
}
/**
* Returns the child resources of a resource, that is the resources
* contained in a folder.<p>
*
* With the parameters <code>getFolders</code> and <code>getFiles</code>
* you can control what type of resources you want in the result list:
* files, folders, or both.<p>
*
* This method is mainly used by the workplace explorer.<p>
*
* @param dbc the current database context
* @param resource the resource to return the child resources for
* @param filter the resource filter to use
* @param getFolders if true the child folders are included in the result
* @param getFiles if true the child files are included in the result
* @param checkPermissions if the resources should be filtered with the current user permissions
*
* @return a list of all child resources
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readChildResources(
CmsDbContext dbc,
CmsResource resource,
CmsResourceFilter filter,
boolean getFolders,
boolean getFiles,
boolean checkPermissions) throws CmsException {
String cacheKey = null;
List<CmsResource> resourceList = null;
if (m_monitor.isEnabled(CmsMemoryMonitor.CacheType.RESOURCE_LIST)) { // check this here to skip the complex cache key generation
String time = "";
if (checkPermissions) {
// ensure correct caching if site time offset is set
if ((dbc.getRequestContext() != null)
&& (OpenCms.getSiteManager().getSiteForSiteRoot(dbc.getRequestContext().getSiteRoot()) != null)) {
time += OpenCms.getSiteManager().getSiteForSiteRoot(dbc.getRequestContext().getSiteRoot()).getSiteMatcher().getTimeOffset();
}
}
// try to get the sub resources from the cache
cacheKey = getCacheKey(
new String[] {
dbc.currentUser().getName(),
getFolders
? (getFiles ? CmsCacheKey.CACHE_KEY_SUBALL : CmsCacheKey.CACHE_KEY_SUBFOLDERS)
: CmsCacheKey.CACHE_KEY_SUBFILES,
checkPermissions ? "+" + time : "-",
filter.getCacheId(),
resource.getRootPath()},
dbc);
resourceList = m_monitor.getCachedResourceList(cacheKey);
}
if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
// read the result form the database
resourceList = getVfsDriver(dbc).readChildResources(
dbc,
dbc.currentProject(),
resource,
getFolders,
getFiles);
if (checkPermissions) {
// apply the permission filter
resourceList = filterPermissions(dbc, resourceList, filter);
}
// cache the sub resources
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheResourceList(cacheKey, resourceList);
}
}
// we must always apply the result filter and update the context dates
return updateContextDates(dbc, resourceList, filter);
}
/**
* Returns the default file for the given folder.<p>
*
* If the given resource is a file, then this file is returned.<p>
*
* Otherwise, in case of a folder:<br>
* <ol>
* <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and
* <li>if still no file could be found, the configured default files in the
* <code>opencms-vfs.xml</code> configuration are iterated until a match is
* found, and
* <li>if still no file could be found, <code>null</code> is retuned
* </ol>
*
* @param dbc the database context
* @param resource the folder to get the default file for
* @param resourceFilter the resource filter
*
* @return the default file for the given folder
*/
public CmsResource readDefaultFile(CmsDbContext dbc, CmsResource resource, CmsResourceFilter resourceFilter) {
// resource exists, lets check if we have a file or a folder
if (resource.isFolder()) {
// the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder
try {
String defaultFileName = readPropertyObject(
dbc,
resource,
CmsPropertyDefinition.PROPERTY_DEFAULT_FILE,
false).getValue();
// check if the default file property does not match the navigation level folder marker value
if (defaultFileName != null) {
// property was set, so look up this file first
String folderName = CmsResource.getFolderPath(resource.getRootPath());
resource = readResource(dbc, folderName + defaultFileName, resourceFilter.addRequireFile());
}
} catch (CmsException e) {
// ignore all other exceptions and continue the lookup process
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
if (resource.isFolder()) {
String folderName = CmsResource.getFolderPath(resource.getRootPath());
// resource is (still) a folder, check default files specified in configuration
Iterator<String> it = OpenCms.getDefaultFiles().iterator();
while (it.hasNext()) {
String tmpResourceName = folderName + it.next();
try {
resource = readResource(dbc, tmpResourceName, resourceFilter.addRequireFile());
// no exception? So we have found the default file
// stop looking for default files
break;
} catch (CmsException e) {
// ignore all other exceptions and continue the lookup process
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
}
}
if (resource.isFolder()) {
// we only want files as a result for further processing
resource = null;
}
return resource;
}
/**
* Reads all deleted (historical) resources below the given path,
* including the full tree below the path, if required.<p>
*
* @param dbc the current db context
* @param resource the parent resource to read the resources from
* @param readTree <code>true</code> to read all subresources
* @param isVfsManager <code>true</code> if the current user has the vfs manager role
*
* @return a list of <code>{@link I_CmsHistoryResource}</code> objects
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#readResource(CmsUUID, int)
* @see CmsObject#readResources(String, CmsResourceFilter, boolean)
* @see CmsObject#readDeletedResources(String, boolean)
*/
public List<I_CmsHistoryResource> readDeletedResources(
CmsDbContext dbc,
CmsResource resource,
boolean readTree,
boolean isVfsManager) throws CmsException {
Set<I_CmsHistoryResource> result = new HashSet<I_CmsHistoryResource>();
List<I_CmsHistoryResource> deletedResources;
dbc.getRequestContext().setAttribute("ATTR_RESOURCE_NAME", resource.getRootPath());
try {
deletedResources = getHistoryDriver(dbc).readDeletedResources(
dbc,
resource.getStructureId(),
isVfsManager ? null : dbc.currentUser().getId());
} finally {
dbc.getRequestContext().removeAttribute("ATTR_RESOURCE_NAME");
}
result.addAll(deletedResources);
Set<I_CmsHistoryResource> newResult = new HashSet<I_CmsHistoryResource>(result.size());
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
Iterator<I_CmsHistoryResource> it = result.iterator();
while (it.hasNext()) {
I_CmsHistoryResource histRes = it.next();
// adjust the paths
try {
if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(), histRes.getStructureId())) {
newResult.add(histRes);
continue;
}
// adjust the path in case of deleted files
String resourcePath = histRes.getRootPath();
String resName = CmsResource.getName(resourcePath);
String path = CmsResource.getParentFolder(resourcePath);
CmsUUID parentId = histRes.getParentId();
try {
// first look for the path through the parent id
path = readResource(dbc, parentId, CmsResourceFilter.IGNORE_EXPIRATION).getRootPath();
} catch (CmsDataAccessException e) {
// if the resource with the parent id is not found, try to get a new parent id with the path
try {
parentId = readResource(dbc, path, CmsResourceFilter.IGNORE_EXPIRATION).getStructureId();
} catch (CmsDataAccessException e1) {
// ignore, the parent folder has been completely deleted
}
}
resourcePath = path + resName;
boolean isFolder = resourcePath.endsWith("/");
if (isFolder) {
newResult.add(new CmsHistoryFolder(
histRes.getPublishTag(),
histRes.getStructureId(),
histRes.getResourceId(),
resourcePath,
histRes.getTypeId(),
histRes.getFlags(),
histRes.getProjectLastModified(),
histRes.getState(),
histRes.getDateCreated(),
histRes.getUserCreated(),
histRes.getDateLastModified(),
histRes.getUserLastModified(),
histRes.getDateReleased(),
histRes.getDateExpired(),
histRes.getVersion(),
parentId,
histRes.getResourceVersion(),
histRes.getStructureVersion()));
} else {
newResult.add(new CmsHistoryFile(
histRes.getPublishTag(),
histRes.getStructureId(),
histRes.getResourceId(),
resourcePath,
histRes.getTypeId(),
histRes.getFlags(),
histRes.getProjectLastModified(),
histRes.getState(),
histRes.getDateCreated(),
histRes.getUserCreated(),
histRes.getDateLastModified(),
histRes.getUserLastModified(),
histRes.getDateReleased(),
histRes.getDateExpired(),
histRes.getLength(),
histRes.getDateContent(),
histRes.getVersion(),
parentId,
null,
histRes.getResourceVersion(),
histRes.getStructureVersion()));
}
} catch (CmsDataAccessException e) {
// should never happen
if (LOG.isErrorEnabled()) {
LOG.error(e.getLocalizedMessage(), e);
}
}
}
if (readTree) {
Iterator<I_CmsHistoryResource> itDeleted = deletedResources.iterator();
while (itDeleted.hasNext()) {
I_CmsHistoryResource delResource = itDeleted.next();
if (delResource.isFolder()) {
newResult.addAll(readDeletedResources(dbc, (CmsFolder)delResource, readTree, isVfsManager));
}
}
try {
readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL);
// resource exists, so recurse
Iterator<CmsResource> itResources = readResources(
dbc,
resource,
CmsResourceFilter.ALL.addRequireFolder(),
readTree).iterator();
while (itResources.hasNext()) {
CmsResource subResource = itResources.next();
if (subResource.isFolder()) {
newResult.addAll(readDeletedResources(dbc, subResource, readTree, isVfsManager));
}
}
} catch (Exception e) {
// resource does not exists
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
List<I_CmsHistoryResource> finalRes = new ArrayList<I_CmsHistoryResource>(newResult);
Collections.sort(finalRes, I_CmsResource.COMPARE_ROOT_PATH);
return finalRes;
}
/**
* Reads a file resource (including it's binary content) from the VFS,
* using the specified resource filter.<p>
*
* In case you do not need the file content,
* use <code>{@link #readResource(CmsDbContext, String, CmsResourceFilter)}</code> instead.<p>
*
* The specified filter controls what kind of resources should be "found"
* during the read operation. This will depend on the application. For example,
* using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
* "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
* will ignore the date release / date expired information of the resource.<p>
*
* @param dbc the current database context
* @param resource the base file resource (without content)
* @return the file read from the VFS
* @throws CmsException if operation was not successful
*/
public CmsFile readFile(CmsDbContext dbc, CmsResource resource) throws CmsException {
if (resource.isFolder()) {
throw new CmsVfsResourceNotFoundException(Messages.get().container(
Messages.ERR_ACCESS_FOLDER_AS_FILE_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
CmsUUID projectId = dbc.currentProject().getUuid();
CmsFile file = null;
if (resource instanceof I_CmsHistoryResource) {
file = new CmsHistoryFile((I_CmsHistoryResource)resource);
file.setContents(getHistoryDriver(dbc).readContent(
dbc,
resource.getResourceId(),
((I_CmsHistoryResource)resource).getPublishTag()));
} else {
file = new CmsFile(resource);
file.setContents(getVfsDriver(dbc).readContent(dbc, projectId, resource.getResourceId()));
}
return file;
}
/**
* Reads a folder from the VFS,
* using the specified resource filter.<p>
*
* @param dbc the current database context
* @param resourcename the name of the folder to read (full path)
* @param filter the resource filter to use while reading
*
* @return the folder that was read
*
* @throws CmsDataAccessException if something goes wrong
*
* @see #readResource(CmsDbContext, String, CmsResourceFilter)
* @see CmsObject#readFolder(String)
* @see CmsObject#readFolder(String, CmsResourceFilter)
*/
public CmsFolder readFolder(CmsDbContext dbc, String resourcename, CmsResourceFilter filter)
throws CmsDataAccessException {
CmsResource resource = readResource(dbc, resourcename, filter);
return convertResourceToFolder(resource);
}
/**
* Reads the group of a project.<p>
*
* @param dbc the current database context
* @param project the project to read from
*
* @return the group of a resource
*/
public CmsGroup readGroup(CmsDbContext dbc, CmsProject project) {
try {
return readGroup(dbc, project.getGroupId());
} catch (CmsException exc) {
return new CmsGroup(
CmsUUID.getNullUUID(),
CmsUUID.getNullUUID(),
project.getGroupId() + "",
"deleted group",
0);
}
}
/**
* Reads a group based on its id.<p>
*
* @param dbc the current database context
* @param groupId the id of the group that is to be read
*
* @return the requested group
*
* @throws CmsException if operation was not successful
*/
public CmsGroup readGroup(CmsDbContext dbc, CmsUUID groupId) throws CmsException {
CmsGroup group = null;
// try to read group from cache
group = m_monitor.getCachedGroup(groupId.toString());
if (group == null) {
group = getUserDriver(dbc).readGroup(dbc, groupId);
m_monitor.cacheGroup(group);
}
return group;
}
/**
* Reads a group based on its name.<p>
*
* @param dbc the current database context
* @param groupname the name of the group that is to be read
*
* @return the requested group
*
* @throws CmsDataAccessException if operation was not successful
*/
public CmsGroup readGroup(CmsDbContext dbc, String groupname) throws CmsDataAccessException {
CmsGroup group = null;
// try to read group from cache
group = m_monitor.getCachedGroup(groupname);
if (group == null) {
group = getUserDriver(dbc).readGroup(dbc, groupname);
m_monitor.cacheGroup(group);
}
return group;
}
/**
* Reads a principal (an user or group) from the historical archive based on its ID.<p>
*
* @param dbc the current database context
* @param principalId the id of the principal to read
*
* @return the historical principal entry with the given id
*
* @throws CmsException if something goes wrong, ie. {@link CmsDbEntryNotFoundException}
*
* @see CmsObject#readUser(CmsUUID)
* @see CmsObject#readGroup(CmsUUID)
* @see CmsObject#readHistoryPrincipal(CmsUUID)
*/
public CmsHistoryPrincipal readHistoricalPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsException {
return getHistoryDriver(dbc).readPrincipal(dbc, principalId);
}
/**
* Returns the latest historical project entry with the given id.<p>
*
* @param dbc the current database context
* @param projectId the project id
*
* @return the requested historical project entry
*
* @throws CmsException if something goes wrong
*/
public CmsHistoryProject readHistoryProject(CmsDbContext dbc, CmsUUID projectId) throws CmsException {
return getHistoryDriver(dbc).readProject(dbc, projectId);
}
/**
* Returns a historical project entry.<p>
*
* @param dbc the current database context
* @param publishTag the publish tag of the project
*
* @return the requested historical project entry
*
* @throws CmsException if something goes wrong
*/
public CmsHistoryProject readHistoryProject(CmsDbContext dbc, int publishTag) throws CmsException {
return getHistoryDriver(dbc).readProject(dbc, publishTag);
}
/**
* Reads the list of all <code>{@link CmsProperty}</code> objects that belongs to the given historical resource.<p>
*
* @param dbc the current database context
* @param historyResource the historical resource to read the properties for
*
* @return the list of <code>{@link CmsProperty}</code> objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsProperty> readHistoryPropertyObjects(CmsDbContext dbc, I_CmsHistoryResource historyResource)
throws CmsException {
return getHistoryDriver(dbc).readProperties(dbc, historyResource);
}
/**
* Reads the structure id which is mapped to a given URL name.<p>
*
* @param dbc the current database context
* @param name the name for which the mapped structure id should be looked up
*
* @return the structure id which is mapped to the given name, or null if there is no such id
*
* @throws CmsDataAccessException if something goes wrong
*/
public CmsUUID readIdForUrlName(CmsDbContext dbc, String name) throws CmsDataAccessException {
List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
dbc,
dbc.currentProject().isOnlineProject(),
CmsUrlNameMappingFilter.ALL.filterName(name));
if (entries.isEmpty()) {
return null;
}
return entries.get(0).getStructureId();
}
/**
* Reads the locks that were saved to the database in the previous run of OpenCms.<p>
*
* @param dbc the current database context
*
* @throws CmsException if something goes wrong
*/
public void readLocks(CmsDbContext dbc) throws CmsException {
m_lockManager.readLocks(dbc);
}
/**
* Reads the manager group of a project.<p>
*
* @param dbc the current database context
* @param project the project to read from
*
* @return the group of a resource
*/
public CmsGroup readManagerGroup(CmsDbContext dbc, CmsProject project) {
try {
return readGroup(dbc, project.getManagerGroupId());
} catch (CmsException exc) {
// the group does not exist any more - return a dummy-group
return new CmsGroup(
CmsUUID.getNullUUID(),
CmsUUID.getNullUUID(),
project.getManagerGroupId() + "",
"deleted group",
0);
}
}
/**
* Reads the URL name which has been most recently mapped to the given structure id, or null
* if no URL name is mapped to the id.<p>
*
* @param dbc the current database context
* @param id a structure id
* @return the name which has been most recently mapped to the given structure id
*
* @throws CmsDataAccessException if something goes wrong
*/
public String readNewestUrlNameForId(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException {
List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
dbc,
dbc.currentProject().isOnlineProject(),
CmsUrlNameMappingFilter.ALL.filterStructureId(id));
if (entries.isEmpty()) {
return null;
}
Collections.sort(entries, new UrlNameMappingComparator());
CmsUrlNameMappingEntry lastEntry = entries.get(entries.size() - 1);
return lastEntry.getName();
}
/**
* Reads an organizational Unit based on its fully qualified name.<p>
*
* @param dbc the current db context
* @param ouFqn the fully qualified name of the organizational Unit to be read
*
* @return the organizational Unit that with the provided fully qualified name
*
* @throws CmsException if something goes wrong
*/
public CmsOrganizationalUnit readOrganizationalUnit(CmsDbContext dbc, String ouFqn) throws CmsException {
CmsOrganizationalUnit organizationalUnit = null;
// try to read organizational unit from cache
organizationalUnit = m_monitor.getCachedOrgUnit(ouFqn);
if (organizationalUnit == null) {
organizationalUnit = getUserDriver(dbc).readOrganizationalUnit(dbc, ouFqn);
m_monitor.cacheOrgUnit(organizationalUnit);
}
return organizationalUnit;
}
/**
* Reads the owner of a project.<p>
*
* @param dbc the current database context
* @param project the project to get the owner from
*
* @return the owner of a resource
* @throws CmsException if something goes wrong
*/
public CmsUser readOwner(CmsDbContext dbc, CmsProject project) throws CmsException {
return readUser(dbc, project.getOwnerId());
}
/**
* Reads the parent folder to a given structure id.<p>
*
* @param dbc the current database context
* @param structureId the structure id of the child
*
* @return the parent folder resource
*
* @throws CmsDataAccessException if something goes wrong
*/
public CmsResource readParentFolder(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException {
return getVfsDriver(dbc).readParentFolder(dbc, dbc.currentProject().getUuid(), structureId);
}
/**
* Builds a list of resources for a given path.<p>
*
* @param dbc the current database context
* @param path the requested path
* @param filter a filter object (only "includeDeleted" information is used!)
*
* @return list of <code>{@link CmsResource}</code>s
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readPath(CmsDbContext dbc, String path, CmsResourceFilter filter) throws CmsException {
// splits the path into folder and filename tokens
List<String> tokens = CmsStringUtil.splitAsList(path, '/');
// the root folder is no token in the path but a resource which has to be added to the path
int count = tokens.size() + 1;
// holds the CmsResource instances in the path
List<CmsResource> pathList = new ArrayList<CmsResource>(count);
// true if the path doesn't end with a folder
boolean lastResourceIsFile = false;
// number of folders in the path
int folderCount = count;
if (!path.endsWith("/")) {
folderCount--;
lastResourceIsFile = true;
}
// read the root folder, because it's ID is required to read any sub-resources
String currentResourceName = "/";
StringBuffer currentPath = new StringBuffer(64);
currentPath.append('/');
String cp = currentPath.toString();
CmsUUID projectId = getProjectIdForContext(dbc);
// key to cache the resources
String cacheKey = getCacheKey(null, false, projectId, cp);
// the current resource
CmsResource currentResource = m_monitor.getCachedResource(cacheKey);
if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) {
currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp);
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheResource(cacheKey, currentResource);
}
}
pathList.add(0, currentResource);
if (count == 1) {
// the root folder was requested- no further operations required
return pathList;
}
Iterator<String> it = tokens.iterator();
currentResourceName = it.next();
// read the folder resources in the path /a/b/c/
int i = 0;
for (i = 1; i < folderCount; i++) {
currentPath.append(currentResourceName);
currentPath.append('/');
// read the folder
cp = currentPath.toString();
cacheKey = getCacheKey(null, false, projectId, cp);
currentResource = m_monitor.getCachedResource(cacheKey);
if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) {
currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp);
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheResource(cacheKey, currentResource);
}
}
pathList.add(i, currentResource);
if (i < (folderCount - 1)) {
currentResourceName = it.next();
}
}
// read the (optional) last file resource in the path /x.html
if (lastResourceIsFile) {
if (it.hasNext()) {
// this will only be false if a resource in the
// top level root folder (e.g. "/index.html") was requested
currentResourceName = it.next();
}
currentPath.append(currentResourceName);
// read the file
cp = currentPath.toString();
cacheKey = getCacheKey(null, false, projectId, cp);
currentResource = m_monitor.getCachedResource(cacheKey);
if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) {
currentResource = getVfsDriver(dbc).readResource(dbc, projectId, cp, filter.includeDeleted());
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheResource(cacheKey, currentResource);
}
}
pathList.add(i, currentResource);
}
return pathList;
}
/**
* Reads a project given the projects id.<p>
*
* @param dbc the current database context
* @param id the id of the project
*
* @return the project read
*
* @throws CmsDataAccessException if something goes wrong
*/
public CmsProject readProject(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException {
CmsProject project = null;
project = m_monitor.getCachedProject(id.toString());
if (project == null) {
project = getProjectDriver(dbc).readProject(dbc, id);
m_monitor.cacheProject(project);
}
return project;
}
/**
* Reads a project.<p>
*
* Important: Since a project name can be used multiple times, this is NOT the most efficient
* way to read the project. This is only a convenience for front end developing.
* Reading a project by name will return the first project with that name.
* All core classes must use the id version {@link #readProject(CmsDbContext, CmsUUID)} to ensure the right project is read.<p>
*
* @param dbc the current database context
* @param name the name of the project
*
* @return the project read
*
* @throws CmsException if something goes wrong
*/
public CmsProject readProject(CmsDbContext dbc, String name) throws CmsException {
CmsProject project = null;
project = m_monitor.getCachedProject(name);
if (project == null) {
project = getProjectDriver(dbc).readProject(dbc, name);
m_monitor.cacheProject(project);
}
return project;
}
/**
* Returns the list of all resource names that define the "view" of the given project.<p>
*
* @param dbc the current database context
* @param project the project to get the project resources for
*
* @return the list of all resources, as <code>{@link String}</code> objects
* that define the "view" of the given project.
*
* @throws CmsException if something goes wrong
*/
public List<String> readProjectResources(CmsDbContext dbc, CmsProject project) throws CmsException {
return getProjectDriver(dbc).readProjectResources(dbc, project);
}
/**
* Reads all resources of a project that match a given state from the VFS.<p>
*
* Possible values for the <code>state</code> parameter are:<br>
* <ul>
* <li><code>{@link CmsResource#STATE_CHANGED}</code>: Read all "changed" resources in the project</li>
* <li><code>{@link CmsResource#STATE_NEW}</code>: Read all "new" resources in the project</li>
* <li><code>{@link CmsResource#STATE_DELETED}</code>: Read all "deleted" resources in the project</li>
* <li><code>{@link CmsResource#STATE_KEEP}</code>: Read all resources either "changed", "new" or "deleted" in the project</li>
* </ul><p>
*
* @param dbc the current database context
* @param projectId the id of the project to read the file resources for
* @param state the resource state to match
*
* @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#readProjectView(CmsUUID, CmsResourceState)
*/
public List<CmsResource> readProjectView(CmsDbContext dbc, CmsUUID projectId, CmsResourceState state)
throws CmsException {
List<CmsResource> resources;
if (state.isNew() || state.isChanged() || state.isDeleted()) {
// get all resources form the database that match the selected state
resources = getVfsDriver(dbc).readResources(dbc, projectId, state, CmsDriverManager.READMODE_MATCHSTATE);
} else {
// get all resources form the database that are somehow changed (i.e. not unchanged)
resources = getVfsDriver(dbc).readResources(
dbc,
projectId,
CmsResource.STATE_UNCHANGED,
CmsDriverManager.READMODE_UNMATCHSTATE);
}
// filter the permissions
List<CmsResource> result = filterPermissions(dbc, resources, CmsResourceFilter.ALL);
// sort the result
Collections.sort(result);
// set the full resource names
return updateContextDates(dbc, result);
}
/**
* Reads a property definition.<p>
*
* If no property definition with the given name is found,
* <code>null</code> is returned.<p>
*
* @param dbc the current database context
* @param name the name of the property definition to read
*
* @return the property definition that was read
*
* @throws CmsException a CmsDbEntryNotFoundException is thrown if the property definition does not exist
*/
public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name) throws CmsException {
return getVfsDriver(dbc).readPropertyDefinition(dbc, name, dbc.currentProject().getUuid());
}
/**
* Reads a property object from a resource specified by a property name.<p>
*
* Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p>
*
* @param dbc the current database context
* @param resource the resource where the property is read from
* @param key the property key name
* @param search if <code>true</code>, the property is searched on all parent folders of the resource.
* if it's not found attached directly to the resource.
*
* @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found
*
* @throws CmsException if something goes wrong
*/
public CmsProperty readPropertyObject(CmsDbContext dbc, CmsResource resource, String key, boolean search)
throws CmsException {
// use the list reading method to obtain all properties for the resource
List<CmsProperty> properties = readPropertyObjects(dbc, resource, search);
// create a lookup propertry object and look this up in the result map
int i = properties.indexOf(new CmsProperty(key, null, null));
CmsProperty result;
if (i >= 0) {
// property has been found in the map
result = properties.get(i);
} else {
// property is not defined, return NULL property
result = CmsProperty.getNullProperty();
}
// ensure the result value is not frozen
return result.cloneAsProperty();
}
/**
* Reads all property objects mapped to a specified resource from the database.<p>
*
* All properties in the result List will be in frozen (read only) state, so you can't change the values.<p>
*
* Returns an empty list if no properties are found at all.<p>
*
* @param dbc the current database context
* @param resource the resource where the properties are read from
* @param search true, if the properties should be searched on all parent folders if not found on the resource
*
* @return a list of CmsProperty objects containing the structure and/or resource value
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#readPropertyObjects(String, boolean)
*/
public List<CmsProperty> readPropertyObjects(CmsDbContext dbc, CmsResource resource, boolean search)
throws CmsException {
// check if we have the result already cached
CmsUUID projectId = getProjectIdForContext(dbc);
String cacheKey = getCacheKey(CACHE_ALL_PROPERTIES, search, projectId, resource.getRootPath());
List<CmsProperty> properties = m_monitor.getCachedPropertyList(cacheKey);
if ((properties == null) || !dbc.getProjectId().isNullUUID()) {
// result not cached, let's look it up in the DB
if (search) {
boolean cont;
properties = new ArrayList<CmsProperty>();
List<CmsProperty> parentProperties = null;
do {
try {
parentProperties = readPropertyObjects(dbc, resource, false);
// make sure properties from lower folders "overwrite" properties from upper folders
parentProperties.removeAll(properties);
parentProperties.addAll(properties);
properties.clear();
properties.addAll(parentProperties);
cont = resource.getRootPath().length() > 1;
} catch (CmsSecurityException se) {
// a security exception (probably no read permission) we return the current result
cont = false;
}
if (cont) {
// no permission check on parent folder is required since we must have "read"
// permissions to read the child resource anyway
resource = readResource(
dbc,
CmsResource.getParentFolder(resource.getRootPath()),
CmsResourceFilter.ALL);
}
} while (cont);
} else {
properties = getVfsDriver(dbc).readPropertyObjects(dbc, dbc.currentProject(), resource);
// for (CmsProperty prop : properties) {
// prop.setOrigin(resource.getRootPath());
// }
}
// set all properties in the result list as frozen
CmsProperty.setFrozen(properties);
if (dbc.getProjectId().isNullUUID()) {
// store the result in the cache if needed
m_monitor.cachePropertyList(cacheKey, properties);
}
}
return new ArrayList<CmsProperty>(properties);
}
/**
* Reads the resources that were published in a publish task for a given publish history ID.<p>
*
* @param dbc the current database context
* @param publishHistoryId unique int ID to identify each publish task in the publish history
*
* @return a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects
*
* @throws CmsException if something goes wrong
*/
public List<CmsPublishedResource> readPublishedResources(CmsDbContext dbc, CmsUUID publishHistoryId)
throws CmsException {
String cacheKey = publishHistoryId.toString();
List<CmsPublishedResource> resourceList = m_monitor.getCachedPublishedResources(cacheKey);
if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
resourceList = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId);
// store the result in the cache
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cachePublishedResources(cacheKey, resourceList);
}
}
return resourceList;
}
/**
* Reads a single publish job identified by its publish history id.<p>
*
* @param dbc the current database context
* @param publishHistoryId unique id to identify the publish job in the publish history
* @return an object of type <code>{@link CmsPublishJobInfoBean}</code>
*
* @throws CmsException if something goes wrong
*/
public CmsPublishJobInfoBean readPublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
return getProjectDriver(dbc).readPublishJob(dbc, publishHistoryId);
}
/**
* Reads all available publish jobs.<p>
*
* @param dbc the current database context
* @param startTime the start of the time range for finish time
* @param endTime the end of the time range for finish time
* @return a list of objects of type <code>{@link CmsPublishJobInfoBean}</code>
*
* @throws CmsException if something goes wrong
*/
public List<CmsPublishJobInfoBean> readPublishJobs(CmsDbContext dbc, long startTime, long endTime)
throws CmsException {
return getProjectDriver(dbc).readPublishJobs(dbc, startTime, endTime);
}
/**
* Reads the publish list assigned to a publish job.<p>
*
* @param dbc the current database context
* @param publishHistoryId the history id identifying the publish job
* @return the assigned publish list
* @throws CmsException if something goes wrong
*/
public CmsPublishList readPublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
return getProjectDriver(dbc).readPublishList(dbc, publishHistoryId);
}
/**
* Reads the publish report assigned to a publish job.<p>
*
* @param dbc the current database context
* @param publishHistoryId the history id identifying the publish job
* @return the content of the assigned publish report
* @throws CmsException if something goes wrong
*/
public byte[] readPublishReportContents(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
return getProjectDriver(dbc).readPublishReportContents(dbc, publishHistoryId);
}
/**
* Reads an historical resource entry for the given resource and with the given version number.<p>
*
* @param dbc the current db context
* @param resource the resource to be read
* @param version the version number to retrieve
*
* @return the resource that was read
*
* @throws CmsException if the resource could not be read for any reason
*
* @see CmsObject#restoreResourceVersion(CmsUUID, int)
* @see CmsObject#readResource(CmsUUID, int)
*/
public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException {
Iterator<I_CmsHistoryResource> itVersions = getHistoryDriver(dbc).readAllAvailableVersions(
dbc,
resource.getStructureId()).iterator();
while (itVersions.hasNext()) {
I_CmsHistoryResource histRes = itVersions.next();
if (histRes.getVersion() == version) {
return histRes;
}
}
throw new CmsVfsResourceNotFoundException(org.opencms.db.generic.Messages.get().container(
org.opencms.db.generic.Messages.ERR_HISTORY_FILE_NOT_FOUND_1,
resource.getStructureId()));
}
/**
* Reads a resource from the VFS, using the specified resource filter.<p>
*
* @param dbc the current database context
* @param structureID the structure id of the resource to read
* @param filter the resource filter to use while reading
*
* @return the resource that was read
*
* @throws CmsDataAccessException if something goes wrong
*
* @see CmsObject#readResource(CmsUUID, CmsResourceFilter)
* @see CmsObject#readResource(CmsUUID)
*/
public CmsResource readResource(CmsDbContext dbc, CmsUUID structureID, CmsResourceFilter filter)
throws CmsDataAccessException {
CmsUUID projectId = getProjectIdForContext(dbc);
// please note: the filter will be applied in the security manager later
CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, structureID, filter.includeDeleted());
// context dates need to be updated
updateContextDates(dbc, resource);
// return the resource
return resource;
}
/**
* Reads a resource from the VFS, using the specified resource filter.<p>
*
* @param dbc the current database context
* @param resourcePath the name of the resource to read (full path)
* @param filter the resource filter to use while reading
*
* @return the resource that was read
*
* @throws CmsDataAccessException if something goes wrong
*
* @see CmsObject#readResource(String, CmsResourceFilter)
* @see CmsObject#readResource(String)
* @see CmsObject#readFile(CmsResource)
*/
public CmsResource readResource(CmsDbContext dbc, String resourcePath, CmsResourceFilter filter)
throws CmsDataAccessException {
CmsUUID projectId = getProjectIdForContext(dbc);
// please note: the filter will be applied in the security manager later
CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, resourcePath, filter.includeDeleted());
// context dates need to be updated
updateContextDates(dbc, resource);
// return the resource
return resource;
}
/**
* Reads all resources below the given path matching the filter criteria,
* including the full tree below the path only in case the <code>readTree</code>
* parameter is <code>true</code>.<p>
*
* @param dbc the current database context
* @param parent the parent path to read the resources from
* @param filter the filter
* @param readTree <code>true</code> to read all subresources
*
* @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria
*
* @throws CmsDataAccessException if the bare reading of the resources fails
* @throws CmsException if security and permission checks for the resources read fail
*/
public List<CmsResource> readResources(
CmsDbContext dbc,
CmsResource parent,
CmsResourceFilter filter,
boolean readTree) throws CmsException, CmsDataAccessException {
// try to get the sub resources from the cache
String cacheKey = getCacheKey(new String[] {
dbc.currentUser().getName(),
filter.getCacheId(),
readTree ? "+" : "-",
parent.getRootPath()}, dbc);
List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey);
if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
// read the result from the database
resourceList = getVfsDriver(dbc).readResourceTree(
dbc,
dbc.currentProject().getUuid(),
(readTree ? parent.getRootPath() : parent.getStructureId().toString()),
filter.getType(),
filter.getState(),
filter.getModifiedAfter(),
filter.getModifiedBefore(),
filter.getReleaseAfter(),
filter.getReleaseBefore(),
filter.getExpireAfter(),
filter.getExpireBefore(),
(readTree ? CmsDriverManager.READMODE_INCLUDE_TREE : CmsDriverManager.READMODE_EXCLUDE_TREE)
| (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0)
| (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0)
| ((filter.getOnlyFolders() != null) ? (filter.getOnlyFolders().booleanValue()
? CmsDriverManager.READMODE_ONLY_FOLDERS
: CmsDriverManager.READMODE_ONLY_FILES) : 0));
// HACK: do not take care of permissions if reading organizational units
if (!parent.getRootPath().startsWith("/system/orgunits/")) {
// apply permission filter
resourceList = filterPermissions(dbc, resourceList, filter);
}
// store the result in the resourceList cache
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheResourceList(cacheKey, resourceList);
}
}
// we must always apply the result filter and update the context dates
return updateContextDates(dbc, resourceList, filter);
}
/**
* Returns the resources that were visited by a user set in the filter.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param filter the filter that is used to get the visited resources
*
* @return the resources that were visited by a user set in the filter
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readResourcesVisitedBy(CmsDbContext dbc, String poolName, CmsVisitedByFilter filter)
throws CmsException {
List<CmsResource> result = getSubscriptionDriver().readResourcesVisitedBy(dbc, poolName, filter);
result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT);
return result;
}
/**
* Reads all resources that have a value (containing the given value string) set
* for the specified property (definition) in the given path.<p>
*
* Both individual and shared properties of a resource are checked.<p>
*
* If the <code>value</code> parameter is <code>null</code>, all resources having the
* given property set are returned.<p>
*
* @param dbc the current database context
* @param folder the folder to get the resources with the property from
* @param propertyDefinition the name of the property (definition) to check for
* @param value the string to search in the value of the property
* @param filter the resource filter to apply to the result set
*
* @return a list of all <code>{@link CmsResource}</code> objects
* that have a value set for the specified property.
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readResourcesWithProperty(
CmsDbContext dbc,
CmsResource folder,
String propertyDefinition,
String value,
CmsResourceFilter filter) throws CmsException {
String cacheKey;
if (value == null) {
cacheKey = getCacheKey(new String[] {
dbc.currentUser().getName(),
folder.getRootPath(),
propertyDefinition,
filter.getCacheId()}, dbc);
} else {
cacheKey = getCacheKey(new String[] {
dbc.currentUser().getName(),
folder.getRootPath(),
propertyDefinition,
value,
filter.getCacheId()}, dbc);
}
List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey);
if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
// first read the property definition
CmsPropertyDefinition propDef = readPropertyDefinition(dbc, propertyDefinition);
// now read the list of resources that have a value set for the property definition
resourceList = getVfsDriver(dbc).readResourcesWithProperty(
dbc,
dbc.currentProject().getUuid(),
propDef.getId(),
folder.getRootPath(),
value);
// apply permission filter
resourceList = filterPermissions(dbc, resourceList, filter);
// store the result in the resourceList cache
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheResourceList(cacheKey, resourceList);
}
}
// we must always apply the result filter and update the context dates
return updateContextDates(dbc, resourceList, filter);
}
/**
* Returns the set of users that are responsible for a specific resource.<p>
*
* @param dbc the current database context
* @param resource the resource to get the responsible users from
*
* @return the set of users that are responsible for a specific resource
*
* @throws CmsException if something goes wrong
*/
public Set<I_CmsPrincipal> readResponsiblePrincipals(CmsDbContext dbc, CmsResource resource) throws CmsException {
Set<I_CmsPrincipal> result = new HashSet<I_CmsPrincipal>();
Iterator<CmsAccessControlEntry> aces = getAccessControlEntries(dbc, resource, true).iterator();
while (aces.hasNext()) {
CmsAccessControlEntry ace = aces.next();
if (ace.isResponsible()) {
I_CmsPrincipal p = lookupPrincipal(dbc, ace.getPrincipal());
if (p != null) {
result.add(p);
}
}
}
return result;
}
/**
* Returns the set of users that are responsible for a specific resource.<p>
*
* @param dbc the current database context
* @param resource the resource to get the responsible users from
*
* @return the set of users that are responsible for a specific resource
*
* @throws CmsException if something goes wrong
*/
public Set<CmsUser> readResponsibleUsers(CmsDbContext dbc, CmsResource resource) throws CmsException {
Set<CmsUser> result = new HashSet<CmsUser>();
Iterator<I_CmsPrincipal> principals = readResponsiblePrincipals(dbc, resource).iterator();
while (principals.hasNext()) {
I_CmsPrincipal principal = principals.next();
if (principal.isGroup()) {
try {
result.addAll(getUsersOfGroup(dbc, principal.getName(), true, false, false));
} catch (CmsException e) {
if (LOG.isInfoEnabled()) {
LOG.info(e);
}
}
} else {
result.add((CmsUser)principal);
}
}
return result;
}
/**
* Returns a List of all siblings of the specified resource,
* the specified resource being always part of the result set.<p>
*
* The result is a list of <code>{@link CmsResource}</code> objects.<p>
*
* @param dbc the current database context
* @param resource the resource to read the siblings for
* @param filter a filter object
*
* @return a list of <code>{@link CmsResource}</code> Objects that
* are siblings to the specified resource,
* including the specified resource itself
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readSiblings(CmsDbContext dbc, CmsResource resource, CmsResourceFilter filter)
throws CmsException {
List<CmsResource> siblings = getVfsDriver(dbc).readSiblings(
dbc,
dbc.currentProject().getUuid(),
resource,
filter.includeDeleted());
// important: there is no permission check done on the returned list of siblings
// this is because of possible issues with the "publish all siblings" option,
// moreover the user has read permission for the content through
// the selected sibling anyway
return updateContextDates(dbc, siblings, filter);
}
/**
* Returns the parameters of a resource in the table of all published template resources.<p>
*
* @param dbc the current database context
* @param rfsName the rfs name of the resource
*
* @return the parameter string of the requested resource
*
* @throws CmsException if something goes wrong
*/
public String readStaticExportPublishedResourceParameters(CmsDbContext dbc, String rfsName) throws CmsException {
return getProjectDriver(dbc).readStaticExportPublishedResourceParameters(dbc, rfsName);
}
/**
* Returns a list of all template resources which must be processed during a static export.<p>
*
* @param dbc the current database context
* @param parameterResources flag for reading resources with parameters (1) or without (0)
* @param timestamp for reading the data from the db
*
* @return a list of template resources as <code>{@link String}</code> objects
*
* @throws CmsException if something goes wrong
*/
public List<String> readStaticExportResources(CmsDbContext dbc, int parameterResources, long timestamp)
throws CmsException {
return getProjectDriver(dbc).readStaticExportResources(dbc, parameterResources, timestamp);
}
/**
* Returns the subscribed history resources that were deleted.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param user the user that subscribed to the resource
* @param groups the groups to check subscribed resources for
* @param parent the parent resource (folder) of the deleted resources, if <code>null</code> all deleted resources will be returned
* @param includeSubFolders indicates if the sub folders of the specified folder path should be considered, too
* @param deletedFrom the time stamp from which the resources should have been deleted
*
* @return the subscribed history resources that were deleted
*
* @throws CmsException if something goes wrong
*/
public List<I_CmsHistoryResource> readSubscribedDeletedResources(
CmsDbContext dbc,
String poolName,
CmsUser user,
List<CmsGroup> groups,
CmsResource parent,
boolean includeSubFolders,
long deletedFrom) throws CmsException {
List<I_CmsHistoryResource> result = getSubscriptionDriver().readSubscribedDeletedResources(
dbc,
poolName,
user,
groups,
parent,
includeSubFolders,
deletedFrom);
return result;
}
/**
* Returns the resources that were subscribed by a user or group set in the filter.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param filter the filter that is used to get the subscribed resources
*
* @return the resources that were subscribed by a user or group set in the filter
*
* @throws CmsException if something goes wrong
*/
public List<CmsResource> readSubscribedResources(CmsDbContext dbc, String poolName, CmsSubscriptionFilter filter)
throws CmsException {
List<CmsResource> result = getSubscriptionDriver().readSubscribedResources(dbc, poolName, filter);
result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT);
return result;
}
/**
* Reads URL name mapping entries which match the given filter.<p>
*
* @param dbc the database context
* @param online if true, read online URL name mappings, else offline ones
* @param filter the filter for matching the URL name entries
*
* @return the list of URL name mapping entries which match the given filter
*
* @throws CmsDataAccessException if something goes wrong
*/
public List<CmsUrlNameMappingEntry> readUrlNameMappingEntries(
CmsDbContext dbc,
boolean online,
CmsUrlNameMappingFilter filter) throws CmsDataAccessException {
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
return vfsDriver.readUrlNameMappingEntries(dbc, online, filter);
}
/**
* Reads the newest URL names of a resource for all locales.<p>
*
* @param dbc the database context
* @param id the resource's structure id
*
* @return the url names for the locales
*
* @throws CmsDataAccessException if the database operation failed
*/
public List<String> readUrlNamesForAllLocales(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException {
List<String> result = new ArrayList<String>();
List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
dbc,
dbc.currentProject().isOnlineProject(),
CmsUrlNameMappingFilter.ALL.filterStructureId(id));
ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create();
for (CmsUrlNameMappingEntry entry : entries) {
String localeKey = entry.getLocale();
entriesByLocale.put(localeKey, entry);
}
for (String localeKey : entriesByLocale.keySet()) {
List<CmsUrlNameMappingEntry> entrs = entriesByLocale.get(localeKey);
CmsUrlNameMappingEntry maxEntryForLocale = Collections.max(entrs, new UrlNameMappingComparator());
result.add(maxEntryForLocale.getName());
}
return result;
}
/**
* Returns a user object based on the id of a user.<p>
*
* @param dbc the current database context
* @param id the id of the user to read
*
* @return the user read
*
* @throws CmsException if something goes wrong
*/
public CmsUser readUser(CmsDbContext dbc, CmsUUID id) throws CmsException {
CmsUser user = m_monitor.getCachedUser(id.toString());
if (user == null) {
user = getUserDriver(dbc).readUser(dbc, id);
m_monitor.cacheUser(user);
}
return user;
}
/**
* Returns a user object.<p>
*
* @param dbc the current database context
* @param username the name of the user that is to be read
*
* @return user read
*
* @throws CmsDataAccessException if operation was not successful
*/
public CmsUser readUser(CmsDbContext dbc, String username) throws CmsDataAccessException {
CmsUser user = m_monitor.getCachedUser(username);
if (user == null) {
user = getUserDriver(dbc).readUser(dbc, username);
m_monitor.cacheUser(user);
}
return user;
}
/**
* Returns a user object if the password for the user is correct.<p>
*
* If the user/pwd pair is not valid a <code>{@link CmsException}</code> is thrown.<p>
*
* @param dbc the current database context
* @param username the username of the user that is to be read
* @param password the password of the user that is to be read
*
* @return user read
*
* @throws CmsException if operation was not successful
*/
public CmsUser readUser(CmsDbContext dbc, String username, String password) throws CmsException {
// don't read user from cache here because password may have changed
CmsUser user = getUserDriver(dbc).readUser(dbc, username, password, null);
m_monitor.cacheUser(user);
return user;
}
/**
* Removes an access control entry for a given resource and principal.<p>
*
* @param dbc the current database context
* @param resource the resource
* @param principal the id of the principal to remove the the access control entry for
*
* @throws CmsException if something goes wrong
*/
public void removeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) throws CmsException {
// remove the ace
getUserDriver(dbc).removeAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal);
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_PERMISSIONS,
new String[] {resource.getRootPath()}), false);
// update the "last modified" information
setDateLastModified(dbc, resource, resource.getDateLastModified());
// clear the cache
m_monitor.clearAccessControlListCache();
// fire a resource modification event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_ACCESSCONTROL));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Removes a resource from the given organizational unit.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to remove the resource from
* @param resource the resource that is to be removed from the organizational unit
*
* @throws CmsException if something goes wrong
*
* @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
* @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
*/
public void removeResourceFromOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource)
throws CmsException {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
getUserDriver(dbc).removeResourceFromOrganizationalUnit(dbc, orgUnit, resource);
}
/**
* Removes a resource from the current project of the user.<p>
*
* @param dbc the current database context
* @param resource the resource to apply this operation to
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#copyResourceToProject(String)
* @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource)
*/
public void removeResourceFromProject(CmsDbContext dbc, CmsResource resource) throws CmsException {
// remove the resource to the project only if the resource is already in the project
if (isInsideCurrentProject(dbc, resource.getRootPath())) {
// check if there are already any subfolders of this resource
I_CmsProjectDriver projectDriver = getProjectDriver(dbc);
if (resource.isFolder()) {
List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject());
for (int i = 0; i < projectResources.size(); i++) {
String resname = projectResources.get(i);
if (resname.startsWith(resource.getRootPath())) {
// delete the existing project resource first
projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname);
}
}
}
try {
projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath());
} catch (CmsException exc) {
// if the subfolder exists already - all is ok
} finally {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_PROJECT_MODIFIED,
Collections.<String, Object> singletonMap("project", dbc.currentProject())));
}
}
}
/**
* Removes the given resource to the given user's publish list.<p>
*
* @param dbc the database context
* @param userId the user's id
* @param structureIds the collection of structure IDs to remove
*
* @throws CmsDataAccessException if something goes wrong
*/
public void removeResourceFromUsersPubList(CmsDbContext dbc, CmsUUID userId, Collection<CmsUUID> structureIds)
throws CmsDataAccessException {
for (CmsUUID structureId : structureIds) {
CmsLogEntry entry = new CmsLogEntry(
userId,
System.currentTimeMillis(),
structureId,
CmsLogEntryType.RESOURCE_HIDDEN,
new String[] {readResource(dbc, structureId, CmsResourceFilter.ALL).getRootPath()});
log(dbc, entry, true);
}
}
/**
* Removes a user from a group.<p>
*
* @param dbc the current database context
* @param username the name of the user that is to be removed from the group
* @param groupname the name of the group
* @param readRoles if to read roles or groups
*
* @throws CmsException if operation was not successful
* @throws CmsIllegalArgumentException if the given user was not member in the given group
* @throws CmsDbEntryNotFoundException if the given group was not found
* @throws CmsSecurityException if the given user was <b>read as 'null' from the database</b>
*
* @see #addUserToGroup(CmsDbContext, String, String, boolean)
*/
public void removeUserFromGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles)
throws CmsException, CmsIllegalArgumentException, CmsDbEntryNotFoundException, CmsSecurityException {
CmsGroup group = readGroup(dbc, groupname);
//check if group exists
if (group == null) {
// the group does not exists
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
}
if (group.isVirtual() && !readRoles) {
// if removing a user from a virtual role treat it as removing the user from the role
removeUserFromGroup(dbc, username, CmsRole.valueOf(group).getGroupName(), true);
return;
}
if (group.isVirtual()) {
// this is an hack so to prevent a unlimited recursive calls
readRoles = false;
}
if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) {
// we want a role but we got a group, or the other way
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
}
// test if this user is existing in the group
if (!userInGroup(dbc, username, groupname, readRoles)) {
// user is not in the group, throw exception
throw new CmsIllegalArgumentException(Messages.get().container(
Messages.ERR_USER_NOT_IN_GROUP_2,
username,
groupname));
}
CmsUser user = readUser(dbc, username);
//check if the user exists
if (user == null) {
// the user does not exists
throw new CmsIllegalArgumentException(Messages.get().container(
Messages.ERR_USER_NOT_IN_GROUP_2,
username,
groupname));
}
if (readRoles) {
CmsRole role = CmsRole.valueOf(group);
// the workplace user role can only be removed if no other user has no other role
if (role.equals(CmsRole.WORKPLACE_USER.forOrgUnit(role.getOuFqn()))) {
if (getGroupsOfUser(
dbc,
username,
role.getOuFqn(),
false,
true,
true,
dbc.getRequestContext().getRemoteAddress()).size() > 1) {
return;
}
}
// update virtual groups
Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator();
while (it.hasNext()) {
CmsGroup virtualGroup = it.next();
if (userInGroup(dbc, username, virtualGroup.getName(), false)) {
// here we say readroles = true, to prevent an unlimited recursive calls
removeUserFromGroup(dbc, username, virtualGroup.getName(), true);
}
}
}
getUserDriver(dbc).deleteUserInGroup(dbc, user.getId(), group.getId());
// flush relevant caches
if (readRoles) {
m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
}
m_monitor.flushCache(CmsMemoryMonitor.CacheType.USERGROUPS, CmsMemoryMonitor.CacheType.USER_LIST);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
eventData.put(
I_CmsEventListener.KEY_USER_ACTION,
I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
}
/**
* Repairs broken categories.<p>
*
* @param dbc the database context
* @param projectId the project id
* @param resource the resource to repair the categories for
*
* @throws CmsException if something goes wrong
*/
public void repairCategories(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsException {
CmsObject cms = OpenCms.initCmsObject(new CmsObject(getSecurityManager(), dbc.getRequestContext()));
cms.getRequestContext().setSiteRoot("");
cms.getRequestContext().setCurrentProject(readProject(dbc, projectId));
CmsCategoryService.getInstance().repairRelations(cms, resource);
}
/**
* Replaces the content, type and properties of a resource.<p>
*
* @param dbc the current database context
* @param resource the name of the resource to apply this operation to
* @param type the new type of the resource
* @param content the new content of the resource
* @param properties the new properties of the resource
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#replaceResource(String, int, byte[], List)
* @see I_CmsResourceType#replaceResource(CmsObject, CmsSecurityManager, CmsResource, int, byte[], List)
*/
public void replaceResource(
CmsDbContext dbc,
CmsResource resource,
int type,
byte[] content,
List<CmsProperty> properties) throws CmsException {
// replace the existing with the new file content
getVfsDriver(dbc).replaceResource(dbc, resource, content, type);
if ((properties != null) && !properties.isEmpty()) {
// write the properties
getVfsDriver(dbc).writePropertyObjects(dbc, dbc.currentProject(), resource, properties);
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
}
// update the resource state
if (resource.getState().isUnchanged()) {
resource.setState(CmsResource.STATE_CHANGED);
}
resource.setUserLastModified(dbc.currentUser().getId());
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_CONTENT_MODIFIED,
new String[] {resource.getRootPath()}), false);
setDateLastModified(dbc, resource, System.currentTimeMillis());
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false);
deleteRelationsWithSiblings(dbc, resource);
// clear the cache
m_monitor.clearResourceCache();
if ((properties != null) && !properties.isEmpty()) {
// resource and properties were modified
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource)));
} else {
// only the resource was modified
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE | CHANGED_CONTENT));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
}
/**
* Resets the password for a specified user.<p>
*
* @param dbc the current database context
* @param username the name of the user
* @param oldPassword the old password
* @param newPassword the new password
*
* @throws CmsException if the user data could not be read from the database
* @throws CmsSecurityException if the specified username and old password could not be verified
*/
public void resetPassword(CmsDbContext dbc, String username, String oldPassword, String newPassword)
throws CmsException, CmsSecurityException {
if ((oldPassword != null) && (newPassword != null)) {
CmsUser user = null;
validatePassword(newPassword);
// read the user as a system user to verify that the specified old password is correct
try {
user = getUserDriver(dbc).readUser(dbc, username, oldPassword, null);
} catch (CmsDbEntryNotFoundException e) {
throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), e);
}
if ((user == null) || user.isManaged()) {
throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username));
}
getUserDriver(dbc).writePassword(dbc, username, oldPassword, newPassword);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(
I_CmsEventListener.KEY_USER_ACTION,
I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
} else if (CmsStringUtil.isEmpty(oldPassword)) {
throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_OLD_MISSING_0));
} else if (CmsStringUtil.isEmpty(newPassword)) {
throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_NEW_MISSING_0));
}
}
/**
* Restores a deleted resource identified by its structure id from the historical archive.<p>
*
* @param dbc the current database context
* @param structureId the structure id of the resource to restore
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#restoreDeletedResource(CmsUUID)
*/
public void restoreDeletedResource(CmsDbContext dbc, CmsUUID structureId) throws CmsException {
// get the last version, which should be the deleted one
int version = getHistoryDriver(dbc).readLastVersion(dbc, structureId);
// get that version
I_CmsHistoryResource histRes = getHistoryDriver(dbc).readResource(dbc, structureId, version);
// check the parent path
CmsResource parent;
try {
// try to read the parent resource by id
parent = getVfsDriver(dbc).readResource(dbc, dbc.currentProject().getUuid(), histRes.getParentId(), true);
} catch (CmsVfsResourceNotFoundException e) {
// if not found try to read the parent resource by name
try {
// try to read the parent resource by id
parent = getVfsDriver(dbc).readResource(
dbc,
dbc.currentProject().getUuid(),
CmsResource.getParentFolder(histRes.getRootPath()),
true);
} catch (CmsVfsResourceNotFoundException e1) {
// if not found try to restore the parent resource
restoreDeletedResource(dbc, histRes.getParentId());
parent = readResource(dbc, histRes.getParentId(), CmsResourceFilter.IGNORE_EXPIRATION);
}
}
// check write permissions
m_securityManager.checkPermissions(
dbc,
parent,
CmsPermissionSet.ACCESS_WRITE,
false,
CmsResourceFilter.IGNORE_EXPIRATION);
// check the name
String path = CmsResource.getParentFolder(histRes.getRootPath()); // path
String resName = CmsResource.getName(histRes.getRootPath()); // name
String ext = "";
if (resName.charAt(resName.length() - 1) == '/') {
resName = resName.substring(0, resName.length() - 1);
} else {
ext = CmsFileUtil.getExtension(resName); // extension
}
String nameWOExt = resName.substring(0, resName.length() - ext.length()); // name without extension
for (int i = 1; true; i++) {
try {
readResource(dbc, path + resName, CmsResourceFilter.ALL);
resName = nameWOExt + "_" + i + ext;
// try the next resource name with following schema: path/name_{i}.ext
} catch (CmsVfsResourceNotFoundException e) {
// ok, we found a not used resource name
break;
}
}
// check structure id
CmsUUID id = structureId;
if (getVfsDriver(dbc).validateStructureIdExists(dbc, dbc.currentProject().getUuid(), structureId)) {
// should never happen, but if already exists create a new one
id = new CmsUUID();
}
byte[] contents = null;
boolean isFolder = true;
// do we need the contents?
if (histRes instanceof CmsFile) {
contents = ((CmsFile)histRes).getContents();
if ((contents == null) || (contents.length == 0)) {
contents = getHistoryDriver(dbc).readContent(dbc, histRes.getResourceId(), histRes.getPublishTag());
}
isFolder = false;
}
// now read the historical properties
List<CmsProperty> properties = getHistoryDriver(dbc).readProperties(dbc, histRes);
// create the object to create
CmsResource newResource = new CmsResource(
id,
histRes.getResourceId(),
path + resName,
histRes.getTypeId(),
isFolder,
histRes.getFlags(),
dbc.currentProject().getUuid(),
CmsResource.STATE_NEW,
histRes.getDateCreated(),
histRes.getUserCreated(),
histRes.getDateLastModified(),
dbc.currentUser().getId(),
histRes.getDateReleased(),
histRes.getDateExpired(),
histRes.getSiblingCount(),
histRes.getLength(),
histRes.getDateContent(),
histRes.getVersion());
// log it
log(dbc, new CmsLogEntry(
dbc,
newResource.getStructureId(),
CmsLogEntryType.RESOURCE_RESTORE_DELETED,
new String[] {newResource.getRootPath()}), false);
// prevent the date last modified is set to the current time
newResource.setDateLastModified(newResource.getDateLastModified());
// restore the resource!
CmsResource resource = createResource(dbc, path + resName, newResource, contents, properties, true);
// set resource state to changed
newResource.setState(CmsResource.STATE_CHANGED);
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), newResource, UPDATE_RESOURCE_STATE, false);
newResource.setState(CmsResource.STATE_NEW);
// fire the event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE | CHANGED_CONTENT));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Restores a resource in the current project with a version from the historical archive.<p>
*
* @param dbc the current database context
* @param resource the resource to restore from the archive
* @param version the version number to restore from the archive
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#restoreResourceVersion(CmsUUID, int)
* @see I_CmsResourceType#restoreResource(CmsObject, CmsSecurityManager, CmsResource, int)
*/
public void restoreResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException {
I_CmsHistoryResource historyResource = readResource(dbc, resource, version);
CmsResourceState state = CmsResource.STATE_CHANGED;
if (resource.getState().isNew()) {
state = CmsResource.STATE_NEW;
}
int newVersion = resource.getVersion();
if (resource.getState().isUnchanged()) {
newVersion++;
}
CmsResource newResource = null;
// is the resource a file?
if (historyResource instanceof CmsFile) {
// get the historical up flags
int flags = historyResource.getFlags();
if (resource.isLabeled()) {
// set the flag for labeled links on the restored file
flags |= CmsResource.FLAG_LABELED;
}
CmsFile newFile = new CmsFile(
resource.getStructureId(),
resource.getResourceId(),
resource.getRootPath(),
historyResource.getTypeId(),
flags,
dbc.currentProject().getUuid(),
state,
resource.getDateCreated(),
historyResource.getUserCreated(),
resource.getDateLastModified(),
dbc.currentUser().getId(),
historyResource.getDateReleased(),
historyResource.getDateExpired(),
resource.getSiblingCount(),
historyResource.getLength(),
historyResource.getDateContent(),
newVersion,
readFile(dbc, (CmsHistoryFile)historyResource).getContents());
// log it
log(dbc, new CmsLogEntry(
dbc,
newFile.getStructureId(),
CmsLogEntryType.RESOURCE_HISTORY,
new String[] {newFile.getRootPath()}), false);
newResource = writeFile(dbc, newFile);
} else {
// it is a folder!
newResource = new CmsFolder(
resource.getStructureId(),
resource.getResourceId(),
resource.getRootPath(),
historyResource.getTypeId(),
historyResource.getFlags(),
dbc.currentProject().getUuid(),
state,
resource.getDateCreated(),
historyResource.getUserCreated(),
resource.getDateLastModified(),
dbc.currentUser().getId(),
historyResource.getDateReleased(),
historyResource.getDateExpired(),
newVersion);
// log it
log(dbc, new CmsLogEntry(
dbc,
newResource.getStructureId(),
CmsLogEntryType.RESOURCE_HISTORY,
new String[] {newResource.getRootPath()}), false);
writeResource(dbc, newResource);
}
if (newResource != null) {
// now read the historical properties
List<CmsProperty> historyProperties = getHistoryDriver(dbc).readProperties(dbc, historyResource);
// remove all properties
deleteAllProperties(dbc, newResource.getRootPath());
// write them to the restored resource
writePropertyObjects(dbc, newResource, historyProperties, false);
m_monitor.clearResourceCache();
}
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE | CHANGED_CONTENT));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Searches for users which fit the given criteria.<p>
*
* @param dbc the database context
* @param searchParams the search criteria
*
* @return the users which fit the search criteria
*
* @throws CmsDataAccessException if something goes wrong
*/
public List<CmsUser> searchUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams
) throws CmsDataAccessException {
return getUserDriver(dbc).searchUsers(dbc, searchParams);
}
/**
* Changes the "expire" date of a resource.<p>
*
* @param dbc the current database context
* @param resource the resource to touch
* @param dateExpired the new expire date of the resource
*
* @throws CmsDataAccessException if something goes wrong
*
* @see CmsObject#setDateExpired(String, long, boolean)
* @see I_CmsResourceType#setDateExpired(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
*/
public void setDateExpired(CmsDbContext dbc, CmsResource resource, long dateExpired) throws CmsDataAccessException {
resource.setDateExpired(dateExpired);
if (resource.getState().isUnchanged()) {
resource.setState(CmsResource.STATE_CHANGED);
}
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false);
// modify the last modified project reference
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false);
// log
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_DATE_EXPIRED,
new String[] {resource.getRootPath()}), false);
// clear the cache
m_monitor.clearResourceCache();
// fire the event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_TIMEFRAME));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Changes the "last modified" timestamp of a resource.<p>
*
* @param dbc the current database context
* @param resource the resource to touch
* @param dateLastModified the new last modified date of the resource
*
* @throws CmsDataAccessException if something goes wrong
*
* @see CmsObject#setDateLastModified(String, long, boolean)
* @see I_CmsResourceType#setDateLastModified(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
*/
public void setDateLastModified(CmsDbContext dbc, CmsResource resource, long dateLastModified)
throws CmsDataAccessException {
// modify the last modification date
resource.setDateLastModified(dateLastModified);
if (resource.getState().isUnchanged()) {
resource.setState(CmsResource.STATE_CHANGED);
} else if (resource.getState().isNew() && (resource.getSiblingCount() > 1)) {
// in case of new resources with siblings make sure the state is correct
resource.setState(CmsResource.STATE_CHANGED);
}
resource.setUserLastModified(dbc.currentUser().getId());
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false);
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_TOUCHED,
new String[] {resource.getRootPath()}), false);
// clear the cache
m_monitor.clearResourceCache();
// fire the event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_LASTMODIFIED));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Changes the "release" date of a resource.<p>
*
* @param dbc the current database context
* @param resource the resource to touch
* @param dateReleased the new release date of the resource
*
* @throws CmsDataAccessException if something goes wrong
*
* @see CmsObject#setDateReleased(String, long, boolean)
* @see I_CmsResourceType#setDateReleased(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
*/
public void setDateReleased(CmsDbContext dbc, CmsResource resource, long dateReleased)
throws CmsDataAccessException {
// modify the last modification date
resource.setDateReleased(dateReleased);
if (resource.getState().isUnchanged()) {
resource.setState(CmsResource.STATE_CHANGED);
}
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false);
// modify the last modified project reference
getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false);
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_DATE_RELEASED,
new String[] {resource.getRootPath()}), false);
// clear the cache
m_monitor.clearResourceCache();
// fire the event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_TIMEFRAME));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Sets a new parent group for an already existing group.<p>
*
* @param dbc the current database context
* @param groupName the name of the group that should be written
* @param parentGroupName the name of the parent group to set,
* or <code>null</code> if the parent
* group should be deleted.
*
* @throws CmsException if operation was not successful
* @throws CmsDataAccessException if the group with <code>groupName</code> could not be read from VFS
*/
public void setParentGroup(CmsDbContext dbc, String groupName, String parentGroupName)
throws CmsException, CmsDataAccessException {
CmsGroup group = readGroup(dbc, groupName);
CmsUUID parentGroupId = CmsUUID.getNullUUID();
// if the group exists, use its id, else set to unknown.
if (parentGroupName != null) {
parentGroupId = readGroup(dbc, parentGroupName).getId();
}
group.setParentId(parentGroupId);
// write the changes to the cms
writeGroup(dbc, group);
}
/**
* Sets the password for a user.<p>
*
* @param dbc the current database context
* @param username the name of the user
* @param newPassword the new password
*
* @throws CmsException if operation was not successful
* @throws CmsIllegalArgumentException if the user with the <code>username</code> was not found
*/
public void setPassword(CmsDbContext dbc, String username, String newPassword)
throws CmsException, CmsIllegalArgumentException {
validatePassword(newPassword);
// read the user as a system user to verify that the specified old password is correct
getUserDriver(dbc).readUser(dbc, username);
// only continue if not found and read user from web might succeed
getUserDriver(dbc).writePassword(dbc, username, null, newPassword);
}
/**
* Marks a subscribed resource as deleted.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param resource the subscribed resource to mark as deleted
*
* @throws CmsException if something goes wrong
*/
public void setSubscribedResourceAsDeleted(CmsDbContext dbc, String poolName, CmsResource resource)
throws CmsException {
getSubscriptionDriver().setSubscribedResourceAsDeleted(dbc, poolName, resource);
}
/**
* Moves an user to the given organizational unit.<p>
*
* @param dbc the current db context
* @param orgUnit the organizational unit to add the resource to
* @param user the user that is to be moved to the organizational unit
*
* @throws CmsException if something goes wrong
*
* @see org.opencms.security.CmsOrgUnitManager#setUsersOrganizationalUnit(CmsObject, String, String)
*/
public void setUsersOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsUser user)
throws CmsException {
if (!getGroupsOfUser(dbc, user.getName(), false).isEmpty()) {
throw new CmsDbConsistencyException(Messages.get().container(
Messages.ERR_ORGUNIT_MOVE_USER_2,
orgUnit.getName(),
user.getName()));
}
// move the principal
getUserDriver(dbc).setUsersOrganizationalUnit(dbc, orgUnit, user);
// remove the principal from cache
m_monitor.clearUserCache(user);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_OU_NAME, user.getOuFqn());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
}
/**
* Subscribes the user or group to the resource.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param principal the principal that subscribes to the resource
* @param resource the resource to subscribe to
*
* @throws CmsException if something goes wrong
*/
public void subscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource)
throws CmsException {
getSubscriptionDriver().subscribeResourceFor(dbc, poolName, principal, resource);
}
/**
* Undelete the resource.<p>
*
* @param dbc the current database context
* @param resource the name of the resource to apply this operation to
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#undeleteResource(String, boolean)
* @see I_CmsResourceType#undelete(CmsObject, CmsSecurityManager, CmsResource, boolean)
*/
public void undelete(CmsDbContext dbc, CmsResource resource) throws CmsException {
if (!resource.getState().isDeleted()) {
throw new CmsVfsException(Messages.get().container(
Messages.ERR_UNDELETE_FOR_RESOURCE_DELETED_1,
dbc.removeSiteRoot(resource.getRootPath())));
}
// set the state to changed
resource.setState(CmsResourceState.STATE_CHANGED);
// perform the changes
updateState(dbc, resource, false);
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_UNDELETED,
new String[] {resource.getRootPath()}), false);
// clear the cache
m_monitor.clearResourceCache();
// fire change event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Undos all changes in the resource by restoring the version from the
* online project to the current offline project.<p>
*
* @param dbc the current database context
* @param resource the name of the resource to apply this operation to
* @param mode the undo mode, one of the <code>{@link org.opencms.file.CmsResource.CmsResourceUndoMode}#UNDO_XXX</code> constants
* please note that the recursive flag is ignored at this level
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#undoChanges(String, CmsResource.CmsResourceUndoMode)
* @see I_CmsResourceType#undoChanges(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode)
*/
public void undoChanges(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceUndoMode mode)
throws CmsException {
if (resource.getState().isNew()) {
// undo changes is impossible on a new resource
throw new CmsVfsException(Messages.get().container(Messages.ERR_UNDO_CHANGES_FOR_RESOURCE_NEW_0));
}
// we need this for later use
CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
// read the resource from the online project
CmsResource onlineResource = getVfsDriver(dbc).readResource(
dbc,
CmsProject.ONLINE_PROJECT_ID,
resource.getStructureId(),
true);
CmsResource onlineResourceByPath = null;
try {
// this is needed to figure out if a moved resource overwrote a deleted one
onlineResourceByPath = getVfsDriver(dbc).readResource(
dbc,
CmsProject.ONLINE_PROJECT_ID,
resource.getRootPath(),
true);
// force undo move operation if needed
if (!mode.isUndoMove() && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) {
mode = mode.includeMove();
}
} catch (Exception e) {
// ok
}
boolean moved = !onlineResource.getRootPath().equals(resource.getRootPath());
// undo move operation if required
if (moved && mode.isUndoMove()) {
moveResource(dbc, resource, onlineResource.getRootPath(), true);
if ((onlineResourceByPath != null)
&& !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) {
// was moved over deleted, so the deleted file has to be undone
undoContentChanges(dbc, onlineProject, null, onlineResourceByPath, CmsResource.STATE_UNCHANGED, true);
}
}
// undo content changes
CmsResourceState newState = CmsResource.STATE_UNCHANGED;
if (moved && !mode.isUndoMove()) {
newState = CmsResource.STATE_CHANGED;
}
undoContentChanges(dbc, onlineProject, resource, onlineResource, newState, moved && mode.isUndoMove());
// because undoContentChanges deletes the offline resource internally, we have
// to write an entry to the log table to prevent the resource from appearing in the
// user's publish list.
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_CHANGES_UNDONE,
new String[] {resource.getRootPath()}), true);
}
/**
* Unlocks all resources in the given project.<p>
*
* @param project the project to unlock the resources in
*/
public void unlockProject(CmsProject project) {
// unlock all resources in the project
m_lockManager.removeResourcesInProject(project.getUuid(), false);
m_monitor.clearResourceCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT, CmsMemoryMonitor.CacheType.PERMISSION);
}
/**
* Unlocks a resource.<p>
*
* @param dbc the current database context
* @param resource the resource to unlock
* @param force <code>true</code>, if a resource is forced to get unlocked, no matter by which user and in which project the resource is currently locked
* @param removeSystemLock <code>true</code>, if you also want to remove system locks
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#unlockResource(String)
* @see I_CmsResourceType#unlockResource(CmsObject, CmsSecurityManager, CmsResource)
*/
public void unlockResource(CmsDbContext dbc, CmsResource resource, boolean force, boolean removeSystemLock)
throws CmsException {
// update the resource cache
m_monitor.clearResourceCache();
// now update lock status
m_lockManager.removeResource(dbc, resource, force, removeSystemLock);
// we must also clear the permission cache
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION);
// fire resource modification event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(NOTHING_CHANGED));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Unsubscribes all deleted resources that were deleted before the specified time stamp.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param deletedTo the time stamp to which the resources have been deleted
*
* @throws CmsException if something goes wrong
*/
public void unsubscribeAllDeletedResources(CmsDbContext dbc, String poolName, long deletedTo) throws CmsException {
getSubscriptionDriver().unsubscribeAllDeletedResources(dbc, poolName, deletedTo);
}
/**
* Unsubscribes the principal from all resources.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param principal the principal that unsubscribes from all resources
*
* @throws CmsException if something goes wrong
*/
public void unsubscribeAllResourcesFor(CmsDbContext dbc, String poolName, CmsPrincipal principal)
throws CmsException {
getSubscriptionDriver().unsubscribeAllResourcesFor(dbc, poolName, principal);
}
/**
* Unsubscribes the principal from the resource.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param principal the principal that unsubscribes from the resource
* @param resource the resource to unsubscribe from
*
* @throws CmsException if something goes wrong
*/
public void unsubscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource)
throws CmsException {
getSubscriptionDriver().unsubscribeResourceFor(dbc, poolName, principal, resource);
}
/**
* Unsubscribes all groups and users from the resource.<p>
*
* @param dbc the database context
* @param poolName the name of the database pool to use
* @param resource the resource to unsubscribe all groups and users from
*
* @throws CmsException if something goes wrong
*/
public void unsubscribeResourceForAll(CmsDbContext dbc, String poolName, CmsResource resource) throws CmsException {
getSubscriptionDriver().unsubscribeResourceForAll(dbc, poolName, resource);
}
/**
* Update the export points.<p>
*
* All files and folders "inside" an export point are written.<p>
*
* @param dbc the current database context
*/
public void updateExportPoints(CmsDbContext dbc) {
try {
// read the export points and return immediately if there are no export points at all
Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>();
exportPoints.addAll(OpenCms.getExportPoints());
exportPoints.addAll(OpenCms.getModuleManager().getExportPoints());
if (exportPoints.size() == 0) {
if (LOG.isWarnEnabled()) {
LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0));
}
return;
}
// create the driver to write the export points
CmsExportPointDriver exportPointDriver = new CmsExportPointDriver(exportPoints);
// the export point hash table contains RFS export paths keyed by their internal VFS paths
Iterator<String> i = exportPointDriver.getExportPointPaths().iterator();
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
while (i.hasNext()) {
String currentExportPoint = i.next();
// print some report messages
if (LOG.isInfoEnabled()) {
LOG.info(Messages.get().getBundle().key(Messages.LOG_WRITE_EXPORT_POINT_1, currentExportPoint));
}
try {
CmsResourceFilter filter = CmsResourceFilter.DEFAULT;
List<CmsResource> resources = vfsDriver.readResourceTree(
dbc,
CmsProject.ONLINE_PROJECT_ID,
currentExportPoint,
filter.getType(),
filter.getState(),
filter.getModifiedAfter(),
filter.getModifiedBefore(),
filter.getReleaseAfter(),
filter.getReleaseBefore(),
filter.getExpireAfter(),
filter.getExpireBefore(),
CmsDriverManager.READMODE_INCLUDE_TREE
| (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0)
| (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0));
Iterator<CmsResource> j = resources.iterator();
while (j.hasNext()) {
CmsResource currentResource = j.next();
if (currentResource.isFolder()) {
// export the folder
exportPointDriver.createFolder(currentResource.getRootPath(), currentExportPoint);
} else {
// try to create the exportpoint folder
exportPointDriver.createFolder(currentExportPoint, currentExportPoint);
byte[] onlineContent = vfsDriver.readContent(
dbc,
CmsProject.ONLINE_PROJECT_ID,
currentResource.getResourceId());
// export the file content online
exportPointDriver.writeFile(
currentResource.getRootPath(),
currentExportPoint,
onlineContent);
}
}
} catch (CmsException e) {
// there might exist export points without corresponding resources in the VFS
// -> ignore exceptions which are not "resource not found" exception quiet here
if (e instanceof CmsVfsResourceNotFoundException) {
if (LOG.isErrorEnabled()) {
LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e);
}
}
}
}
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e);
}
}
}
/**
* Logs everything that has not been written to DB jet.<p>
*
* @param dbc the current db context
*
* @throws CmsDataAccessException if something goes wrong
*/
public void updateLog(CmsDbContext dbc) throws CmsDataAccessException {
if (m_log.isEmpty()) {
return;
}
List<CmsLogEntry> log = new ArrayList<CmsLogEntry>(m_log);
m_log.clear();
m_projectDriver.log(dbc, log);
}
/**
* Updates/Creates the given relations for the given resource.<p>
*
* @param dbc the db context
* @param resource the resource to update the relations for
* @param links the links to consider for updating
*
* @throws CmsException if something goes wrong
*
* @see CmsSecurityManager#updateRelationsForResource(CmsRequestContext, CmsResource, List)
*/
public void updateRelationsForResource(CmsDbContext dbc, CmsResource resource, List<CmsLink> links)
throws CmsException {
deleteRelationsWithSiblings(dbc, resource);
// build the links again only if needed
if ((links == null) || links.isEmpty()) {
return;
}
// the set of written relations
Set<CmsRelation> writtenRelations = new HashSet<CmsRelation>();
// create new relation information
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
Iterator<CmsLink> itLinks = links.iterator();
while (itLinks.hasNext()) {
CmsLink link = itLinks.next();
if (link.isInternal()) { // only update internal links
if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) {
// only an anchor
continue;
}
CmsUUID targetId = link.getStructureId();
String destPath = link.getTarget();
if (targetId != null) {
// the link target may not be a VFS path even if the link id is a structure id,
// so if possible, we read the resource for the id and set the relation target to its
// real root path.
try {
CmsResource destRes = readResource(dbc, targetId, CmsResourceFilter.ALL);
destPath = destRes.getRootPath();
} catch (CmsVfsResourceNotFoundException e) {
// ignore
}
}
CmsRelation originalRelation = new CmsRelation(
resource.getStructureId(),
resource.getRootPath(),
link.getStructureId(),
destPath,
link.getType());
// do not write twice the same relation
if (writtenRelations.contains(originalRelation)) {
continue;
}
writtenRelations.add(originalRelation);
// TODO: it would be good to have the link locale to make the relation just to the right sibling
// create the relations in content for all siblings
Iterator<CmsResource> itSiblings = readSiblings(dbc, resource, CmsResourceFilter.ALL).iterator();
while (itSiblings.hasNext()) {
CmsResource sibling = itSiblings.next();
CmsRelation relation = new CmsRelation(
sibling.getStructureId(),
sibling.getRootPath(),
originalRelation.getTargetId(),
originalRelation.getTargetPath(),
link.getType());
vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation);
}
}
}
}
/**
* Returns <code>true</code> if a user is member of the given group.<p>
*
* @param dbc the current database context
* @param username the name of the user to check
* @param groupname the name of the group to check
* @param readRoles if to read roles or groups
*
* @return <code>true</code>, if the user is in the group, <code>false</code> otherwise
*
* @throws CmsException if something goes wrong
*/
public boolean userInGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles)
throws CmsException {
List<CmsGroup> groups = getGroupsOfUser(dbc, username, readRoles);
for (int i = 0; i < groups.size(); i++) {
CmsGroup group = groups.get(i);
if (groupname.equals(group.getName()) || groupname.substring(1).equals(group.getName())) {
return true;
}
}
return false;
}
/**
* This method checks if a new password follows the rules for
* new passwords, which are defined by a Class implementing the
* <code>{@link org.opencms.security.I_CmsPasswordHandler}</code>
* interface and configured in the opencms.properties file.<p>
*
* If this method throws no exception the password is valid.<p>
*
* @param password the new password that has to be checked
*
* @throws CmsSecurityException if the password is not valid
*/
public void validatePassword(String password) throws CmsSecurityException {
OpenCms.getPasswordHandler().validatePassword(password);
}
/**
* Validates the relations for the given resources.<p>
*
* @param dbc the database context
* @param publishList the resources to validate during publishing
* @param report a report to write the messages to
*
* @return a map with lists of invalid links
* (<code>{@link org.opencms.relations.CmsRelation}}</code> objects)
* keyed by root paths
*
* @throws Exception if something goes wrong
*/
public Map<String, List<CmsRelation>> validateRelations(
CmsDbContext dbc,
CmsPublishList publishList,
I_CmsReport report) throws Exception {
return m_htmlLinkValidator.validateResources(dbc, publishList, report);
}
/**
* Writes an access control entries to a given resource.<p>
*
* @param dbc the current database context
* @param resource the resource
* @param ace the entry to write
*
* @throws CmsException if something goes wrong
*/
public void writeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsAccessControlEntry ace)
throws CmsException {
// write the new ace
getUserDriver(dbc).writeAccessControlEntry(dbc, dbc.currentProject(), ace);
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_PERMISSIONS,
new String[] {resource.getRootPath()}), false);
// update the "last modified" information
setDateLastModified(dbc, resource, resource.getDateLastModified());
// clear the cache
m_monitor.clearAccessControlListCache();
// fire a resource modification event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_ACCESSCONTROL));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Writes all export points into the file system for the publish task
* specified by trhe given publish history ID.<p>
*
* @param dbc the current database context
* @param report an I_CmsReport instance to print output message, or null to write messages to the log file
* @param publishHistoryId ID to identify the publish task in the publish history
*/
public void writeExportPoints(CmsDbContext dbc, I_CmsReport report, CmsUUID publishHistoryId) {
boolean printReportHeaders = false;
List<CmsPublishedResource> publishedResources = null;
try {
// read the "published resources" for the specified publish history ID
publishedResources = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId);
} catch (CmsException e) {
if (LOG.isErrorEnabled()) {
LOG.error(
Messages.get().getBundle().key(Messages.ERR_READ_PUBLISHED_RESOURCES_FOR_ID_1, publishHistoryId),
e);
}
}
if ((publishedResources == null) || publishedResources.isEmpty()) {
if (LOG.isWarnEnabled()) {
LOG.warn(Messages.get().getBundle().key(Messages.LOG_EMPTY_PUBLISH_HISTORY_1, publishHistoryId));
}
return;
}
// read the export points and return immediately if there are no export points at all
Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>();
exportPoints.addAll(OpenCms.getExportPoints());
exportPoints.addAll(OpenCms.getModuleManager().getExportPoints());
if (exportPoints.size() == 0) {
if (LOG.isWarnEnabled()) {
LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0));
}
return;
}
// create the driver to write the export points
CmsExportPointDriver exportPointDriver = new CmsExportPointDriver(exportPoints);
// the report may be null if the export point write was started by an event
if (report == null) {
if (dbc.getRequestContext() != null) {
report = new CmsLogReport(dbc.getRequestContext().getLocale(), getClass());
} else {
report = new CmsLogReport(CmsLocaleManager.getDefaultLocale(), getClass());
}
}
// iterate over all published resources to export them
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
Iterator<CmsPublishedResource> i = publishedResources.iterator();
while (i.hasNext()) {
CmsPublishedResource currentPublishedResource = i.next();
String currentExportPoint = exportPointDriver.getExportPoint(currentPublishedResource.getRootPath());
if (currentExportPoint != null) {
if (!printReportHeaders) {
report.println(
Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_BEGIN_0),
I_CmsReport.FORMAT_HEADLINE);
printReportHeaders = true;
}
// print report message
if (currentPublishedResource.getState().isDeleted()) {
report.print(Messages.get().container(Messages.RPT_EXPORT_POINTS_DELETE_0), I_CmsReport.FORMAT_NOTE);
} else {
report.print(Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_0), I_CmsReport.FORMAT_NOTE);
}
report.print(org.opencms.report.Messages.get().container(
org.opencms.report.Messages.RPT_ARGUMENT_1,
currentPublishedResource.getRootPath()));
report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
if (currentPublishedResource.isFolder()) {
// export the folder
if (currentPublishedResource.getState().isDeleted()) {
exportPointDriver.deleteResource(currentPublishedResource.getRootPath(), currentExportPoint);
} else {
exportPointDriver.createFolder(currentPublishedResource.getRootPath(), currentExportPoint);
}
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
I_CmsReport.FORMAT_OK);
} else {
// export the file
try {
if (currentPublishedResource.getState().isDeleted()) {
exportPointDriver.deleteResource(currentPublishedResource.getRootPath(), currentExportPoint);
} else {
// read the file content online
byte[] onlineContent = vfsDriver.readContent(
dbc,
CmsProject.ONLINE_PROJECT_ID,
currentPublishedResource.getResourceId());
exportPointDriver.writeFile(
currentPublishedResource.getRootPath(),
currentExportPoint,
onlineContent);
}
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
I_CmsReport.FORMAT_OK);
} catch (CmsException e) {
if (LOG.isErrorEnabled()) {
LOG.error(
Messages.get().getBundle().key(
Messages.LOG_WRITE_EXPORT_POINT_ERROR_1,
currentPublishedResource.getRootPath()),
e);
}
report.println(
org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0),
I_CmsReport.FORMAT_ERROR);
}
}
}
}
if (printReportHeaders) {
report.println(
Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_END_0),
I_CmsReport.FORMAT_HEADLINE);
}
}
/**
* Writes a resource to the OpenCms VFS, including it's content.<p>
*
* Applies only to resources of type <code>{@link CmsFile}</code>
* i.e. resources that have a binary content attached.<p>
*
* Certain resource types might apply content validation or transformation rules
* before the resource is actually written to the VFS. The returned result
* might therefore be a modified version from the provided original.<p>
*
* @param dbc the current database context
* @param resource the resource to apply this operation to
*
* @return the written resource (may have been modified)
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#writeFile(CmsFile)
* @see I_CmsResourceType#writeFile(CmsObject, CmsSecurityManager, CmsFile)
*/
public CmsFile writeFile(CmsDbContext dbc, CmsFile resource) throws CmsException {
resource.setUserLastModified(dbc.currentUser().getId());
resource.setContents(resource.getContents()); // to be sure the content date is updated
getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), resource, UPDATE_RESOURCE_STATE);
byte[] contents = resource.getContents();
getVfsDriver(dbc).writeContent(dbc, resource.getResourceId(), contents);
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_CONTENT_MODIFIED,
new String[] {resource.getRootPath()}), false);
// read the file back from db
resource = new CmsFile(readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL));
resource.setContents(contents);
deleteRelationsWithSiblings(dbc, resource);
// update the cache
m_monitor.clearResourceCache();
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_CONTENT));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
return resource;
}
/**
* Writes an already existing group.<p>
*
* The group id has to be a valid OpenCms group id.<br>
*
* The group with the given id will be completely overridden
* by the given data.<p>
*
* @param dbc the current database context
* @param group the group that should be written
*
* @throws CmsException if operation was not successful
*/
public void writeGroup(CmsDbContext dbc, CmsGroup group) throws CmsException {
CmsGroup oldGroup = readGroup(dbc, group.getName());
m_monitor.uncacheGroup(oldGroup);
getUserDriver(dbc).writeGroup(dbc, group);
m_monitor.cacheGroup(group);
if (!dbc.getProjectId().isNullUUID()) {
// group modified event is not needed
return;
}
// fire group modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
eventData.put(I_CmsEventListener.KEY_GROUP_NAME, oldGroup.getName());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_WRITE);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData));
}
/**
* Creates an historical entry of the current project.<p>
*
* @param dbc the current database context
* @param publishTag the version
* @param publishDate the date of publishing
*
* @throws CmsDataAccessException if operation was not successful
*/
public void writeHistoryProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException {
getHistoryDriver(dbc).writeProject(dbc, publishTag, publishDate);
}
/**
* Writes the locks that are currently stored in-memory to the database to allow restoring them
* in future server startups.<p>
*
* This overwrites the locks previously stored in the underlying database table.<p>
*
* @param dbc the current database context
*
* @throws CmsException if something goes wrong
*/
public void writeLocks(CmsDbContext dbc) throws CmsException {
m_lockManager.writeLocks(dbc);
}
/**
* Writes an already existing organizational unit.<p>
*
* The organizational unit id has to be a valid OpenCms organizational unit id.<br>
*
* The organizational unit with the given id will be completely overridden
* by the given data.<p>
*
* @param dbc the current db context
* @param organizationalUnit the organizational unit that should be written
*
* @throws CmsException if operation was not successful
*
* @see org.opencms.security.CmsOrgUnitManager#writeOrganizationalUnit(CmsObject, CmsOrganizationalUnit)
*/
public void writeOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) throws CmsException {
m_monitor.uncacheOrgUnit(organizationalUnit);
getUserDriver(dbc).writeOrganizationalUnit(dbc, organizationalUnit);
// create a publish list for the 'virtual' publish event
CmsResource ouRes = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT);
CmsPublishList pl = new CmsPublishList(ouRes, false);
pl.add(ouRes, false);
getProjectDriver(dbc).writePublishHistory(
dbc,
pl.getPublishHistoryId(),
new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW));
// fire the 'virtual' publish event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString());
eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData);
OpenCms.fireCmsEvent(afterPublishEvent);
m_monitor.cacheOrgUnit(organizationalUnit);
}
/**
* Writes an already existing project.<p>
*
* The project id has to be a valid OpenCms project id.<br>
*
* The project with the given id will be completely overridden
* by the given data.<p>
*
* @param dbc the current database context
* @param project the project that should be written
*
* @throws CmsException if operation was not successful
*/
public void writeProject(CmsDbContext dbc, CmsProject project) throws CmsException {
m_monitor.uncacheProject(project);
getProjectDriver(dbc).writeProject(dbc, project);
m_monitor.cacheProject(project);
}
/**
* Writes a property for a specified resource.<p>
*
* @param dbc the current database context
* @param resource the resource to write the property for
* @param property the property to write
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#writePropertyObject(String, CmsProperty)
* @see I_CmsResourceType#writePropertyObject(CmsObject, CmsSecurityManager, CmsResource, CmsProperty)
*/
public void writePropertyObject(CmsDbContext dbc, CmsResource resource, CmsProperty property) throws CmsException {
try {
if (property == CmsProperty.getNullProperty()) {
// skip empty or null properties
return;
}
// test if and what state should be updated
// 0: none, 1: structure, 2: resource
int updateState = getUpdateState(dbc, resource, Collections.singletonList(property));
// write the property
getVfsDriver(dbc).writePropertyObject(dbc, dbc.currentProject(), resource, property);
if (updateState > 0) {
updateState(dbc, resource, updateState == 2);
}
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_PROPERTIES,
new String[] {resource.getRootPath()}), false);
} finally {
// update the driver manager cache
m_monitor.clearResourceCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
// fire an event that a property of a resource has been modified
Map<String, Object> data = new HashMap<String, Object>();
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put("property", property);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_PROPERTY_MODIFIED, data));
}
}
/**
* Writes a list of properties for a specified resource.<p>
*
* Code calling this method has to ensure that the no properties
* <code>a, b</code> are contained in the specified list so that <code>a.equals(b)</code>,
* otherwise an exception is thrown.<p>
*
* @param dbc the current database context
* @param resource the resource to write the properties for
* @param properties the list of properties to write
* @param updateState if <code>true</code> the state of the resource will be updated
*
* @throws CmsException if something goes wrong
*
* @see CmsObject#writePropertyObjects(String, List)
* @see I_CmsResourceType#writePropertyObjects(CmsObject, CmsSecurityManager, CmsResource, List)
*/
public void writePropertyObjects(
CmsDbContext dbc,
CmsResource resource,
List<CmsProperty> properties,
boolean updateState) throws CmsException {
if ((properties == null) || (properties.size() == 0)) {
// skip empty or null lists
return;
}
try {
// the specified list must not contain two or more equal property objects
for (int i = 0, n = properties.size(); i < n; i++) {
Set<String> keyValidationSet = new HashSet<String>();
CmsProperty property = properties.get(i);
if (!keyValidationSet.contains(property.getName())) {
keyValidationSet.add(property.getName());
} else {
throw new CmsVfsException(Messages.get().container(
Messages.ERR_VFS_INVALID_PROPERTY_LIST_1,
property.getName()));
}
}
// test if and what state should be updated
// 0: none, 1: structure, 2: resource
int updateStateValue = 0;
if (updateState) {
updateStateValue = getUpdateState(dbc, resource, properties);
}
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
for (int i = 0; i < properties.size(); i++) {
// write the property
CmsProperty property = properties.get(i);
vfsDriver.writePropertyObject(dbc, dbc.currentProject(), resource, property);
}
if (updateStateValue > 0) {
// update state
updateState(dbc, resource, (updateStateValue == 2));
}
if (updateState) {
// log it
log(dbc, new CmsLogEntry(
dbc,
resource.getStructureId(),
CmsLogEntryType.RESOURCE_PROPERTIES,
new String[] {resource.getRootPath()}), false);
}
} finally {
// update the driver manager cache
m_monitor.clearResourceCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
// fire an event that the properties of a resource have been modified
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource)));
}
}
/**
* Updates a publish job.<p>
*
* @param dbc the current database context
* @param publishJob the publish job to update
*
* @throws CmsException if something goes wrong
*/
public void writePublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException {
getProjectDriver(dbc).writePublishJob(dbc, publishJob);
}
/**
* Writes the publish report for a publish job.<p>
*
* @param dbc the current database context
* @param publishJob the publish job
* @throws CmsException if something goes wrong
*/
public void writePublishReport(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException {
CmsPublishReport report = (CmsPublishReport)publishJob.removePublishReport();
if (report != null) {
getProjectDriver(dbc).writePublishReport(dbc, publishJob.getPublishHistoryId(), report.getContents());
}
}
/**
* Writes a resource to the OpenCms VFS.<p>
*
* @param dbc the current database context
* @param resource the resource to write
*
* @throws CmsException if something goes wrong
*/
public void writeResource(CmsDbContext dbc, CmsResource resource) throws CmsException {
// access was granted - write the resource
resource.setUserLastModified(dbc.currentUser().getId());
CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID())
? dbc.currentProject().getUuid()
: dbc.getProjectId();
getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE);
// make sure the written resource has the state correctly set
if (resource.getState().isUnchanged()) {
resource.setState(CmsResource.STATE_CHANGED);
}
// delete in content relations if the new type is not parseable
if (!(OpenCms.getResourceManager().getResourceType(resource.getTypeId()) instanceof I_CmsLinkParseable)) {
deleteRelationsWithSiblings(dbc, resource);
}
// update the cache
m_monitor.clearResourceCache();
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
/**
* Inserts an entry in the published resource table.<p>
*
* This is done during static export.<p>
*
* @param dbc the current database context
* @param resourceName The name of the resource to be added to the static export
* @param linkType the type of resource exported (0= non-parameter, 1=parameter)
* @param linkParameter the parameters added to the resource
* @param timestamp a time stamp for writing the data into the db
*
* @throws CmsException if something goes wrong
*/
public void writeStaticExportPublishedResource(
CmsDbContext dbc,
String resourceName,
int linkType,
String linkParameter,
long timestamp) throws CmsException {
getProjectDriver(dbc).writeStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter, timestamp);
}
/**
* Adds a new url name mapping for a structure id.<p>
*
* Instead of taking the name directly, this method takes an iterator of strings
* which generates candidate URL names on-the-fly. The first generated name which is
* not already mapped to another structure id will be chosen for the new URL name mapping.
*
* @param dbc the current database context
* @param nameSeq the sequence of URL name candidates
* @param structureId the structure id to which the url name should be mapped
* @param locale the locale for which the mapping should be written
*
* @return the actual name which was mapped to the structure id
*
* @throws CmsDataAccessException if something goes wrong
*/
public String writeUrlNameMapping(CmsDbContext dbc, Iterator<String> nameSeq, CmsUUID structureId, String locale)
throws CmsDataAccessException {
String bestName = findBestNameForUrlNameMapping(dbc, nameSeq, structureId, locale);
addOrReplaceUrlNameMapping(dbc, bestName, structureId, locale);
return bestName;
}
/**
* Updates the user information. <p>
*
* The user id has to be a valid OpenCms user id.<br>
*
* The user with the given id will be completely overridden
* by the given data.<p>
*
* @param dbc the current database context
* @param user the user to be updated
*
* @throws CmsException if operation was not successful
*/
public void writeUser(CmsDbContext dbc, CmsUser user) throws CmsException {
CmsUser oldUser = readUser(dbc, user.getId());
m_monitor.clearUserCache(oldUser);
getUserDriver(dbc).writeUser(dbc, user);
m_monitor.flushCache(CmsMemoryMonitor.CacheType.USERGROUPS, CmsMemoryMonitor.CacheType.USER_LIST);
if (!dbc.getProjectId().isNullUUID()) {
// user modified event is not needed
return;
}
// fire user modified event
Map<String, Object> eventData = new HashMap<String, Object>();
eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
eventData.put(I_CmsEventListener.KEY_USER_NAME, oldUser.getName());
eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER);
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
}
/**
* Adds or replaces a new url name mapping in the offline project.<p>
*
* @param dbc the current database context
* @param name the URL name of the mapping
* @param structureId the structure id of the mapping
* @param locale the locale of the mapping
*
* @throws CmsDataAccessException if something goes wrong
*/
protected void addOrReplaceUrlNameMapping(CmsDbContext dbc, String name, CmsUUID structureId, String locale)
throws CmsDataAccessException {
getVfsDriver(dbc).deleteUrlNameMappingEntries(
dbc,
false,
CmsUrlNameMappingFilter.ALL.filterStructureId(structureId).filterLocale(locale).filterState(
CmsUrlNameMappingEntry.MAPPING_STATUS_NEW));
CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry(
name,
structureId,
CmsUrlNameMappingEntry.MAPPING_STATUS_NEW,
System.currentTimeMillis(),
locale);
getVfsDriver(dbc).addUrlNameMappingEntry(dbc, false, newEntry);
}
/**
* Converts a resource to a folder (if possible).<p>
*
* @param resource the resource to convert
* @return the converted resource
*
* @throws CmsVfsResourceNotFoundException if the resource is not a folder
*/
protected CmsFolder convertResourceToFolder(CmsResource resource) throws CmsVfsResourceNotFoundException {
if (resource.isFolder()) {
return new CmsFolder(resource);
}
throw new CmsVfsResourceNotFoundException(Messages.get().container(
Messages.ERR_ACCESS_FILE_AS_FOLDER_1,
resource.getRootPath()));
}
/**
* Helper method for creating a driver from configuration data.<p>
*
* @param dbc the db context
* @param configManager the configuration manager
* @param config the configuration
* @param driverChainKey the configuration key under which the driver chain is stored
* @param suffix the suffix to append to a driver chain entry to get the key for the driver class
*
* @return the newly created driver
*/
protected Object createDriver(
CmsDbContext dbc,
CmsConfigurationManager configManager,
CmsParameterConfiguration config,
String driverChainKey,
String suffix) {
// read the vfs driver class properties and initialize a new instance
List<String> drivers = config.getList(driverChainKey);
String driverKey = drivers.get(0) + suffix;
String driverName = config.get(driverKey);
drivers = (drivers.size() > 1) ? drivers.subList(1, drivers.size()) : null;
if (driverName == null) {
CmsLog.INIT.error(Messages.get().getBundle().key(Messages.INIT_DRIVER_FAILED_1, driverKey));
}
return newDriverInstance(dbc, configManager, driverName, drivers);
}
/**
* Deletes all relations for the given resource and all its siblings.<p>
*
* @param dbc the current database context
* @param resource the resource to delete the resource for
*
* @throws CmsException if something goes wrong
*/
protected void deleteRelationsWithSiblings(CmsDbContext dbc, CmsResource resource) throws CmsException {
// get all siblings
List<CmsResource> siblings;
if (resource.getSiblingCount() > 1) {
siblings = readSiblings(dbc, resource, CmsResourceFilter.ALL);
} else {
siblings = new ArrayList<CmsResource>();
siblings.add(resource);
}
// clean the relations in content for all siblings
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
Iterator<CmsResource> it = siblings.iterator();
while (it.hasNext()) {
CmsResource sibling = it.next();
// clean the relation information for this sibling
vfsDriver.deleteRelations(
dbc,
dbc.currentProject().getUuid(),
sibling,
CmsRelationFilter.TARGETS.filterDefinedInContent());
}
}
/**
* Tries to add sub-resources of moved folders to the publish list and throws an exception if the publish list still does
* not contain some sub-resources of the moved folders.<p>
*
* @param cms the current CMS context
* @param dbc the current database context
* @param pubList the publish list
* @throws CmsException if something goes wrong
*/
protected void ensureSubResourcesOfMovedFoldersPublished(CmsObject cms, CmsDbContext dbc, CmsPublishList pubList)
throws CmsException {
List<CmsResource> topMovedFolders = pubList.getTopMovedFolders(cms);
Iterator<CmsResource> folderIt = topMovedFolders.iterator();
while (folderIt.hasNext()) {
CmsResource folder = folderIt.next();
addSubResources(dbc, pubList, folder);
}
CmsResource checkRes = pubList.checkContainsSubResources(cms, topMovedFolders);
if (checkRes != null) {
throw new CmsVfsException(Messages.get().container(
Messages.RPT_CHILDREN_OF_MOVED_FOLDER_NOT_PUBLISHED_1,
checkRes.getRootPath()));
}
}
/**
* Tries to find the best name for an URL name mapping for the given structure id.<p>
*
* @param dbc the database context
* @param nameSeq the sequence of name candidates
* @param structureId the structure id to which an URL name should be mapped
* @param locale the locale for which the URL name should be mapped
*
* @return the selected URL name candidate
*
* @throws CmsDataAccessException if something goes wrong
*/
protected String findBestNameForUrlNameMapping(
CmsDbContext dbc,
Iterator<String> nameSeq,
CmsUUID structureId,
String locale) throws CmsDataAccessException {
String newName;
boolean alreadyInUse;
do {
newName = nameSeq.next();
alreadyInUse = false;
CmsUrlNameMappingFilter filter = CmsUrlNameMappingFilter.ALL.filterName(newName);
List<CmsUrlNameMappingEntry> entriesWithSameName = getVfsDriver(dbc).readUrlNameMappingEntries(
dbc,
false,
filter);
for (CmsUrlNameMappingEntry entry : entriesWithSameName) {
boolean sameId = entry.getStructureId().equals(structureId);
boolean sameLocale = Objects.equal(entry.getLocale(), locale);
if (!sameId || !sameLocale) {
// name already used for other resource, or for different locale of the same resource
alreadyInUse = true;
break;
}
}
} while (alreadyInUse);
return newName;
}
/**
* Helper method for finding the 'best' URL name to use for a new URL name mapping.<p>
*
* Since the name given as a parameter may be already used, this method will try to append numeric suffixes
* to the name to find a mapping name which is not used.<p>
*
* @param dbc the current database context
* @param name the name of the mapping
* @param structureId the structure id to which the name is mapped
*
* @return the best name which was found for the new mapping
*
* @throws CmsDataAccessException if something goes wrong
*/
protected String findBestNameForUrlNameMapping(CmsDbContext dbc, String name, CmsUUID structureId)
throws CmsDataAccessException {
List<CmsUrlNameMappingEntry> entriesStartingWithName = getVfsDriver(dbc).readUrlNameMappingEntries(
dbc,
false,
CmsUrlNameMappingFilter.ALL.filterNamePattern(name + "%").filterRejectStructureId(structureId));
Set<String> usedNames = new HashSet<String>();
for (CmsUrlNameMappingEntry entry : entriesStartingWithName) {
usedNames.add(entry.getName());
}
int counter = 0;
String numberedName;
do {
numberedName = getNumberedName(name, counter);
counter += 1;
} while (usedNames.contains(numberedName));
return numberedName;
}
/**
* Returns the lock manager instance.<p>
*
* @return the lock manager instance
*/
protected CmsLockManager getLockManager() {
return m_lockManager;
}
/**
* Adds a numeric suffix to the end of a string, unless the number passed as a parameter is 0.<p>
*
* @param name the base name
* @param number the number from which to form the suffix
*
* @return the concatenation of the base name and possibly the numeric suffix
*/
protected String getNumberedName(String name, int number) {
if (number == 0) {
return name;
}
PrintfFormat fmt = new PrintfFormat("%0.6d");
return name + "_" + fmt.sprintf(number);
}
/**
* Counts the total number of users which fit the given criteria.<p>
*
* @param dbc the database context
* @param searchParams the user search criteria
*
* @return the total number of users matching the criteria
*
* @throws CmsDataAccessException if something goes wrong
*/
long countUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams) throws CmsDataAccessException {
return getUserDriver(dbc).countUsers(dbc, searchParams);
}
private void addSubResources(CmsDbContext dbc, CmsPublishList publishList, CmsResource directPublishResource)
throws CmsDataAccessException {
int flags = CmsDriverManager.READMODE_INCLUDE_TREE | CmsDriverManager.READMODE_EXCLUDE_STATE;
if (!directPublishResource.getState().isDeleted()) {
// fix for org.opencms.file.TestPublishIssues#testPublishFolderWithDeletedFileFromOtherProject
flags = flags | CmsDriverManager.READMODE_INCLUDE_PROJECT;
}
// add all sub resources of the folder
List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree(
dbc,
dbc.currentProject().getUuid(),
directPublishResource.getRootPath(),
CmsDriverManager.READ_IGNORE_TYPE,
CmsResource.STATE_UNCHANGED,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
flags | CmsDriverManager.READMODE_ONLY_FOLDERS);
publishList.addAll(filterResources(dbc, publishList, folderList), true);
List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree(
dbc,
dbc.currentProject().getUuid(),
directPublishResource.getRootPath(),
CmsDriverManager.READ_IGNORE_TYPE,
CmsResource.STATE_UNCHANGED,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
CmsDriverManager.READ_IGNORE_TIME,
flags | CmsDriverManager.READMODE_ONLY_FILES);
publishList.addAll(filterResources(dbc, publishList, fileList), true);
}
/**
* Checks the parent of a resource during publishing.<p>
*
* @param dbc the current database context
* @param deletedFolders a list of deleted folders
* @param res a resource to check the parent for
*
* @return <code>true</code> if the parent resource will be deleted during publishing
*/
private boolean checkDeletedParentFolder(CmsDbContext dbc, List<CmsResource> deletedFolders, CmsResource res) {
String parentPath = CmsResource.getParentFolder(res.getRootPath());
if (parentPath == null) {
// resource has no parent
return false;
}
CmsResource parent;
try {
parent = readResource(dbc, parentPath, CmsResourceFilter.ALL);
} catch (Exception e) {
// failure: if we cannot read the parent, we should not publish the resource
return false;
}
if (!parent.getState().isDeleted()) {
// parent is not deleted
return false;
}
for (int j = 0; j < deletedFolders.size(); j++) {
if ((deletedFolders.get(j)).getStructureId().equals(parent.getStructureId())) {
// parent is deleted, and it will get published
return true;
}
}
// parent is new, but it will not get published
return false;
}
/**
* Checks that no one of the resources to be published has a 'new' parent (that has not been published yet).<p>
*
* @param dbc the db context
* @param publishList the publish list to check
*
* @throws CmsVfsException if there is a resource to be published with a 'new' parent
*/
private void checkParentFolders(CmsDbContext dbc, CmsPublishList publishList) throws CmsVfsException {
boolean directPublish = publishList.isDirectPublish();
// if we direct publish a file, check if all parent folders are already published
if (directPublish) {
// first get the names of all parent folders
Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator();
List<String> parentFolderNames = new ArrayList<String>();
while (it.hasNext()) {
CmsResource res = it.next();
String parentFolderName = CmsResource.getParentFolder(res.getRootPath());
if (parentFolderName != null) {
parentFolderNames.add(parentFolderName);
}
}
// remove duplicate parent folder names
parentFolderNames = CmsFileUtil.removeRedundancies(parentFolderNames);
String parentFolderName = null;
try {
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
// now check all folders if they exist in the online project
Iterator<String> parentIt = parentFolderNames.iterator();
while (parentIt.hasNext()) {
parentFolderName = parentIt.next();
vfsDriver.readFolder(dbc, CmsProject.ONLINE_PROJECT_ID, parentFolderName);
}
} catch (CmsException e) {
throw new CmsVfsException(Messages.get().container(
Messages.RPT_PARENT_FOLDER_NOT_PUBLISHED_1,
parentFolderName));
}
}
}
/**
* Checks the parent of a resource during publishing.<p>
*
* @param dbc the current database context
* @param folderList a list of folders
* @param res a resource to check the parent for
*
* @return true if the resource should be published
*/
private boolean checkParentResource(CmsDbContext dbc, List<CmsResource> folderList, CmsResource res) {
String parentPath = CmsResource.getParentFolder(res.getRootPath());
if (parentPath == null) {
// resource has no parent
return true;
}
CmsResource parent;
try {
parent = readResource(dbc, parentPath, CmsResourceFilter.ALL);
} catch (Exception e) {
// failure: if we cannot read the parent, we should not publish the resource
return false;
}
if (!parent.getState().isNew()) {
// parent is already published
return true;
}
for (int j = 0; j < folderList.size(); j++) {
if (folderList.get(j).getStructureId().equals(parent.getStructureId())) {
// parent is new, but it will get published
return true;
}
}
// parent is new, but it will not get published
return false;
}
/**
* Copies all relations from the source resource to the target resource.<p>
*
* @param dbc the database context
* @param source the source
* @param target the target
*
* @throws CmsException if something goes wrong
*/
private void copyRelations(CmsDbContext dbc, CmsResource source, CmsResource target) throws CmsException {
// copy relations all relations
CmsObject cms = new CmsObject(getSecurityManager(), dbc.getRequestContext());
Iterator<CmsRelation> itRelations = getRelationsForResource(
dbc,
source,
CmsRelationFilter.TARGETS.filterNotDefinedInContent()).iterator();
while (itRelations.hasNext()) {
CmsRelation relation = itRelations.next();
try {
CmsResource relTarget = relation.getTarget(cms, CmsResourceFilter.ALL);
addRelationToResource(dbc, target, relTarget, relation.getType(), true);
} catch (CmsVfsResourceNotFoundException e) {
// ignore this broken relation
if (LOG.isWarnEnabled()) {
LOG.warn(e.getLocalizedMessage(), e);
}
}
}
// repair categories
repairCategories(dbc, getProjectIdForContext(dbc), target);
}
/**
* Filters the given list of resources, removes all resources where the current user
* does not have READ permissions, plus the filter is applied.<p>
*
* @param dbc the current database context
* @param resourceList a list of CmsResources
* @param filter the resource filter to use
*
* @return the filtered list of resources
*
* @throws CmsException in case errors testing the permissions
*/
private List<CmsResource> filterPermissions(
CmsDbContext dbc,
List<CmsResource> resourceList,
CmsResourceFilter filter) throws CmsException {
if (filter.requireTimerange()) {
// never check time range here - this must be done later in #updateContextDates(...)
filter = filter.addExcludeTimerange();
}
ArrayList<CmsResource> result = new ArrayList<CmsResource>(resourceList.size());
for (int i = 0; i < resourceList.size(); i++) {
// check the permission of all resources
CmsResource currentResource = resourceList.get(i);
if (m_securityManager.hasPermissions(dbc, currentResource, CmsPermissionSet.ACCESS_READ, true, filter).isAllowed()) {
// only return resources where permission was granted
result.add(currentResource);
}
}
// return the result
return result;
}
/**
* Returns a filtered list of resources for publishing.<p>
* Contains all resources, which are not locked
* and which have a parent folder that is already published or will be published, too.<p>
*
* @param dbc the current database context
* @param publishList the filling publish list
* @param resourceList the list of resources to filter
*
* @return a filtered list of resources
*/
private List<CmsResource> filterResources(
CmsDbContext dbc,
CmsPublishList publishList,
List<CmsResource> resourceList) {
List<CmsResource> result = new ArrayList<CmsResource>();
// local folder list for adding new publishing subfolders
// this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioD} problem.
List<CmsResource> newFolderList = new ArrayList<CmsResource>(publishList == null
? resourceList
: publishList.getFolderList());
for (int i = 0; i < resourceList.size(); i++) {
CmsResource res = resourceList.get(i);
try {
CmsLock lock = getLock(dbc, res);
if (lock.isPublish()) {
// if already enqueued
continue;
}
if (!lock.isLockableBy(dbc.currentUser())) {
// checks if there is a shared lock and if the resource is deleted
// this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem.
if (lock.isShared() && (publishList != null)) {
if (!res.getState().isDeleted()
|| !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) {
continue;
}
} else {
// don't add locked resources
continue;
}
}
if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, newFolderList, res)) {
continue;
}
// check permissions
try {
m_securityManager.checkPermissions(
dbc,
res,
CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
false,
CmsResourceFilter.ALL);
} catch (CmsException e) {
// skip if not enough permissions
continue;
}
if (res.isFolder()) {
newFolderList.add(res);
}
result.add(res);
} catch (Exception e) {
// should never happen
LOG.error(e.getLocalizedMessage(), e);
}
}
return result;
}
/**
* Returns a filtered list of sibling resources for publishing.<p>
*
* Contains all siblings of the given resources, which are not locked
* and which have a parent folder that is already published or will be published, too.<p>
*
* @param dbc the current database context
* @param publishList the unfinished publish list
* @param resourceList the list of siblings to filter
*
* @return a filtered list of sibling resources for publishing
*/
private List<CmsResource> filterSiblings(
CmsDbContext dbc,
CmsPublishList publishList,
Collection<CmsResource> resourceList) {
List<CmsResource> result = new ArrayList<CmsResource>();
// removed internal extendible folder list, since iterated (sibling) resources are files in any case, never folders
for (Iterator<CmsResource> i = resourceList.iterator(); i.hasNext();) {
CmsResource res = i.next();
try {
CmsLock lock = getLock(dbc, res);
if (lock.isPublish()) {
// if already enqueued
continue;
}
if (!lock.isLockableBy(dbc.currentUser())) {
// checks if there is a shared lock and if the resource is deleted
// this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem.
if (lock.isShared() && (publishList != null)) {
if (!res.getState().isDeleted()
|| !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) {
continue;
}
} else {
// don't add locked resources
continue;
}
}
if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, publishList.getFolderList(), res)) {
// don't add resources that have no parent in the online project
continue;
}
// check permissions
try {
m_securityManager.checkPermissions(
dbc,
res,
CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
false,
CmsResourceFilter.ALL);
} catch (CmsException e) {
// skip if not enough permissions
continue;
}
result.add(res);
} catch (Exception e) {
// should never happen
LOG.error(e.getLocalizedMessage(), e);
}
}
return result;
}
/**
* Returns the access control list of a given resource.<p>
*
* @param dbc the current database context
* @param resource the resource
* @param forFolder should be true if resource is a folder
* @param depth the depth to include non-inherited access entries, also
* @param inheritedOnly flag indicates to collect inherited permissions only
*
* @return the access control list of the resource
*
* @throws CmsException if something goes wrong
*/
private CmsAccessControlList getAccessControlList(
CmsDbContext dbc,
CmsResource resource,
boolean inheritedOnly,
boolean forFolder,
int depth) throws CmsException {
String cacheKey = getCacheKey(
new String[] {
inheritedOnly ? "+" : "-",
forFolder ? "+" : "-",
Integer.toString(depth),
resource.getStructureId().toString()},
dbc);
CmsAccessControlList acl = m_monitor.getCachedACL(cacheKey);
// return the cached acl if already available
if ((acl != null) && dbc.getProjectId().isNullUUID()) {
return acl;
}
List<CmsAccessControlEntry> aces = getUserDriver(dbc).readAccessControlEntries(
dbc,
dbc.currentProject(),
resource.getResourceId(),
(depth > 1) || ((depth > 0) && forFolder));
// sort the list of aces
boolean overwriteAll = sortAceList(aces);
// if no 'overwrite all' ace was found
if (!overwriteAll) {
// get the acl of the parent
CmsResource parentResource = null;
try {
// try to recurse over the id
parentResource = getVfsDriver(dbc).readParentFolder(
dbc,
dbc.currentProject().getUuid(),
resource.getStructureId());
} catch (CmsVfsResourceNotFoundException e) {
// should never happen, but try with the path
String parentPath = CmsResource.getParentFolder(resource.getRootPath());
if (parentPath != null) {
parentResource = getVfsDriver(dbc).readFolder(dbc, dbc.currentProject().getUuid(), parentPath);
}
}
if (parentResource != null) {
acl = (CmsAccessControlList)getAccessControlList(
dbc,
parentResource,
inheritedOnly,
forFolder,
depth + 1).clone();
}
}
if (acl == null) {
acl = new CmsAccessControlList();
}
if (!((depth == 0) && inheritedOnly)) {
Iterator<CmsAccessControlEntry> itAces = aces.iterator();
while (itAces.hasNext()) {
CmsAccessControlEntry acEntry = itAces.next();
if (depth > 0) {
acEntry.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED);
}
acl.add(acEntry);
// if the overwrite flag is set, reset the allowed permissions to the permissions of this entry
// denied permissions are kept or extended
if ((acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE) > 0) {
acl.setAllowedPermissions(acEntry);
}
}
}
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheACL(cacheKey, acl);
}
return acl;
}
/**
* Return a cache key build from the provided information.<p>
*
* @param prefix a prefix for the key
* @param flag a boolean flag for the key (only used if prefix is not null)
* @param projectId the project for which to generate the key
* @param resource the resource for which to generate the key
*
* @return String a cache key build from the provided information
*/
private String getCacheKey(String prefix, boolean flag, CmsUUID projectId, String resource) {
StringBuffer b = new StringBuffer(64);
if (prefix != null) {
b.append(prefix);
b.append(flag ? '+' : '-');
}
b.append(CmsProject.isOnlineProject(projectId) ? '+' : '-');
return b.append(resource).toString();
}
/**
* Return a cache key build from the provided information.<p>
*
* @param keys an array of keys to generate the cache key from
* @param dbc the database context for which to generate the key
*
* @return String a cache key build from the provided information
*/
private String getCacheKey(String[] keys, CmsDbContext dbc) {
if (!dbc.getProjectId().isNullUUID()) {
return "";
}
StringBuffer b = new StringBuffer(64);
int len = keys.length;
if (len > 0) {
for (int i = 0; i < len; i++) {
b.append(keys[i]);
b.append('_');
}
}
if (dbc.currentProject().isOnlineProject()) {
b.append("+");
} else {
b.append("-");
}
return b.toString();
}
/**
* Returns the correct project id.<p>
*
* @param dbc the database context
*
* @return the correct project id
*/
private CmsUUID getProjectIdForContext(CmsDbContext dbc) {
CmsUUID projectId = dbc.getProjectId();
if (projectId.isNullUUID()) {
projectId = dbc.currentProject().getUuid();
}
return projectId;
}
/**
* Returns if and what state needs to be updated.<p>
*
* @param dbc the db context
* @param resource the resource
* @param properties the properties to check
*
* @return 0: none, 1: structure, 2: resource
*
* @throws CmsDataAccessException if something goes wrong
*/
private int getUpdateState(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties)
throws CmsDataAccessException {
int updateState = 0;
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
Iterator<CmsProperty> it = properties.iterator();
while (it.hasNext() && (updateState < 2)) {
CmsProperty property = it.next();
// read existing property
CmsProperty existingProperty = vfsDriver.readPropertyObject(
dbc,
property.getName(),
dbc.currentProject(),
resource);
// check the shared property
if (property.getResourceValue() != null) {
if (property.isDeleteResourceValue()) {
if (existingProperty.getResourceValue() != null) {
updateState = 2; // deleted
}
} else {
if (existingProperty.getResourceValue() == null) {
updateState = 2; // created
} else {
if (!property.getResourceValue().equals(existingProperty.getResourceValue())) {
updateState = 2; // updated
}
}
}
}
if (updateState == 0) {
// check the individual property only if needed
if (property.getStructureValue() != null) {
if (property.isDeleteStructureValue()) {
if (existingProperty.getStructureValue() != null) {
updateState = 1; // deleted
}
} else {
if (existingProperty.getStructureValue() == null) {
updateState = 1; // created
} else {
if (!property.getStructureValue().equals(existingProperty.getStructureValue())) {
updateState = 1; // updated
}
}
}
}
}
}
return updateState;
}
/**
* Returns all groups that are virtualizing the given role in the given ou.<p>
*
* @param dbc the database context
* @param role the role
*
* @return all groups that are virtualizing the given role (or a child of it)
*
* @throws CmsException if something goes wrong
*/
private List<CmsGroup> getVirtualGroupsForRole(CmsDbContext dbc, CmsRole role) throws CmsException {
Set<Integer> roleFlags = new HashSet<Integer>();
// add role flag
Integer flags = new Integer(role.getVirtualGroupFlags());
roleFlags.add(flags);
// collect all child role flags
Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator();
while (itChildRoles.hasNext()) {
CmsRole child = itChildRoles.next();
flags = new Integer(child.getVirtualGroupFlags());
roleFlags.add(flags);
}
// iterate all groups matching the flags
List<CmsGroup> groups = new ArrayList<CmsGroup>();
Iterator<CmsGroup> it = getGroups(dbc, readOrganizationalUnit(dbc, role.getOuFqn()), false, false).iterator();
while (it.hasNext()) {
CmsGroup group = it.next();
if (group.isVirtual()) {
CmsRole r = CmsRole.valueOf(group);
if (roleFlags.contains(new Integer(r.getVirtualGroupFlags()))) {
groups.add(group);
}
}
}
return groups;
}
/**
* Returns a list of users in a group.<p>
*
* @param dbc the current database context
* @param ouFqn the organizational unit to get the users from
* @param groupname the name of the group to list users from
* @param includeOtherOuUsers include users of other organizational units
* @param directUsersOnly if set only the direct assigned users will be returned,
* if not also indirect users, ie. members of parent roles,
* this parameter only works with roles
* @param readRoles if to read roles or groups
*
* @return all <code>{@link CmsUser}</code> objects in the group
*
* @throws CmsException if operation was not successful
*/
private List<CmsUser> internalUsersOfGroup(
CmsDbContext dbc,
String ouFqn,
String groupname,
boolean includeOtherOuUsers,
boolean directUsersOnly,
boolean readRoles) throws CmsException {
CmsGroup group = readGroup(dbc, groupname); // check that the group really exists
if ((group == null) || (!((!readRoles && !group.isRole()) || (readRoles && group.isRole())))) {
throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
}
String prefix = "_" + includeOtherOuUsers + "_" + directUsersOnly + "_" + ouFqn;
String cacheKey = m_keyGenerator.getCacheKeyForGroupUsers(prefix, dbc, group);
List<CmsUser> allUsers = m_monitor.getCachedUserList(cacheKey);
if (allUsers == null) {
Set<CmsUser> users = new HashSet<CmsUser>(getUserDriver(dbc).readUsersOfGroup(
dbc,
groupname,
includeOtherOuUsers));
if (readRoles && !directUsersOnly) {
CmsRole role = CmsRole.valueOf(group);
if (role.getParentRole() != null) {
try {
String parentGroup = role.getParentRole().getGroupName();
readGroup(dbc, parentGroup);
// iterate the parent roles
users.addAll(internalUsersOfGroup(
dbc,
ouFqn,
parentGroup,
includeOtherOuUsers,
directUsersOnly,
readRoles));
} catch (CmsDbEntryNotFoundException e) {
// ignore, this may happen while deleting an orgunit
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn());
if (parentOu != null) {
// iterate the parent ou's
users.addAll(internalUsersOfGroup(
dbc,
ouFqn,
parentOu + group.getSimpleName(),
includeOtherOuUsers,
directUsersOnly,
readRoles));
}
} else if (!readRoles && !directUsersOnly) {
List<CmsGroup> groups = getChildren(dbc, group, false);
for (CmsGroup parentGroup : groups) {
try {
// iterate the parent groups
users.addAll(internalUsersOfGroup(
dbc,
ouFqn,
parentGroup.getName(),
includeOtherOuUsers,
directUsersOnly,
readRoles));
} catch (CmsDbEntryNotFoundException e) {
// ignore, this may happen while deleting an orgunit
if (LOG.isDebugEnabled()) {
LOG.debug(e.getLocalizedMessage(), e);
}
}
}
}
// filter users from other ous
if (!includeOtherOuUsers) {
Iterator<CmsUser> itUsers = users.iterator();
while (itUsers.hasNext()) {
CmsUser user = itUsers.next();
if (!user.getOuFqn().equals(ouFqn)) {
itUsers.remove();
}
}
}
// make user list unmodifiable for caching
allUsers = Collections.unmodifiableList(new ArrayList<CmsUser>(users));
if (dbc.getProjectId().isNullUUID()) {
m_monitor.cacheUserList(cacheKey, allUsers);
}
}
return allUsers;
}
/**
* Reads all resources that are inside and changed in a specified project.<p>
*
* @param dbc the current database context
* @param projectId the ID of the project
* @param mode one of the {@link CmsReadChangedProjectResourceMode} constants
*
* @return a List with all resources inside the specified project
*
* @throws CmsException if something goes wrong
*/
private List<CmsResource> readChangedResourcesInsideProject(
CmsDbContext dbc,
CmsUUID projectId,
CmsReadChangedProjectResourceMode mode) throws CmsException {
String cacheKey = projectId + "_" + mode.toString();
List<CmsResource> result = m_monitor.getCachedProjectResources(cacheKey);
if (result != null) {
return result;
}
List<String> projectResources = readProjectResources(dbc, readProject(dbc, projectId));
result = new ArrayList<CmsResource>();
String currentProjectResource = null;
List<CmsResource> resources = new ArrayList<CmsResource>();
CmsResource currentResource = null;
CmsLock currentLock = null;
for (int i = 0; i < projectResources.size(); i++) {
// read all resources that are inside the project by visiting each project resource
currentProjectResource = projectResources.get(i);
try {
currentResource = readResource(dbc, currentProjectResource, CmsResourceFilter.ALL);
if (currentResource.isFolder()) {
resources.addAll(readResources(dbc, currentResource, CmsResourceFilter.ALL, true));
} else {
resources.add(currentResource);
}
} catch (CmsException e) {
// the project resource probably doesn't exist (anymore)...
if (!(e instanceof CmsVfsResourceNotFoundException)) {
throw e;
}
}
}
for (int j = 0; j < resources.size(); j++) {
currentResource = resources.get(j);
currentLock = getLock(dbc, currentResource).getEditionLock();
if (!currentResource.getState().isUnchanged()) {
if ((currentLock.isNullLock() && (currentResource.getProjectLastModified().equals(projectId)))
|| (currentLock.isOwnedBy(dbc.currentUser()) && (currentLock.getProjectId().equals(projectId)))) {
// add only resources that are
// - inside the project,
// - changed in the project,
// - either unlocked, or locked for the current user in the project
if ((mode == RCPRM_FILES_AND_FOLDERS_MODE)
|| (currentResource.isFolder() && (mode == RCPRM_FOLDERS_ONLY_MODE))
|| (currentResource.isFile() && (mode == RCPRM_FILES_ONLY_MODE))) {
result.add(currentResource);
}
}
}
}
resources.clear();
resources = null;
m_monitor.cacheProjectResources(cacheKey, result);
return result;
}
/**
* Sorts the given list of {@link CmsAccessControlEntry} objects.<p>
*
* The the 'all others' ace in first place, the 'overwrite all' ace in second.<p>
*
* @param aces the list of ACEs to sort
*
* @return <code>true</code> if the list contains the 'overwrite all' ace
*/
private boolean sortAceList(List<CmsAccessControlEntry> aces) {
// sort the list of entries
Collections.sort(aces, CmsAccessControlEntry.COMPARATOR_ACE);
// after sorting just the first 2 positions come in question
for (int i = 0; i < Math.min(aces.size(), 2); i++) {
CmsAccessControlEntry acEntry = aces.get(i);
if (acEntry.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_ID)) {
return true;
}
}
return false;
}
/**
* All permissions and resources attributes of the principal
* are transfered to a replacement principal.<p>
*
* @param dbc the current database context
* @param project the current project
* @param principalId the id of the principal to be replaced
* @param replacementId the user to be transfered
* @param withACEs flag to signal if the ACEs should also be transfered or just deleted
*
* @throws CmsException if operation was not successful
*/
private void transferPrincipalResources(
CmsDbContext dbc,
CmsProject project,
CmsUUID principalId,
CmsUUID replacementId,
boolean withACEs) throws CmsException {
// get all resources for the given user including resources associated by ACEs or attributes
I_CmsUserDriver userDriver = getUserDriver(dbc);
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
Set<CmsResource> resources = getResourcesForPrincipal(dbc, project, principalId, null, true);
Iterator<CmsResource> it = resources.iterator();
while (it.hasNext()) {
CmsResource resource = it.next();
// check resource attributes
boolean attrModified = false;
CmsUUID createdUser = null;
if (resource.getUserCreated().equals(principalId)) {
createdUser = replacementId;
attrModified = true;
}
CmsUUID lastModUser = null;
if (resource.getUserLastModified().equals(principalId)) {
lastModUser = replacementId;
attrModified = true;
}
if (attrModified) {
vfsDriver.transferResource(dbc, project, resource, createdUser, lastModUser);
// clear the cache
m_monitor.clearResourceCache();
}
boolean aceModified = false;
// check aces
if (withACEs) {
Iterator<CmsAccessControlEntry> itAces = userDriver.readAccessControlEntries(
dbc,
project,
resource.getResourceId(),
false).iterator();
while (itAces.hasNext()) {
CmsAccessControlEntry ace = itAces.next();
if (ace.getPrincipal().equals(principalId)) {
CmsAccessControlEntry newAce = new CmsAccessControlEntry(
ace.getResource(),
replacementId,
ace.getAllowedPermissions(),
ace.getDeniedPermissions(),
ace.getFlags());
// write the new ace
userDriver.writeAccessControlEntry(dbc, project, newAce);
aceModified = true;
}
}
if (aceModified) {
// clear the cache
m_monitor.clearAccessControlListCache();
}
}
if (attrModified || aceModified) {
// fire the event
Map<String, Object> data = new HashMap<String, Object>(2);
data.put(I_CmsEventListener.KEY_RESOURCE, resource);
data.put(I_CmsEventListener.KEY_CHANGE, new Integer(((attrModified) ? CHANGED_RESOURCE : 0)
| ((aceModified) ? CHANGED_ACCESSCONTROL : 0)));
OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
}
}
}
/**
* Undoes all content changes of a resource.<p>
*
* @param dbc the database context
* @param onlineProject the online project
* @param offlineResource the offline resource, or <code>null</code> if deleted
* @param onlineResource the online resource
* @param newState the new resource state
* @param moveUndone is a move operation on the same resource has been made
*
* @throws CmsException if something goes wrong
*/
private void undoContentChanges(
CmsDbContext dbc,
CmsProject onlineProject,
CmsResource offlineResource,
CmsResource onlineResource,
CmsResourceState newState,
boolean moveUndone) throws CmsException {
String path = ((moveUndone || (offlineResource == null))
? onlineResource.getRootPath()
: offlineResource.getRootPath());
// change folder or file?
I_CmsUserDriver userDriver = getUserDriver(dbc);
I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
if (onlineResource.isFolder()) {
CmsFolder restoredFolder = new CmsFolder(
onlineResource.getStructureId(),
onlineResource.getResourceId(),
path,
onlineResource.getTypeId(),
onlineResource.getFlags(),
dbc.currentProject().getUuid(),
newState,
onlineResource.getDateCreated(),
onlineResource.getUserCreated(),
onlineResource.getDateLastModified(),
onlineResource.getUserLastModified(),
onlineResource.getDateReleased(),
onlineResource.getDateExpired(),
onlineResource.getVersion()); // version number does not matter since it will be computed later
// write the folder in the offline project
// this sets a flag so that the folder date is not set to the current time
restoredFolder.setDateLastModified(onlineResource.getDateLastModified());
// write the folder
vfsDriver.writeResource(dbc, dbc.currentProject().getUuid(), restoredFolder, NOTHING_CHANGED);
// restore the properties from the online project
vfsDriver.deletePropertyObjects(
dbc,
dbc.currentProject().getUuid(),
restoredFolder,
CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES);
List<CmsProperty> propertyInfos = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource);
vfsDriver.writePropertyObjects(dbc, dbc.currentProject(), restoredFolder, propertyInfos);
// restore the access control entries from the online project
userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId());
ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries(
dbc,
onlineProject,
onlineResource.getResourceId(),
false).listIterator();
while (aceList.hasNext()) {
CmsAccessControlEntry ace = aceList.next();
userDriver.createAccessControlEntry(
dbc,
dbc.currentProject(),
onlineResource.getResourceId(),
ace.getPrincipal(),
ace.getPermissions().getAllowedPermissions(),
ace.getPermissions().getDeniedPermissions(),
ace.getFlags());
}
} else {
byte[] onlineContent = vfsDriver.readContent(
dbc,
CmsProject.ONLINE_PROJECT_ID,
onlineResource.getResourceId());
CmsFile restoredFile = new CmsFile(
onlineResource.getStructureId(),
onlineResource.getResourceId(),
path,
onlineResource.getTypeId(),
onlineResource.getFlags(),
dbc.currentProject().getUuid(),
newState,
onlineResource.getDateCreated(),
onlineResource.getUserCreated(),
onlineResource.getDateLastModified(),
onlineResource.getUserLastModified(),
onlineResource.getDateReleased(),
onlineResource.getDateExpired(),
0,
onlineResource.getLength(),
onlineResource.getDateContent(),
onlineResource.getVersion(), // version number does not matter since it will be computed later
onlineContent);
// write the file in the offline project
// this sets a flag so that the file date is not set to the current time
restoredFile.setDateLastModified(onlineResource.getDateLastModified());
// collect the old properties
List<CmsProperty> properties = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource);
if (offlineResource != null) {
// bug fix 1020: delete all properties (included shared),
// shared properties will be recreated by the next call of #createResource(...)
vfsDriver.deletePropertyObjects(
dbc,
dbc.currentProject().getUuid(),
onlineResource,
CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES);
// implementation notes:
// undo changes can become complex e.g. if a resource was deleted, and then
// another resource was copied over the deleted file as a sibling
// therefore we must "clean" delete the offline resource, and then create
// an new resource with the create method
// note that this does NOT apply to folders, since a folder cannot be replaced
// like a resource anyway
deleteResource(dbc, offlineResource, CmsResource.DELETE_PRESERVE_SIBLINGS);
}
CmsResource res = createResource(
dbc,
restoredFile.getRootPath(),
restoredFile,
restoredFile.getContents(),
properties,
false);
// copy the access control entries from the online project
if (offlineResource != null) {
userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId());
}
ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries(
dbc,
onlineProject,
onlineResource.getResourceId(),
false).listIterator();
while (aceList.hasNext()) {
CmsAccessControlEntry ace = aceList.next();
userDriver.createAccessControlEntry(
dbc,
dbc.currentProject(),
res.getResourceId(),
ace.getPrincipal(),
ace.getPermissions().getAllowedPermissions(),
ace.getPermissions().getDeniedPermissions(),
ace.getFlags());
}
vfsDriver.deleteUrlNameMappingEntries(
dbc,
false,
CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterState(
CmsUrlNameMappingEntry.MAPPING_STATUS_NEW));
// restore the state to unchanged
res.setState(newState);
m_vfsDriver.writeResourceState(dbc, dbc.currentProject(), res, UPDATE_ALL, false);
}
// delete all offline relations
if (offlineResource != null) {
vfsDriver.deleteRelations(dbc, dbc.currentProject().getUuid(), offlineResource, CmsRelationFilter.TARGETS);
}
// get online relations
List<CmsRelation> relations = vfsDriver.readRelations(
dbc,
CmsProject.ONLINE_PROJECT_ID,
onlineResource,
CmsRelationFilter.TARGETS);
// write offline relations
Iterator<CmsRelation> itRelations = relations.iterator();
while (itRelations.hasNext()) {
CmsRelation relation = itRelations.next();
vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation);
}
// update the cache
m_monitor.clearResourceCache();
m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
if ((offlineResource == null) || offlineResource.getRootPath().equals(onlineResource.getRootPath())) {
log(dbc, new CmsLogEntry(
dbc,
onlineResource.getStructureId(),
CmsLogEntryType.RESOURCE_RESTORED,
new String[] {onlineResource.getRootPath()}), false);
} else {
log(dbc, new CmsLogEntry(
dbc,
offlineResource.getStructureId(),
CmsLogEntryType.RESOURCE_MOVE_RESTORED,
new String[] {offlineResource.getRootPath(), onlineResource.getRootPath()}), false);
}
if (offlineResource != null) {
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, offlineResource)));
} else {
OpenCms.fireCmsEvent(new CmsEvent(
I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, onlineResource)));
}
}
/**
* Updates the current users context dates with the given resource.<p>
*
* This checks the date information of the resource based on
* {@link CmsResource#getDateLastModified()} as well as
* {@link CmsResource#getDateReleased()} and {@link CmsResource#getDateExpired()}.
* The current users request context is updated with the the "latest" dates found.<p>
*
* This is required in order to ensure proper setting of <code>"last-modified"</code> http headers
* and also for expiration of cached elements in the Flex cache.
* Consider the following use case: Page A is generated from resources x, y and z.
* If either x, y or z has an expiration / release date set, then page A must expire at a certain point
* in time. This is ensured by the context date check here.<p>
*
* @param dbc the current database context
* @param resource the resource to get the date information from
*/
private void updateContextDates(CmsDbContext dbc, CmsResource resource) {
CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo();
if (info != null) {
info.updateFromResource(resource);
}
}
/**
* Updates the current users context dates with each {@link CmsResource} object in the given list.<p>
*
* The given input list is returned unmodified.<p>
*
* Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p>
*
* @param dbc the current database context
* @param resourceList a list of {@link CmsResource} objects
*
* @return the original list of CmsResources with the full resource name set
*/
private List<CmsResource> updateContextDates(CmsDbContext dbc, List<CmsResource> resourceList) {
CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo();
if (info != null) {
for (int i = 0; i < resourceList.size(); i++) {
CmsResource resource = resourceList.get(i);
info.updateFromResource(resource);
}
}
return resourceList;
}
/**
* Returns a List of {@link CmsResource} objects generated when applying the given filter to the given list,
* also updates the current users context dates with each {@link CmsResource} object in the given list,
* also applies the selected resource filter to all resources in the list and returns the remaining resources.<p>
*
* Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p>
*
* @param dbc the current database context
* @param resourceList a list of {@link CmsResource} objects
* @param filter the resource filter to use
*
* @return a List of {@link CmsResource} objects generated when applying the given filter to the given list
*/
private List<CmsResource> updateContextDates(
CmsDbContext dbc,
List<CmsResource> resourceList,
CmsResourceFilter filter) {
if (CmsResourceFilter.ALL == filter) {
// if there is no filter required, then use the simpler method that does not apply the filter
return new ArrayList<CmsResource>(updateContextDates(dbc, resourceList));
}
CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo();
List<CmsResource> result = new ArrayList<CmsResource>(resourceList.size());
for (int i = 0; i < resourceList.size(); i++) {
CmsResource resource = resourceList.get(i);
if (filter.isValid(dbc.getRequestContext(), resource)) {
result.add(resource);
}
// must also include "invalid" resources for the update of context dates
// since a resource may be invalid because of release / expiration date
if (info != null) {
info.updateFromResource(resource);
}
}
return result;
}
/**
* Updates the state of a resource, depending on the <code>resourceState</code> parameter.<p>
*
* @param dbc the db context
* @param resource the resource
* @param resourceState if <code>true</code> the resource state will be updated, if not just the structure state.
*
* @throws CmsDataAccessException if something goes wrong
*/
private void updateState(CmsDbContext dbc, CmsResource resource, boolean resourceState)
throws CmsDataAccessException {
CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID())
? dbc.currentProject().getUuid()
: dbc.getProjectId();
resource.setUserLastModified(dbc.currentUser().getId());
if (resourceState) {
// update the whole resource state
getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE);
} else {
// update the structure state
getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_STRUCTURE_STATE);
}
}
}