001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.db;
029
030import org.opencms.ade.publish.CmsTooManyPublishResourcesException;
031import org.opencms.configuration.CmsConfigurationManager;
032import org.opencms.configuration.CmsParameterConfiguration;
033import org.opencms.configuration.CmsSystemConfiguration;
034import org.opencms.db.generic.CmsPublishHistoryCleanupFilter;
035import org.opencms.db.generic.CmsUserDriver;
036import org.opencms.db.log.CmsLogEntry;
037import org.opencms.db.log.CmsLogEntryType;
038import org.opencms.db.log.CmsLogFilter;
039import org.opencms.db.timing.CmsDefaultProfilingHandler;
040import org.opencms.db.timing.CmsProfilingInvocationHandler;
041import org.opencms.db.urlname.CmsUrlNameMappingEntry;
042import org.opencms.db.urlname.CmsUrlNameMappingFilter;
043import org.opencms.db.userpublishlist.A_CmsLogPublishListConverter;
044import org.opencms.db.userpublishlist.CmsLogPublishListConverterAllUsers;
045import org.opencms.db.userpublishlist.CmsLogPublishListConverterCurrentUser;
046import org.opencms.file.CmsDataAccessException;
047import org.opencms.file.CmsFile;
048import org.opencms.file.CmsFolder;
049import org.opencms.file.CmsGroup;
050import org.opencms.file.CmsObject;
051import org.opencms.file.CmsProject;
052import org.opencms.file.CmsProperty;
053import org.opencms.file.CmsPropertyDefinition;
054import org.opencms.file.CmsRequestContext;
055import org.opencms.file.CmsResource;
056import org.opencms.file.CmsResourceFilter;
057import org.opencms.file.CmsUser;
058import org.opencms.file.CmsUserSearchParameters;
059import org.opencms.file.CmsVfsException;
060import org.opencms.file.CmsVfsResourceAlreadyExistsException;
061import org.opencms.file.CmsVfsResourceNotFoundException;
062import org.opencms.file.I_CmsResource;
063import org.opencms.file.history.CmsHistoryFile;
064import org.opencms.file.history.CmsHistoryFolder;
065import org.opencms.file.history.CmsHistoryPrincipal;
066import org.opencms.file.history.CmsHistoryProject;
067import org.opencms.file.history.I_CmsHistoryResource;
068import org.opencms.file.types.CmsResourceTypeFolder;
069import org.opencms.file.types.CmsResourceTypeJsp;
070import org.opencms.file.types.I_CmsResourceType;
071import org.opencms.flex.CmsFlexRequestContextInfo;
072import org.opencms.gwt.shared.alias.CmsAliasImportResult;
073import org.opencms.gwt.shared.alias.CmsAliasImportStatus;
074import org.opencms.gwt.shared.alias.CmsAliasMode;
075import org.opencms.i18n.CmsLocaleManager;
076import org.opencms.i18n.CmsMessageContainer;
077import org.opencms.jsp.CmsJspNavBuilder;
078import org.opencms.lock.CmsLock;
079import org.opencms.lock.CmsLockException;
080import org.opencms.lock.CmsLockFilter;
081import org.opencms.lock.CmsLockManager;
082import org.opencms.lock.CmsLockType;
083import org.opencms.main.CmsEvent;
084import org.opencms.main.CmsException;
085import org.opencms.main.CmsIllegalArgumentException;
086import org.opencms.main.CmsIllegalStateException;
087import org.opencms.main.CmsInitException;
088import org.opencms.main.CmsLog;
089import org.opencms.main.CmsMultiException;
090import org.opencms.main.I_CmsEventListener;
091import org.opencms.main.OpenCms;
092import org.opencms.module.CmsModule;
093import org.opencms.monitor.CmsMemoryMonitor;
094import org.opencms.monitor.CmsMemoryMonitor.CacheType;
095import org.opencms.publish.CmsPublishEngine;
096import org.opencms.publish.CmsPublishJobInfoBean;
097import org.opencms.publish.CmsPublishReport;
098import org.opencms.relations.CmsCategoryService;
099import org.opencms.relations.CmsLink;
100import org.opencms.relations.CmsRelation;
101import org.opencms.relations.CmsRelationFilter;
102import org.opencms.relations.CmsRelationSystemValidator;
103import org.opencms.relations.CmsRelationType;
104import org.opencms.relations.CmsRelationType.CopyBehavior;
105import org.opencms.relations.I_CmsLinkParseable;
106import org.opencms.report.CmsLogReport;
107import org.opencms.report.I_CmsReport;
108import org.opencms.security.CmsAccessControlEntry;
109import org.opencms.security.CmsAccessControlList;
110import org.opencms.security.CmsAuthentificationException;
111import org.opencms.security.CmsOrganizationalUnit;
112import org.opencms.security.CmsPasswordEncryptionException;
113import org.opencms.security.CmsPermissionSet;
114import org.opencms.security.CmsPermissionSetCustom;
115import org.opencms.security.CmsPrincipal;
116import org.opencms.security.CmsRole;
117import org.opencms.security.CmsSecurityException;
118import org.opencms.security.I_CmsPermissionHandler;
119import org.opencms.security.I_CmsPermissionHandler.LockCheck;
120import org.opencms.security.I_CmsPrincipal;
121import org.opencms.site.CmsSiteMatcher;
122import org.opencms.util.CmsFileUtil;
123import org.opencms.util.CmsPath;
124import org.opencms.util.CmsStringUtil;
125import org.opencms.util.CmsUUID;
126import org.opencms.util.PrintfFormat;
127import org.opencms.workflow.CmsDefaultWorkflowManager;
128import org.opencms.workplace.threads.A_CmsProgressThread;
129
130import java.lang.reflect.Proxy;
131import java.util.ArrayList;
132import java.util.Collection;
133import java.util.Collections;
134import java.util.Comparator;
135import java.util.Date;
136import java.util.HashMap;
137import java.util.HashSet;
138import java.util.Iterator;
139import java.util.List;
140import java.util.ListIterator;
141import java.util.Locale;
142import java.util.Map;
143import java.util.Map.Entry;
144import java.util.Set;
145import java.util.TreeSet;
146import java.util.concurrent.ConcurrentMap;
147import java.util.concurrent.ExecutionException;
148import java.util.function.Predicate;
149import java.util.regex.Pattern;
150import java.util.regex.PatternSyntaxException;
151import java.util.stream.Collectors;
152
153import org.apache.commons.logging.Log;
154
155import com.google.common.collect.ArrayListMultimap;
156import com.google.common.collect.Maps;
157import com.google.common.collect.Multimap;
158
159/**
160 * The OpenCms driver manager.<p>
161 *
162 * @since 6.0.0
163 */
164public final class CmsDriverManager implements I_CmsEventListener {
165
166    /**
167     * Special key class for caching the resource OU data with a Guava LoadingCache.<p>
168     *
169     * In principle, the actual cache key is just the current project, but because of how cache loaders work,
170     * the key must contain everything that varies between calls and is required to load the value. So we also store the DB context
171     * for use by the cache loader. The project (offline/online) must still be stored, because the DB context gets invalidated
172     * eventually, i.e. its project id gets nulled.
173     */
174    public static class ResourceOUCacheKey {
175
176        /** The DB context. */
177        private CmsDbContext m_dbc;
178
179        /** The actual cache key. */
180        private String m_actualKey;
181
182        /** The driver manager to use. */
183        private CmsDriverManager m_driverManager;
184
185        /**
186         * Creates a new instance.
187         *
188         * @param driverManager the driver manager to use
189         * @param dbc the current DB context
190         */
191        public ResourceOUCacheKey(CmsDriverManager driverManager, CmsDbContext dbc) {
192
193            m_dbc = dbc;
194            m_driverManager = driverManager;
195            m_actualKey = CmsProject.ONLINE_PROJECT_ID.equals(dbc.currentProject().getId()) ? "ONLINE" : "OFFLINE";
196        }
197
198        /**
199         * @see java.lang.Object#equals(java.lang.Object)
200         */
201        @Override
202        public boolean equals(Object obj) {
203
204            return (obj instanceof ResourceOUCacheKey)
205                && ((ResourceOUCacheKey)obj).getActualKey().equals(getActualKey());
206        }
207
208        /**
209         * Gets the stored DB context.<p>
210         *
211         * Note that the DB contex returned by this may have been invalidated!
212         *
213         * @return the stored DB context
214         */
215        public CmsDbContext getDbContext() {
216
217            return m_dbc;
218        }
219
220        /**
221         * Gets the current driver manager.
222         *
223         * @return the driver manager to use
224         **/
225        public CmsDriverManager getDriverManager() {
226
227            return m_driverManager;
228        }
229
230        /**
231         * @see java.lang.Object#hashCode()
232         */
233        @Override
234        public int hashCode() {
235
236            return getActualKey().hashCode();
237        }
238
239        /**
240         * Gets the actual key data.
241         *
242         * @return the actual key data
243         */
244        private String getActualKey() {
245
246            return m_actualKey;
247        }
248
249    }
250
251    /**
252     * Helper class used to store information about resources assigned to OUs in a cache.
253     */
254    public static class ResourceOUMap {
255
256        /** The organizational units, with their UUIDs as keys. */
257        private Map<CmsUUID, CmsOrganizationalUnit> m_ousById = new HashMap<>();
258
259        /** Multimap from the paths of resources to the OUs to which they are assigned as OU resources. */
260        private Multimap<CmsPath, CmsOrganizationalUnit> m_ousByAssignedResourcePaths = ArrayListMultimap.create();
261
262        /**
263         * Gets the list of organizational units to which a given root path belongs, according to the cached
264         * OU resource assignments.
265         *
266         * @param rootPath the root path
267         * @return the organizational units to which the path belongs
268         */
269        public List<CmsOrganizationalUnit> getResourceOrgUnits(String rootPath) {
270
271            Set<CmsOrganizationalUnit> result = new HashSet<>();
272            String currentPath = rootPath;
273            while (currentPath != null) {
274                result.addAll(m_ousByAssignedResourcePaths.get(new CmsPath(currentPath)));
275                currentPath = CmsResource.getParentFolder(currentPath);
276            }
277            return new ArrayList<>(result);
278        }
279
280        /**
281         * Reads the OU resource data from the VFS and initializes this instance with it.
282         *
283         * @param driverManager the driver manager to use
284         * @param dbc the current DB context
285         * @throws CmsException if something goes wrong
286         */
287        public void init(CmsDriverManager driverManager, CmsDbContext dbc) throws CmsException {
288
289            List<CmsRelation> relations = driverManager.getRelationsForResource(
290                dbc,
291                null,
292                CmsRelationFilter.ALL.filterType(CmsRelationType.OU_RESOURCE));
293            CmsOrganizationalUnit root = driverManager.readOrganizationalUnit(dbc, "");
294            List<CmsOrganizationalUnit> children = driverManager.getOrganizationalUnits(dbc, root, true);
295
296            Set<CmsOrganizationalUnit> ous = new HashSet<>();
297            ous.add(root);
298            ous.addAll(children);
299            init(relations, ous);
300
301        }
302
303        /**
304         * Initializes the OU resource data.
305         *
306         * @param ouRelations the current list of OU relations
307         * @param ous the current list of OUs
308         */
309        public void init(Collection<CmsRelation> ouRelations, Collection<CmsOrganizationalUnit> ous) {
310
311            m_ousById.clear();
312            m_ousByAssignedResourcePaths.clear();
313            for (CmsOrganizationalUnit ou : ous) {
314                m_ousById.put(ou.getId(), ou);
315            }
316            for (CmsRelation rel : ouRelations) {
317                CmsOrganizationalUnit ou = m_ousById.get(rel.getSourceId());
318                if (ou != null) {
319                    m_ousByAssignedResourcePaths.put(new CmsPath(rel.getTargetPath()), ou);
320                }
321            }
322        }
323    }
324
325    /**
326     * The comparator used for comparing url name mapping entries by date.<p>
327     */
328    class UrlNameMappingComparator implements Comparator<CmsUrlNameMappingEntry> {
329
330        /**
331         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
332         */
333        public int compare(CmsUrlNameMappingEntry o1, CmsUrlNameMappingEntry o2) {
334
335            long date1 = o1.getDateChanged();
336            long date2 = o2.getDateChanged();
337            if (date1 < date2) {
338                return -1;
339            }
340            if (date1 > date2) {
341                return +1;
342            }
343            return 0;
344        }
345    }
346
347    /**
348     * Enumeration class for the mode parameter in the
349     * {@link CmsDriverManager#readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}
350     * method.<p>
351     */
352    private static class CmsReadChangedProjectResourceMode {
353
354        /**
355         * Default constructor.<p>
356         */
357        protected CmsReadChangedProjectResourceMode() {
358
359            // noop
360        }
361    }
362
363    /** Attribute for signaling to the user driver that a specific OU should be initialized by fillDefaults. */
364    public static final String ATTR_INIT_OU = "INIT_OU";
365
366    /** Attribute login. */
367    public static final String ATTRIBUTE_LOGIN = "A_LOGIN";
368
369    /** Cache key for all properties. */
370    public static final String CACHE_ALL_PROPERTIES = "_CAP_";
371
372    /**
373     * Values indicating changes of a resource,
374     * ordered according to the scope of the change.
375     */
376    /** Value to indicate a change in access control entries of a resource. */
377    public static final int CHANGED_ACCESSCONTROL = 1;
378
379    /** Value to indicate a content change. */
380    public static final int CHANGED_CONTENT = 16;
381
382    /** Value to indicate a change in the lastmodified settings of a resource. */
383    public static final int CHANGED_LASTMODIFIED = 4;
384
385    /** Value to indicate a project change. */
386    public static final int CHANGED_PROJECT = 32;
387
388    /** Value to indicate a change in the resource data. */
389    public static final int CHANGED_RESOURCE = 8;
390
391    /** Value to indicate a change in the availability timeframe. */
392    public static final int CHANGED_TIMEFRAME = 2;
393
394    /** "cache" string in the configuration-file. */
395    public static final String CONFIGURATION_CACHE = "cache";
396
397    /** "db" string in the configuration-file. */
398    public static final String CONFIGURATION_DB = "db";
399
400    /** "driver.history" string in the configuration-file. */
401    public static final String CONFIGURATION_HISTORY = "driver.history";
402
403    /** "driver.project" string in the configuration-file. */
404    public static final String CONFIGURATION_PROJECT = "driver.project";
405
406    /** "subscription.vfs" string in the configuration file. */
407    public static final String CONFIGURATION_SUBSCRIPTION = "driver.subscription";
408
409    /** "driver.user" string in the configuration-file. */
410    public static final String CONFIGURATION_USER = "driver.user";
411
412    /** "driver.vfs" string in the configuration-file. */
413    public static final String CONFIGURATION_VFS = "driver.vfs";
414
415    /** DBC attribute key needed to fix publishing behavior involving siblings. */
416    public static final String KEY_CHANGED_AND_DELETED = "changedAndDeleted";
417
418    /** The vfs path of the loast and found folder. */
419    public static final String LOST_AND_FOUND_FOLDER = "/system/lost-found";
420
421    /** The maximum length of a VFS resource path. */
422    public static final int MAX_VFS_RESOURCE_PATH_LENGTH = 512;
423
424    /** Key for indicating no changes. */
425    public static final int NOTHING_CHANGED = 0;
426
427    /** Name of the configuration parameter to enable/disable logging to the CMS_LOG table. */
428    public static final String PARAM_LOG_TABLE_ENABLED = "log.table.enabled";
429
430    /** Indicates to ignore the resource path when matching resources. */
431    public static final String READ_IGNORE_PARENT = null;
432
433    /** Indicates to ignore the time value. */
434    public static final long READ_IGNORE_TIME = 0L;
435
436    /** Indicates to ignore the resource type when matching resources. */
437    public static final int READ_IGNORE_TYPE = -1;
438
439    /** Indicates to match resources NOT having the given state. */
440    public static final int READMODE_EXCLUDE_STATE = 8;
441
442    /** Indicates to match immediate children only. */
443    public static final int READMODE_EXCLUDE_TREE = 1;
444
445    /** Indicates to match resources NOT having the given type. */
446    public static final int READMODE_EXCLUDE_TYPE = 4;
447
448    /** Mode for reading project resources from the db. */
449    public static final int READMODE_IGNORESTATE = 0;
450
451    /** Indicates to match resources in given project only. */
452    public static final int READMODE_INCLUDE_PROJECT = 2;
453
454    /** Indicates to match all successors. */
455    public static final int READMODE_INCLUDE_TREE = 0;
456
457    /** Mode for reading project resources from the db. */
458    public static final int READMODE_MATCHSTATE = 1;
459
460    /** Indicates if only file resources should be read. */
461    public static final int READMODE_ONLY_FILES = 128;
462
463    /** Indicates if only folder resources should be read. */
464    public static final int READMODE_ONLY_FOLDERS = 64;
465
466    /** Mode for reading project resources from the db. */
467    public static final int READMODE_UNMATCHSTATE = 2;
468
469    /** Prefix char for temporary files in the VFS. */
470    public static final String TEMP_FILE_PREFIX = "~";
471
472    /** Key to indicate complete update. */
473    public static final int UPDATE_ALL = 3;
474
475    /** Key to indicate update of resource record. */
476    public static final int UPDATE_RESOURCE = 4;
477
478    /** Key to indicate update of last modified project reference. */
479    public static final int UPDATE_RESOURCE_PROJECT = 6;
480
481    /** Key to indicate update of resource state. */
482    public static final int UPDATE_RESOURCE_STATE = 1;
483
484    /** Key to indicate update of resource state including the content date. */
485    public static final int UPDATE_RESOURCE_STATE_CONTENT = 7;
486
487    /** Key to indicate update of structure record. */
488    public static final int UPDATE_STRUCTURE = 5;
489
490    /** Key to indicate update of structure state. */
491    public static final int UPDATE_STRUCTURE_STATE = 2;
492
493    /** Map of pools defined in opencms.properties. */
494    protected static ConcurrentMap<String, CmsDbPoolV11> m_pools = Maps.newConcurrentMap();
495
496    /** The log object for this class. */
497    private static final Log LOG = CmsLog.getLog(CmsDriverManager.class);
498
499    /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */
500    private static final CmsReadChangedProjectResourceMode RCPRM_FILES_AND_FOLDERS_MODE = new CmsReadChangedProjectResourceMode();
501
502    /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */
503    private static final CmsReadChangedProjectResourceMode RCPRM_FILES_ONLY_MODE = new CmsReadChangedProjectResourceMode();
504
505    /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */
506    private static final CmsReadChangedProjectResourceMode RCPRM_FOLDERS_ONLY_MODE = new CmsReadChangedProjectResourceMode();
507
508    /** Flag that can be used to disable the resource OU caching if necessary. */
509    public static boolean resourceOrgUnitCachingEnabled = true;
510
511    /** The history driver. */
512    private I_CmsHistoryDriver m_historyDriver;
513
514    /** The HTML link validator. */
515    private CmsRelationSystemValidator m_htmlLinkValidator;
516
517    /** The class used for cache key generation. */
518    private I_CmsCacheKey m_keyGenerator;
519
520    /** The lock manager. */
521    private CmsLockManager m_lockManager;
522
523    /** The log entry cache. */
524    private List<CmsLogEntry> m_log = new ArrayList<CmsLogEntry>();
525
526    /** Local reference to the memory monitor to avoid multiple lookups through the OpenCms singleton. */
527    private CmsMemoryMonitor m_monitor;
528
529    /** The project driver. */
530    private I_CmsProjectDriver m_projectDriver;
531
532    /** The the configuration read from the <code>opencms.properties</code> file. */
533    private CmsParameterConfiguration m_propertyConfiguration;
534
535    /** the publish engine. */
536    private CmsPublishEngine m_publishEngine;
537
538    /** Object used for synchronizing updates to the user publish list. */
539    private Object m_publishListUpdateLock = new Object();
540
541    /** The security manager (for access checks). */
542    private CmsSecurityManager m_securityManager;
543
544    /** The sql manager. */
545    private CmsSqlManager m_sqlManager;
546
547    /** The subscription driver. */
548    private I_CmsSubscriptionDriver m_subscriptionDriver;
549
550    /** The user driver. */
551    private I_CmsUserDriver m_userDriver;
552
553    /** The VFS driver. */
554    private I_CmsVfsDriver m_vfsDriver;
555
556    /**
557     * Private constructor, initializes some required member variables.<p>
558     */
559    private CmsDriverManager() {
560
561        // intentionally left blank
562    }
563
564    /**
565     * Reads the required configurations from the opencms.properties file and creates
566     * the various drivers to access the cms resources.<p>
567     *
568     * The initialization process of the driver manager and its drivers is split into
569     * the following phases:
570     * <ul>
571     * <li>the database pool configuration is read</li>
572     * <li>a plain and empty driver manager instance is created</li>
573     * <li>an instance of each driver is created</li>
574     * <li>the driver manager is passed to each driver during initialization</li>
575     * <li>finally, the driver instances are passed to the driver manager during initialization</li>
576     * </ul>
577     *
578     * @param configurationManager the configuration manager
579     * @param securityManager the security manager
580     * @param runtimeInfoFactory the initialized OpenCms runtime info factory
581     * @param publishEngine the publish engine
582     *
583     * @return CmsDriverManager the instantiated driver manager
584     * @throws CmsInitException if the driver manager couldn't be instantiated
585     */
586    public static CmsDriverManager newInstance(
587        CmsConfigurationManager configurationManager,
588        CmsSecurityManager securityManager,
589        I_CmsDbContextFactory runtimeInfoFactory,
590        CmsPublishEngine publishEngine)
591    throws CmsInitException {
592
593        // read the opencms.properties from the configuration
594        CmsParameterConfiguration config = configurationManager.getConfiguration();
595
596        CmsDriverManager driverManager = null;
597        try {
598            // create a driver manager instance
599            driverManager = new CmsDriverManager();
600            if (CmsLog.INIT.isInfoEnabled()) {
601                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE1_0));
602            }
603            if (runtimeInfoFactory == null) {
604                throw new CmsInitException(
605                    org.opencms.main.Messages.get().container(org.opencms.main.Messages.ERR_CRITICAL_NO_DB_CONTEXT_0));
606            }
607        } catch (Exception exc) {
608            CmsMessageContainer message = Messages.get().container(Messages.LOG_ERR_DRIVER_MANAGER_START_0);
609            if (LOG.isFatalEnabled()) {
610                LOG.fatal(message.key(), exc);
611            }
612            throw new CmsInitException(message, exc);
613        }
614
615        // store the configuration
616        driverManager.m_propertyConfiguration = config;
617
618        // set the security manager
619        driverManager.m_securityManager = securityManager;
620
621        // set the lock manager
622        driverManager.m_lockManager = new CmsLockManager(driverManager);
623
624        // create and set the sql manager
625        driverManager.m_sqlManager = new CmsSqlManager(driverManager);
626
627        // set the publish engine
628        driverManager.m_publishEngine = publishEngine;
629
630        if (CmsLog.INIT.isInfoEnabled()) {
631            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE2_0));
632        }
633
634        // read the pool names to initialize
635        List<String> driverPoolNames = config.getList(CmsDriverManager.CONFIGURATION_DB + ".pools");
636        if (CmsLog.INIT.isInfoEnabled()) {
637            String names = "";
638            for (String name : driverPoolNames) {
639                names += name + " ";
640            }
641            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_POOLS_1, names));
642        }
643
644        // initialize each pool
645        for (String name : driverPoolNames) {
646            driverManager.newPoolInstance(config, name);
647        }
648
649        // initialize the runtime info factory with the generated driver manager
650        runtimeInfoFactory.initialize(driverManager);
651
652        if (CmsLog.INIT.isInfoEnabled()) {
653            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE3_0));
654        }
655
656        // store the access objects
657        CmsDbContext dbc = runtimeInfoFactory.getDbContext();
658        driverManager.m_vfsDriver = (I_CmsVfsDriver)driverManager.createDriver(
659            dbc,
660            configurationManager,
661            config,
662            CONFIGURATION_VFS,
663            ".vfs.driver");
664        dbc.clear();
665
666        dbc = runtimeInfoFactory.getDbContext();
667        driverManager.m_userDriver = (I_CmsUserDriver)driverManager.createDriver(
668            dbc,
669            configurationManager,
670            config,
671            CONFIGURATION_USER,
672            ".user.driver");
673        dbc.clear();
674
675        dbc = runtimeInfoFactory.getDbContext();
676        driverManager.m_projectDriver = (I_CmsProjectDriver)driverManager.createDriver(
677            dbc,
678            configurationManager,
679            config,
680            CONFIGURATION_PROJECT,
681            ".project.driver");
682        dbc.clear();
683
684        dbc = runtimeInfoFactory.getDbContext();
685        driverManager.m_historyDriver = (I_CmsHistoryDriver)driverManager.createDriver(
686            dbc,
687            configurationManager,
688            config,
689            CONFIGURATION_HISTORY,
690            ".history.driver");
691        dbc.clear();
692
693        dbc = runtimeInfoFactory.getDbContext();
694        try {
695            // we wrap this in a try-catch because otherwise it would fail during the update
696            // process, since the subscription driver configuration does not exist at that point.
697            driverManager.m_subscriptionDriver = (I_CmsSubscriptionDriver)driverManager.createDriver(
698                dbc,
699                configurationManager,
700                config,
701                CONFIGURATION_SUBSCRIPTION,
702                ".subscription.driver");
703        } catch (IndexOutOfBoundsException npe) {
704            LOG.warn("Could not instantiate subscription driver!");
705            LOG.warn(npe.getLocalizedMessage(), npe);
706        }
707        dbc.clear();
708
709        // register the driver manager for required events
710        org.opencms.main.OpenCms.addCmsEventListener(
711            driverManager,
712            new int[] {
713                I_CmsEventListener.EVENT_UPDATE_EXPORTS,
714                I_CmsEventListener.EVENT_CLEAR_CACHES,
715                I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES,
716                I_CmsEventListener.EVENT_USER_MODIFIED,
717                I_CmsEventListener.EVENT_PUBLISH_PROJECT});
718
719        // return the configured driver manager
720        return driverManager;
721    }
722
723    /**
724     * Adds an alias entry.<p>
725     *
726     * @param dbc the database context
727     * @param project the current project
728     * @param alias the alias to add
729     *
730     * @throws CmsException if something goes wrong
731     */
732    public void addAlias(CmsDbContext dbc, CmsProject project, CmsAlias alias) throws CmsException {
733
734        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
735        vfsDriver.insertAlias(dbc, project, alias);
736    }
737
738    /**
739     * Adds a new relation to the given resource.<p>
740     *
741     * @param dbc the database context
742     * @param resource the resource to add the relation to
743     * @param target the target of the relation
744     * @param type the type of the relation
745     * @param importCase if importing relations
746     *
747     * @throws CmsException if something goes wrong
748     */
749    public void addRelationToResource(
750        CmsDbContext dbc,
751        CmsResource resource,
752        CmsResource target,
753        CmsRelationType type,
754        boolean importCase)
755    throws CmsException {
756
757        if (type.isDefinedInContent()) {
758            throw new CmsIllegalArgumentException(
759                Messages.get().container(
760                    Messages.ERR_ADD_RELATION_IN_CONTENT_3,
761                    dbc.removeSiteRoot(resource.getRootPath()),
762                    dbc.removeSiteRoot(target.getRootPath()),
763                    type.getLocalizedName(dbc.getRequestContext().getLocale())));
764        }
765        CmsRelation relation = new CmsRelation(resource, target, type);
766        getVfsDriver(dbc).createRelation(dbc, dbc.currentProject().getUuid(), relation);
767        if (!importCase) {
768            // log it
769            log(
770                dbc,
771                new CmsLogEntry(
772                    dbc,
773                    resource.getStructureId(),
774                    CmsLogEntryType.RESOURCE_ADD_RELATION,
775                    new String[] {relation.getSourcePath(), relation.getTargetPath()}),
776                false);
777            // touch the resource
778            setDateLastModified(dbc, resource, System.currentTimeMillis());
779        }
780    }
781
782    /**
783     * Adds a resource to the given organizational unit.<p>
784     *
785     * @param dbc the current db context
786     * @param orgUnit the organizational unit to add the resource to
787     * @param resource the resource that is to be added to the organizational unit
788     *
789     * @throws CmsException if something goes wrong
790     *
791     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
792     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
793     */
794    public void addResourceToOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource)
795    throws CmsException {
796
797        m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
798        getUserDriver(dbc).addResourceToOrganizationalUnit(dbc, orgUnit, resource);
799    }
800
801    /**
802     * Adds a user to a group.<p>
803     *
804     * @param dbc the current database context
805     * @param username the name of the user that is to be added to the group
806     * @param groupname the name of the group
807     * @param readRoles if reading roles or groups
808     *
809     * @throws CmsException if operation was not successful
810     * @throws CmsDbEntryNotFoundException if the given user or the given group was not found
811     *
812     * @see #removeUserFromGroup(CmsDbContext, String, String, boolean)
813     */
814    public void addUserToGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles)
815    throws CmsException, CmsDbEntryNotFoundException {
816
817        //check if group exists
818        CmsGroup group = readGroup(dbc, groupname);
819        if (group == null) {
820            // the group does not exists
821            throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
822        }
823        if (group.isVirtual() && !readRoles) {
824            String roleName = CmsRole.valueOf(group).getGroupName();
825            if (!userInGroup(dbc, username, roleName, true)) {
826                addUserToGroup(dbc, username, roleName, true);
827                return;
828            }
829        }
830        if (group.isVirtual()) {
831            // this is an hack to prevent unlimited recursive calls
832            readRoles = false;
833        }
834        if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) {
835            // we want a role but we got a group, or the other way
836            throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
837        }
838        if (userInGroup(dbc, username, groupname, readRoles)) {
839            // the user is already member of the group
840            return;
841        }
842        //check if the user exists
843        CmsUser user = readUser(dbc, username);
844        if (user == null) {
845            // the user does not exists
846            throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, username));
847        }
848
849        // if adding an user to a role
850        if (readRoles) {
851            CmsRole role = CmsRole.valueOf(group);
852            // a role can only be set if the user has the given role
853            m_securityManager.checkRole(dbc, role);
854            // now we check if we already have the role
855            if (m_securityManager.hasRole(dbc, user, role)) {
856                // do nothing
857                return;
858            }
859            // and now we need to remove all possible child-roles
860            List<CmsRole> children = role.getChildren(true);
861            Iterator<CmsGroup> itUserGroups = getGroupsOfUser(
862                dbc,
863                username,
864                group.getOuFqn(),
865                true,
866                true,
867                true,
868                dbc.getRequestContext().getRemoteAddress()).iterator();
869            while (itUserGroups.hasNext()) {
870                CmsGroup roleGroup = itUserGroups.next();
871                if (children.contains(CmsRole.valueOf(roleGroup))) {
872                    // remove only child roles
873                    removeUserFromGroup(dbc, username, roleGroup.getName(), true);
874                }
875            }
876            // update virtual groups
877            Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator();
878            while (it.hasNext()) {
879                CmsGroup virtualGroup = it.next();
880                // here we say readroles = true, to prevent an unlimited recursive calls
881                addUserToGroup(dbc, username, virtualGroup.getName(), true);
882            }
883        }
884
885        //add this user to the group
886        getUserDriver(dbc).createUserInGroup(dbc, user.getId(), group.getId());
887
888        // flush the cache
889        if (readRoles) {
890            m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
891        }
892        m_monitor.flushUserGroups(user.getId());
893        m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST);
894
895        if (!dbc.getProjectId().isNullUUID() && !CmsProject.ONLINE_PROJECT_ID.equals(dbc.getProjectId())) {
896            // user modified event is not needed
897            return;
898        }
899        // fire user modified event
900        Map<String, Object> eventData = new HashMap<String, Object>();
901        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
902        eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
903        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
904        eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
905        eventData.put(
906            I_CmsEventListener.KEY_USER_ACTION,
907            I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP);
908        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
909    }
910
911    /**
912     * Changes the lock of a resource to the current user,
913     * that is "steals" the lock from another user.<p>
914     *
915     * @param dbc the current database context
916     * @param resource the resource to change the lock for
917     * @param lockType the new lock type to set
918     *
919     * @throws CmsException if something goes wrong
920     * @throws CmsSecurityException if something goes wrong
921     *
922     *
923     * @see CmsObject#changeLock(String)
924     * @see I_CmsResourceType#changeLock(CmsObject, CmsSecurityManager, CmsResource)
925     *
926     * @see CmsSecurityManager#hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter)
927     */
928    public void changeLock(CmsDbContext dbc, CmsResource resource, CmsLockType lockType)
929    throws CmsException, CmsSecurityException {
930
931        // get the current lock
932        CmsLock currentLock = getLock(dbc, resource);
933        // check if the resource is locked at all
934        if (currentLock.getEditionLock().isUnlocked() && currentLock.getSystemLock().isUnlocked()) {
935            throw new CmsLockException(
936                Messages.get().container(
937                    Messages.ERR_CHANGE_LOCK_UNLOCKED_RESOURCE_1,
938                    dbc.getRequestContext().getSitePath(resource)));
939        } else if ((lockType == CmsLockType.EXCLUSIVE)
940            && currentLock.isExclusiveOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) {
941                // the current lock requires no change
942                return;
943            }
944
945        // duplicate logic from CmsSecurityManager#hasPermissions() because lock state can't be ignored
946        // if another user has locked the file, the current user can never get WRITE permissions with the default check
947        int denied = 0;
948
949        // check if the current user is vfs manager
950        boolean canIgnorePermissions = m_securityManager.hasRoleForResource(
951            dbc,
952            dbc.currentUser(),
953            CmsRole.VFS_MANAGER,
954            resource);
955        // if the resource type is jsp
956        // write is only allowed for developers
957        if (!canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) {
958            if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource)) {
959                denied |= CmsPermissionSet.PERMISSION_WRITE;
960            }
961        }
962        CmsPermissionSetCustom permissions;
963        if (canIgnorePermissions) {
964            // if the current user is administrator, anything is allowed
965            permissions = new CmsPermissionSetCustom(~0);
966        } else {
967            // otherwise, get the permissions from the access control list
968            permissions = getPermissions(dbc, resource, dbc.currentUser());
969        }
970        // revoke the denied permissions
971        permissions.denyPermissions(denied);
972        // now check if write permission is granted
973        if ((CmsPermissionSet.ACCESS_WRITE.getPermissions()
974            & permissions.getPermissions()) != CmsPermissionSet.ACCESS_WRITE.getPermissions()) {
975            // check failed, throw exception
976            m_securityManager.checkPermissions(
977                dbc.getRequestContext(),
978                resource,
979                CmsPermissionSet.ACCESS_WRITE,
980                I_CmsPermissionHandler.PERM_DENIED);
981        }
982        // if we got here write permission is granted on the target
983
984        // remove the old lock
985        m_lockManager.removeResource(dbc, resource, true, lockType.isSystem());
986        // apply the new lock
987        lockResource(dbc, resource, lockType);
988    }
989
990    /**
991     * Returns a list with all sub resources of a given folder that have set the given property,
992     * matching the current property's value with the given old value and replacing it by a given new value.<p>
993     *
994     * @param dbc the current database context
995     * @param resource the resource on which property definition values are changed
996     * @param propertyDefinition the name of the propertydefinition to change the value
997     * @param oldValue the old value of the propertydefinition
998     * @param newValue the new value of the propertydefinition
999     * @param recursive if true, change the property value on the resource and recursively all property values on
1000     *                     sub-resources (only for folders)
1001     * @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed
1002     *
1003     * @throws CmsVfsException for now only when the search for the oldvalue failed.
1004     * @throws CmsException if operation was not successful
1005     */
1006    public List<CmsResource> changeResourcesInFolderWithProperty(
1007        CmsDbContext dbc,
1008        CmsResource resource,
1009        String propertyDefinition,
1010        String oldValue,
1011        String newValue,
1012        boolean recursive)
1013    throws CmsVfsException, CmsException {
1014
1015        CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION;
1016        // collect the resources to look up
1017        List<CmsResource> resources = new ArrayList<CmsResource>();
1018        if (recursive) {
1019            // read the files in the folder
1020            resources = readResourcesWithProperty(dbc, resource, propertyDefinition, null, filter);
1021            // add the folder itself
1022            resources.add(resource);
1023        } else {
1024            resources.add(resource);
1025        }
1026
1027        Pattern oldPattern;
1028        try {
1029            // remove the place holder if available
1030            String tmpOldValue = oldValue;
1031            if (tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_START)
1032                && tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_END)) {
1033                tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_START, "");
1034                tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_END, "");
1035            }
1036            // compile regular expression pattern
1037            oldPattern = Pattern.compile(tmpOldValue);
1038        } catch (PatternSyntaxException e) {
1039            throw new CmsVfsException(
1040                Messages.get().container(
1041                    Messages.ERR_CHANGE_RESOURCES_IN_FOLDER_WITH_PROP_4,
1042                    new Object[] {propertyDefinition, oldValue, newValue, resource.getRootPath()}),
1043                e);
1044        }
1045
1046        List<CmsResource> changedResources = new ArrayList<CmsResource>(resources.size());
1047        // create permission set and filter to check each resource
1048        CmsPermissionSet perm = CmsPermissionSet.ACCESS_WRITE;
1049        for (int i = 0; i < resources.size(); i++) {
1050            // loop through found resources and check property values
1051            CmsResource res = resources.get(i);
1052            // check resource state and permissions
1053            try {
1054                m_securityManager.checkPermissions(dbc, res, perm, true, filter);
1055            } catch (Exception e) {
1056                // resource is deleted or not writable for current user
1057                continue;
1058            }
1059            CmsProperty property = readPropertyObject(dbc, res, propertyDefinition, false);
1060            String propertyValue = property.getValue();
1061            boolean changed = false;
1062            if ((propertyValue != null) && oldPattern.matcher(propertyValue).matches()) {
1063                // apply the place holder content
1064                String tmpNewValue = CmsStringUtil.transformValues(oldValue, newValue, propertyValue);
1065                // change structure value
1066                property.setStructureValue(tmpNewValue);
1067                changed = true;
1068            }
1069            if (changed) {
1070                // write property object if something has changed
1071                writePropertyObject(dbc, res, property);
1072                changedResources.add(res);
1073            }
1074        }
1075        return changedResources;
1076    }
1077
1078    /**
1079     * Changes the resource flags of a resource.<p>
1080     *
1081     * The resource flags are used to indicate various "special" conditions
1082     * for a resource. Most notably, the "internal only" setting which signals
1083     * that a resource can not be directly requested with it's URL.<p>
1084     *
1085     * @param dbc the current database context
1086     * @param resource the resource to change the flags for
1087     * @param flags the new resource flags for this resource
1088     *
1089     * @throws CmsException if something goes wrong
1090     *
1091     * @see CmsObject#chflags(String, int)
1092     * @see I_CmsResourceType#chflags(CmsObject, CmsSecurityManager, CmsResource, int)
1093     */
1094    public void chflags(CmsDbContext dbc, CmsResource resource, int flags) throws CmsException {
1095
1096        // must operate on a clone to ensure resource is not modified in case permissions are not granted
1097        CmsResource clone = (CmsResource)resource.clone();
1098        clone.setFlags(flags);
1099        // log it
1100        log(
1101            dbc,
1102            new CmsLogEntry(
1103                dbc,
1104                resource.getStructureId(),
1105                CmsLogEntryType.RESOURCE_FLAGS,
1106                new String[] {resource.getRootPath()}),
1107            false);
1108        // write it
1109        writeResource(dbc, clone);
1110    }
1111
1112    /**
1113     * Changes the resource type of a resource.<p>
1114     *
1115     * OpenCms handles resources according to the resource type,
1116     * not the file suffix. This is e.g. why a JSP in OpenCms can have the
1117     * suffix ".html" instead of ".jsp" only. Changing the resource type
1118     * makes sense e.g. if you want to make a plain text file a JSP resource,
1119     * or a binary file an image, etc.<p>
1120     *
1121     * @param dbc the current database context
1122     * @param resource the resource to change the type for
1123     * @param type the new resource type for this resource
1124     *
1125     * @throws CmsException if something goes wrong
1126     *
1127     * @see CmsObject#chtype(String, int)
1128     * @see I_CmsResourceType#chtype(CmsObject, CmsSecurityManager, CmsResource, int)
1129     */
1130    @SuppressWarnings({"javadoc", "deprecation"})
1131    public void chtype(CmsDbContext dbc, CmsResource resource, int type) throws CmsException {
1132
1133        // must operate on a clone to ensure resource is not modified in case permissions are not granted
1134        CmsResource clone = (CmsResource)resource.clone();
1135        I_CmsResourceType newType = OpenCms.getResourceManager().getResourceType(type);
1136        clone.setType(newType.getTypeId());
1137        // log it
1138        log(
1139            dbc,
1140            new CmsLogEntry(
1141                dbc,
1142                resource.getStructureId(),
1143                CmsLogEntryType.RESOURCE_TYPE,
1144                new String[] {resource.getRootPath()}),
1145            false);
1146        // write it
1147        writeResource(dbc, clone);
1148    }
1149
1150    /**
1151     * Cleans up the publish history entries according to the given filter.
1152     *
1153     * @param dbc the database context
1154     * @param filter the filter
1155     * @return the number of cleaned up rows
1156     * @throws CmsDataAccessException if something goes wrong
1157     */
1158    public int cleanupPublishHistory(CmsDbContext dbc, CmsPublishHistoryCleanupFilter filter)
1159    throws CmsDataAccessException {
1160
1161        int result = m_projectDriver.cleanupPublishHistory(dbc, filter);
1162        if (filter.getMode() == CmsPublishHistoryCleanupFilter.Mode.single) {
1163            OpenCms.getMemoryMonitor().cachePublishedResources(filter.getHistoryId().toString(), null);
1164        } else {
1165            OpenCms.getMemoryMonitor().flushCache(CmsMemoryMonitor.CacheType.PUBLISHED_RESOURCES);
1166        }
1167        return result;
1168    }
1169
1170    /**
1171     * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent)
1172     */
1173    public void cmsEvent(CmsEvent event) {
1174
1175        if (LOG.isDebugEnabled()) {
1176            LOG.debug(Messages.get().getBundle().key(Messages.LOG_CMS_EVENT_1, new Integer(event.getType())));
1177        }
1178
1179        I_CmsReport report;
1180        CmsDbContext dbc;
1181
1182        switch (event.getType()) {
1183
1184            case I_CmsEventListener.EVENT_UPDATE_EXPORTS:
1185                dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT);
1186                updateExportPoints(dbc);
1187                break;
1188
1189            case I_CmsEventListener.EVENT_PUBLISH_PROJECT:
1190                CmsUUID publishHistoryId = new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID));
1191                report = (I_CmsReport)event.getData().get(I_CmsEventListener.KEY_REPORT);
1192                dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT);
1193                m_monitor.clearCacheForPublishing();
1194                writeExportPoints(dbc, report, publishHistoryId);
1195                break;
1196
1197            case I_CmsEventListener.EVENT_CLEAR_CACHES:
1198                m_monitor.clearCache();
1199                break;
1200            case I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES:
1201                m_monitor.clearPrincipalsCache();
1202                break;
1203            case I_CmsEventListener.EVENT_USER_MODIFIED:
1204                String action = (String)event.getData().get(I_CmsEventListener.KEY_USER_ACTION);
1205                m_monitor.flushCache(
1206                    CacheType.USER,
1207                    CacheType.GROUP,
1208                    CacheType.ORG_UNIT,
1209                    CacheType.ACL,
1210                    CacheType.PERMISSION,
1211                    CacheType.USER_LIST);
1212                if (I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP.equals(action)
1213                    || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP.equals(action)
1214                    || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU.equals(action)) {
1215
1216                    Object userIdObj = event.getData().get(I_CmsEventListener.KEY_USER_ID);
1217                    if (userIdObj != null) {
1218                        CmsUUID userId = null;
1219                        if (userIdObj instanceof CmsUUID) {
1220                            userId = (CmsUUID)userIdObj;
1221                        } else if (userIdObj instanceof String) {
1222                            try {
1223                                userId = new CmsUUID(userIdObj.toString());
1224                            } catch (Exception e) {
1225                                LOG.error(e.getLocalizedMessage(), e);
1226                            }
1227                        }
1228                        if (userId != null) {
1229                            m_monitor.flushUserGroups(userId);
1230                        }
1231                    } else {
1232                        m_monitor.flushCache(CacheType.USERGROUPS);
1233                    }
1234                    m_monitor.flushCache(CacheType.HAS_ROLE, CacheType.ROLE_LIST);
1235                }
1236                break;
1237            default:
1238                // noop
1239        }
1240    }
1241
1242    /**
1243     * Copies the access control entries of a given resource to a destination resource.<p>
1244     *
1245     * Already existing access control entries of the destination resource are removed.<p>
1246     *
1247     * @param dbc the current database context
1248     * @param source the resource to copy the access control entries from
1249     * @param destination the resource to which the access control entries are copied
1250     * @param updateLastModifiedInfo if true, user and date "last modified" information on the target resource will be updated
1251     *
1252     * @throws CmsException if something goes wrong
1253     */
1254    public void copyAccessControlEntries(
1255        CmsDbContext dbc,
1256        CmsResource source,
1257        CmsResource destination,
1258        boolean updateLastModifiedInfo)
1259    throws CmsException {
1260
1261        // get the entries to copy
1262        ListIterator<CmsAccessControlEntry> aceList = getUserDriver(
1263            dbc).readAccessControlEntries(dbc, dbc.currentProject(), source.getResourceId(), false).listIterator();
1264
1265        // remove the current entries from the destination
1266        getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), destination.getResourceId());
1267
1268        // now write the new entries
1269        while (aceList.hasNext()) {
1270            CmsAccessControlEntry ace = aceList.next();
1271            getUserDriver(dbc).createAccessControlEntry(
1272                dbc,
1273                dbc.currentProject(),
1274                destination.getResourceId(),
1275                ace.getPrincipal(),
1276                ace.getPermissions().getAllowedPermissions(),
1277                ace.getPermissions().getDeniedPermissions(),
1278                ace.getFlags());
1279        }
1280
1281        // log it
1282        log(
1283            dbc,
1284            new CmsLogEntry(
1285                dbc,
1286                destination.getStructureId(),
1287                CmsLogEntryType.RESOURCE_PERMISSIONS,
1288                new String[] {destination.getRootPath()}),
1289            false);
1290
1291        // update the "last modified" information
1292        if (updateLastModifiedInfo) {
1293            setDateLastModified(dbc, destination, destination.getDateLastModified());
1294        }
1295
1296        // clear the cache
1297        m_monitor.clearAccessControlListCache();
1298
1299        // fire a resource modification event
1300        Map<String, Object> data = new HashMap<String, Object>(2);
1301        data.put(I_CmsEventListener.KEY_RESOURCE, destination);
1302        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_ACCESSCONTROL));
1303        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
1304    }
1305
1306    /**
1307     * Copies a resource.<p>
1308     *
1309     * You must ensure that the destination path is an absolute, valid and
1310     * existing VFS path. Relative paths from the source are currently not supported.<p>
1311     *
1312     * In case the target resource already exists, it is overwritten with the
1313     * source resource.<p>
1314     *
1315     * The <code>siblingMode</code> parameter controls how to handle siblings
1316     * during the copy operation.
1317     * Possible values for this parameter are:
1318     * <ul>
1319     * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_NEW}</code></li>
1320     * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_SIBLING}</code></li>
1321     * <li><code>{@link org.opencms.file.CmsResource#COPY_PRESERVE_SIBLING}</code></li>
1322     * </ul><p>
1323     *
1324     * @param dbc the current database context
1325     * @param source the resource to copy
1326     * @param destination the name of the copy destination with complete path
1327     * @param siblingMode indicates how to handle siblings during copy
1328     *
1329     * @throws CmsException if something goes wrong
1330     * @throws CmsIllegalArgumentException if the <code>source</code> argument is <code>null</code>
1331     *
1332     * @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode)
1333     * @see I_CmsResourceType#copyResource(CmsObject, CmsSecurityManager, CmsResource, String, CmsResource.CmsResourceCopyMode)
1334     */
1335    public void copyResource(
1336        CmsDbContext dbc,
1337        CmsResource source,
1338        String destination,
1339        CmsResource.CmsResourceCopyMode siblingMode)
1340    throws CmsException, CmsIllegalArgumentException {
1341
1342        // check the sibling mode to see if this resource has to be copied as a sibling
1343        boolean copyAsSibling = false;
1344
1345        // siblings of folders are not supported
1346        if (!source.isFolder()) {
1347            // if the "copy as sibling" mode is used, set the flag to true
1348            if (siblingMode == CmsResource.COPY_AS_SIBLING) {
1349                copyAsSibling = true;
1350            }
1351            // if the mode is "preserve siblings", we have to check the sibling counter
1352            if (siblingMode == CmsResource.COPY_PRESERVE_SIBLING) {
1353                if (source.getSiblingCount() > 1) {
1354                    copyAsSibling = true;
1355                }
1356            }
1357        }
1358
1359        // read the source properties
1360        List<CmsProperty> properties = readPropertyObjects(dbc, source, false);
1361
1362        if (copyAsSibling) {
1363            // create a sibling of the source file at the destination
1364            createSibling(dbc, source, destination, properties);
1365            // after the sibling is created the copy operation is finished
1366            return;
1367        }
1368
1369        // prepare the content if required
1370        byte[] content = null;
1371        if (source.isFile()) {
1372            if (source instanceof CmsFile) {
1373                // resource already is a file
1374                content = ((CmsFile)source).getContents();
1375            }
1376            if ((content == null) || (content.length < 1)) {
1377                // no known content yet - read from database
1378                content = getVfsDriver(dbc).readContent(dbc, dbc.currentProject().getUuid(), source.getResourceId());
1379            }
1380        }
1381
1382        // determine destination folder
1383        String destinationFoldername = CmsResource.getParentFolder(destination);
1384
1385        // read the destination folder (will also check read permissions)
1386        CmsFolder destinationFolder = m_securityManager.readFolder(
1387            dbc,
1388            destinationFoldername,
1389            CmsResourceFilter.IGNORE_EXPIRATION);
1390
1391        // no further permission check required here, will be done in createResource()
1392
1393        // set user and creation time stamps
1394        long currentTime = System.currentTimeMillis();
1395        long dateLastModified;
1396        CmsUUID userLastModified;
1397        if (source.isFolder()) {
1398            // folders always get a new date and user when they are copied
1399            dateLastModified = currentTime;
1400            userLastModified = dbc.currentUser().getId();
1401        } else {
1402            // files keep the date and user last modified from the source
1403            dateLastModified = source.getDateLastModified();
1404            userLastModified = source.getUserLastModified();
1405        }
1406
1407        // check the resource flags
1408        int flags = source.getFlags();
1409        if (source.isLabeled()) {
1410            // reset "labeled" link flag for new resource
1411            flags &= ~CmsResource.FLAG_LABELED;
1412        }
1413
1414        // create the new resource
1415        CmsResource newResource = new CmsResource(
1416            new CmsUUID(),
1417            new CmsUUID(),
1418            destination,
1419            source.getTypeId(),
1420            source.isFolder(),
1421            flags,
1422            dbc.currentProject().getUuid(),
1423            CmsResource.STATE_NEW,
1424            currentTime,
1425            dbc.currentUser().getId(),
1426            dateLastModified,
1427            userLastModified,
1428            source.getDateReleased(),
1429            source.getDateExpired(),
1430            1,
1431            source.getLength(),
1432            source.getDateContent(),
1433            source.getVersion()); // version number does not matter since it will be computed later
1434
1435        // trigger "is touched" state on resource (will ensure modification date is kept unchanged)
1436        newResource.setDateLastModified(dateLastModified);
1437
1438        // log it
1439        log(
1440            dbc,
1441            new CmsLogEntry(
1442                dbc,
1443                newResource.getStructureId(),
1444                CmsLogEntryType.RESOURCE_COPIED,
1445                new String[] {newResource.getRootPath()}),
1446            false);
1447
1448        // create the resource
1449        newResource = createResource(dbc, destination, newResource, content, properties, false);
1450        // copy relations
1451        copyRelations(dbc, source, newResource);
1452
1453        // copy the access control entries to the created resource
1454        copyAccessControlEntries(dbc, source, newResource, false);
1455
1456        // clear the cache
1457        m_monitor.clearAccessControlListCache();
1458
1459        List<CmsResource> modifiedResources = new ArrayList<CmsResource>();
1460        modifiedResources.add(source);
1461        modifiedResources.add(newResource);
1462        modifiedResources.add(destinationFolder);
1463        OpenCms.fireCmsEvent(
1464            new CmsEvent(
1465                I_CmsEventListener.EVENT_RESOURCE_COPIED,
1466                Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, modifiedResources)));
1467    }
1468
1469    /**
1470     * Copies a resource to the current project of the user.<p>
1471     *
1472     * @param dbc the current database context
1473     * @param resource the resource to apply this operation to
1474     *
1475     * @throws CmsException if something goes wrong
1476     *
1477     * @see CmsObject#copyResourceToProject(String)
1478     * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource)
1479     */
1480    public void copyResourceToProject(CmsDbContext dbc, CmsResource resource) throws CmsException {
1481
1482        // copy the resource to the project only if the resource is not already in the project
1483        if (!isInsideCurrentProject(dbc, resource.getRootPath())) {
1484            // check if there are already any subfolders of this resource
1485            I_CmsProjectDriver projectDriver = getProjectDriver(dbc);
1486            if (resource.isFolder()) {
1487                List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject());
1488                for (int i = 0; i < projectResources.size(); i++) {
1489                    String resname = projectResources.get(i);
1490                    if (resname.startsWith(resource.getRootPath())) {
1491                        // delete the existing project resource first
1492                        projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname);
1493                    }
1494                }
1495            }
1496            try {
1497                projectDriver.createProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath());
1498            } catch (CmsException exc) {
1499                // if the subfolder exists already - all is ok
1500            } finally {
1501                m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
1502
1503                OpenCms.fireCmsEvent(
1504                    new CmsEvent(
1505                        I_CmsEventListener.EVENT_PROJECT_MODIFIED,
1506                        Collections.<String, Object> singletonMap("project", dbc.currentProject())));
1507            }
1508        }
1509    }
1510
1511    /**
1512     * Counts the locked resources in this project.<p>
1513     *
1514     * @param project the project to count the locked resources in
1515     *
1516     * @return the amount of locked resources in this project
1517     */
1518    public int countLockedResources(CmsProject project) {
1519
1520        // count locks
1521        return m_lockManager.countExclusiveLocksInProject(project);
1522    }
1523
1524    /**
1525     * Add a new group to the Cms.<p>
1526     *
1527     * Only the admin can do this.
1528     * Only users, which are in the group "administrators" are granted.<p>
1529     *
1530     * @param dbc the current database context
1531     * @param id the id of the new group
1532     * @param name the name of the new group
1533     * @param description the description for the new group
1534     * @param flags the flags for the new group
1535     * @param parent the name of the parent group (or <code>null</code>)
1536     *
1537     * @return new created group
1538     *
1539     * @throws CmsException if the creation of the group failed
1540     * @throws CmsIllegalArgumentException if the length of the given name was below 1
1541     */
1542    public CmsGroup createGroup(CmsDbContext dbc, CmsUUID id, String name, String description, int flags, String parent)
1543    throws CmsIllegalArgumentException, CmsException {
1544
1545        // check the group name
1546        OpenCms.getValidationHandler().checkGroupName(CmsOrganizationalUnit.getSimpleName(name));
1547        // trim the name
1548        name = name.trim();
1549
1550        // check the OU
1551        readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
1552
1553        // get the id of the parent group if necessary
1554        if (CmsStringUtil.isNotEmpty(parent)) {
1555            CmsGroup parentGroup = readGroup(dbc, parent);
1556            if (!parentGroup.isRole()
1557                && !CmsOrganizationalUnit.getParentFqn(parent).equals(CmsOrganizationalUnit.getParentFqn(name))) {
1558                throw new CmsDataAccessException(
1559                    Messages.get().container(
1560                        Messages.ERR_PARENT_GROUP_MUST_BE_IN_SAME_OU_3,
1561                        CmsOrganizationalUnit.getSimpleName(name),
1562                        CmsOrganizationalUnit.getParentFqn(name),
1563                        parent));
1564            }
1565        }
1566
1567        // create the group
1568        CmsGroup group = getUserDriver(dbc).createGroup(dbc, id, name, description, flags, parent);
1569
1570        // if the group is in fact a role, initialize it
1571        if (group.isVirtual()) {
1572            // get all users that have the given role
1573            String groupname = CmsRole.valueOf(group).getGroupName();
1574            Iterator<CmsUser> it = getUsersOfGroup(dbc, groupname, true, false, true).iterator();
1575            while (it.hasNext()) {
1576                CmsUser user = it.next();
1577                // put them in the new group
1578                addUserToGroup(dbc, user.getName(), group.getName(), true);
1579            }
1580        }
1581
1582        // put it into the cache
1583        m_monitor.cacheGroup(group);
1584
1585        if (!dbc.getProjectId().isNullUUID()) {
1586            // group modified event is not needed
1587            return group;
1588        }
1589        // fire group modified event
1590        Map<String, Object> eventData = new HashMap<String, Object>();
1591        eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
1592        eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
1593        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_CREATE);
1594        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData));
1595
1596        // return it
1597        return group;
1598    }
1599
1600    /**
1601     * Creates a new organizational unit.<p>
1602     *
1603     * @param dbc the current db context
1604     * @param ouFqn the fully qualified name of the new organizational unit
1605     * @param description the description of the new organizational unit
1606     * @param flags the flags for the new organizational unit
1607     * @param resource the first associated resource
1608     *
1609     * @return a <code>{@link CmsOrganizationalUnit}</code> object representing
1610     *          the newly created organizational unit
1611     *
1612     * @throws CmsException if operation was not successful
1613     *
1614     * @see org.opencms.security.CmsOrgUnitManager#createOrganizationalUnit(CmsObject, String, String, int, String)
1615     */
1616    public CmsOrganizationalUnit createOrganizationalUnit(
1617        CmsDbContext dbc,
1618        String ouFqn,
1619        String description,
1620        int flags,
1621        CmsResource resource)
1622    throws CmsException {
1623
1624        // normal case
1625        CmsOrganizationalUnit parent = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(ouFqn));
1626        String name = CmsOrganizationalUnit.getSimpleName(ouFqn);
1627        if (name.endsWith(CmsOrganizationalUnit.SEPARATOR)) {
1628            name = name.substring(0, name.length() - 1);
1629        }
1630
1631        // check the name
1632        CmsResource.checkResourceName(name);
1633
1634        // trim the name
1635        name = name.trim();
1636
1637        // check the description
1638        if (CmsStringUtil.isEmptyOrWhitespaceOnly(description)) {
1639            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_OU_DESCRIPTION_EMPTY_0));
1640        }
1641
1642        // create the organizational unit
1643        CmsOrganizationalUnit orgUnit = getUserDriver(dbc).createOrganizationalUnit(
1644            dbc,
1645            name,
1646            description,
1647            flags,
1648            parent,
1649            resource != null ? resource.getRootPath() : null);
1650        // put the new created org unit into the cache
1651        m_monitor.cacheOrgUnit(orgUnit);
1652
1653        // flush relevant caches
1654        m_monitor.clearPrincipalsCache();
1655        m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
1656
1657        // create a publish list for the 'virtual' publish event
1658        CmsResource ouRes = readResource(
1659            dbc,
1660            CmsUserDriver.ORGUNIT_BASE_FOLDER + orgUnit.getName(),
1661            CmsResourceFilter.DEFAULT);
1662        CmsPublishList pl = new CmsPublishList(ouRes, false);
1663        pl.add(ouRes, false);
1664
1665        getProjectDriver(dbc).writePublishHistory(
1666            dbc,
1667            pl.getPublishHistoryId(),
1668            new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW));
1669
1670        // fire the 'virtual' publish event
1671        Map<String, Object> eventData = new HashMap<String, Object>();
1672        eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString());
1673        eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
1674        eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
1675        CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData);
1676        OpenCms.fireCmsEvent(afterPublishEvent);
1677
1678        if (!dbc.getProjectId().isNullUUID()) {
1679            // OU modified event is not needed
1680            return orgUnit;
1681        }
1682
1683        // fire OU modified event
1684        Map<String, Object> event2Data = new HashMap<String, Object>();
1685        event2Data.put(I_CmsEventListener.KEY_OU_NAME, orgUnit.getName());
1686        event2Data.put(I_CmsEventListener.KEY_OU_ID, orgUnit.getId().toString());
1687        event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_CREATE);
1688        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data));
1689
1690        // return it
1691        return orgUnit;
1692    }
1693
1694    /**
1695     * Creates a project.<p>
1696     *
1697     * @param dbc the current database context
1698     * @param name the name of the project to create
1699     * @param description the description of the project
1700     * @param groupname the project user group to be set
1701     * @param managergroupname the project manager group to be set
1702     * @param projecttype the type of the project
1703     *
1704     * @return the created project
1705     *
1706     * @throws CmsIllegalArgumentException if the chosen <code>name</code> is already used
1707     *         by the online project, or if the name is not valid
1708     * @throws CmsException if something goes wrong
1709     */
1710    public CmsProject createProject(
1711        CmsDbContext dbc,
1712        String name,
1713        String description,
1714        String groupname,
1715        String managergroupname,
1716        CmsProject.CmsProjectType projecttype)
1717    throws CmsIllegalArgumentException, CmsException {
1718
1719        if (CmsProject.ONLINE_PROJECT_NAME.equals(name)) {
1720            throw new CmsIllegalArgumentException(
1721                Messages.get().container(
1722                    Messages.ERR_CREATE_PROJECT_ONLINE_PROJECT_NAME_1,
1723                    CmsProject.ONLINE_PROJECT_NAME));
1724        }
1725        // check the name
1726        CmsProject.checkProjectName(CmsOrganizationalUnit.getSimpleName(name));
1727        // check the ou
1728        readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
1729        // read the needed groups from the cms
1730        CmsGroup group = readGroup(dbc, groupname);
1731        CmsGroup managergroup = readGroup(dbc, managergroupname);
1732
1733        return getProjectDriver(dbc).createProject(
1734            dbc,
1735            new CmsUUID(),
1736            dbc.currentUser(),
1737            group,
1738            managergroup,
1739            name,
1740            description,
1741            projecttype.getDefaultFlags(),
1742            projecttype);
1743    }
1744
1745    /**
1746     * Creates a property definition.<p>
1747     *
1748     * Property definitions are valid for all resource types.<p>
1749     *
1750     * @param dbc the current database context
1751     * @param name the name of the property definition to create
1752     *
1753     * @return the created property definition
1754     *
1755     * @throws CmsException if something goes wrong
1756     */
1757    public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name) throws CmsException {
1758
1759        CmsPropertyDefinition propertyDefinition = null;
1760
1761        name = name.trim();
1762        // validate the property name
1763        CmsPropertyDefinition.checkPropertyName(name);
1764        // TODO: make the type a parameter
1765        try {
1766            try {
1767                propertyDefinition = getVfsDriver(dbc).readPropertyDefinition(
1768                    dbc,
1769                    name,
1770                    dbc.currentProject().getUuid());
1771            } catch (CmsException e) {
1772                propertyDefinition = getVfsDriver(dbc).createPropertyDefinition(
1773                    dbc,
1774                    dbc.currentProject().getUuid(),
1775                    name,
1776                    CmsPropertyDefinition.TYPE_NORMAL);
1777            }
1778
1779            try {
1780                getVfsDriver(dbc).readPropertyDefinition(dbc, name, CmsProject.ONLINE_PROJECT_ID);
1781            } catch (CmsException e) {
1782                getVfsDriver(dbc).createPropertyDefinition(
1783                    dbc,
1784                    CmsProject.ONLINE_PROJECT_ID,
1785                    name,
1786                    CmsPropertyDefinition.TYPE_NORMAL);
1787            }
1788
1789            try {
1790                getHistoryDriver(dbc).readPropertyDefinition(dbc, name);
1791            } catch (CmsException e) {
1792                getHistoryDriver(dbc).createPropertyDefinition(dbc, name, CmsPropertyDefinition.TYPE_NORMAL);
1793            }
1794        } finally {
1795
1796            // fire an event that a property of a resource has been deleted
1797            OpenCms.fireCmsEvent(
1798                new CmsEvent(
1799                    I_CmsEventListener.EVENT_PROPERTY_DEFINITION_CREATED,
1800                    Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition)));
1801
1802        }
1803
1804        return propertyDefinition;
1805    }
1806
1807    /**
1808     * Creates a new publish job.<p>
1809     *
1810     * @param dbc the current database context
1811     * @param publishJob the publish job to create
1812     *
1813     * @throws CmsException if something goes wrong
1814     */
1815    public void createPublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException {
1816
1817        getProjectDriver(dbc).createPublishJob(dbc, publishJob);
1818    }
1819
1820    /**
1821     * Creates a new resource with the provided content and properties.<p>
1822     *
1823     * The <code>content</code> parameter may be <code>null</code> if the resource id
1824     * already exists. If so, the created resource will be a sibling of the existing
1825     * resource, the existing content will remain unchanged.<p>
1826     *
1827     * This is used during file import for import of siblings as the
1828     * <code>manifest.xml</code> only contains one binary copy per file.<p>
1829     *
1830     * If the resource id exists but the <code>content</code> is not <code>null</code>,
1831     * the created resource will be made a sibling of the existing resource,
1832     * and both will share the new content.<p>
1833     *
1834     * @param dbc the current database context
1835     * @param resourcePath the name of the resource to create (full path)
1836     * @param resource the new resource to create
1837     * @param content the content for the new resource
1838     * @param properties the properties for the new resource
1839     * @param importCase if <code>true</code>, signals that this operation is done while
1840     *                      importing resource, causing different lock behavior and
1841     *                      potential "lost and found" usage
1842     *
1843     * @return the created resource
1844     *
1845     * @throws CmsException if something goes wrong
1846     */
1847    public CmsResource createResource(
1848        CmsDbContext dbc,
1849        String resourcePath,
1850        CmsResource resource,
1851        byte[] content,
1852        List<CmsProperty> properties,
1853        boolean importCase)
1854    throws CmsException {
1855
1856        CmsResource newResource = null;
1857        if (resource.isFolder()) {
1858            resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath);
1859        }
1860
1861        try {
1862            synchronized (this) {
1863                // need to provide the parent folder id for resource creation
1864                String parentFolderName = CmsResource.getParentFolder(resourcePath);
1865                CmsResource parentFolder = readFolder(dbc, parentFolderName, CmsResourceFilter.IGNORE_EXPIRATION);
1866
1867                CmsLock parentLock = getLock(dbc, parentFolder);
1868                // it is not allowed to create a resource in a folder locked by other user
1869                if (!parentLock.isUnlocked() && !parentLock.isOwnedBy(dbc.currentUser())) {
1870                    // one exception is if the admin user tries to create a temporary resource
1871                    if (!CmsResource.getName(resourcePath).startsWith(TEMP_FILE_PREFIX)
1872                        || !m_securityManager.hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) {
1873                        throw new CmsLockException(
1874                            Messages.get().container(
1875                                Messages.ERR_CREATE_RESOURCE_PARENT_LOCK_1,
1876                                dbc.removeSiteRoot(resourcePath)));
1877                    }
1878                }
1879                if (CmsResourceTypeJsp.isJsp(resource)) {
1880                    // security check when trying to create a new jsp file
1881                    m_securityManager.checkRoleForResource(dbc, CmsRole.VFS_MANAGER, parentFolder);
1882                }
1883
1884                // check import configuration of "lost and found" folder
1885                boolean useLostAndFound = importCase && !OpenCms.getImportExportManager().overwriteCollidingResources();
1886
1887                // check if the resource already exists by name
1888                CmsResource currentResourceByName = null;
1889                try {
1890                    currentResourceByName = readResource(dbc, resourcePath, CmsResourceFilter.ALL);
1891                } catch (CmsVfsResourceNotFoundException e) {
1892                    // if the resource does exist, we have to check the id later to decide what to do
1893                }
1894
1895                // check if the resource already exists by id
1896                try {
1897                    CmsResource currentResourceById = readResource(
1898                        dbc,
1899                        resource.getStructureId(),
1900                        CmsResourceFilter.ALL);
1901                    // it is not allowed to import resources when there is already a resource with the same id but different path
1902                    if (!currentResourceById.getRootPath().equals(resourcePath)) {
1903                        throw new CmsVfsResourceAlreadyExistsException(
1904                            Messages.get().container(
1905                                Messages.ERR_RESOURCE_WITH_ID_ALREADY_EXISTS_3,
1906                                dbc.removeSiteRoot(resourcePath),
1907                                dbc.removeSiteRoot(currentResourceById.getRootPath()),
1908                                currentResourceById.getStructureId()));
1909                    }
1910                } catch (CmsVfsResourceNotFoundException e) {
1911                    // if the resource does exist, we have to check the id later to decide what to do
1912                }
1913
1914                // check the permissions
1915                if (currentResourceByName == null) {
1916                    // resource does not exist - check parent folder
1917                    m_securityManager.checkPermissions(
1918                        dbc,
1919                        parentFolder,
1920                        CmsPermissionSet.ACCESS_WRITE,
1921                        false,
1922                        CmsResourceFilter.IGNORE_EXPIRATION);
1923                } else {
1924                    // resource already exists - check existing resource
1925                    m_securityManager.checkPermissions(
1926                        dbc,
1927                        currentResourceByName,
1928                        CmsPermissionSet.ACCESS_WRITE,
1929                        !importCase,
1930                        CmsResourceFilter.ALL);
1931                }
1932
1933                // now look for the resource by name
1934                if (currentResourceByName != null) {
1935                    boolean overwrite = true;
1936                    if (currentResourceByName.getState().isDeleted()) {
1937                        if (!currentResourceByName.isFolder()) {
1938                            // if a non-folder resource was deleted it's treated like a new resource
1939                            overwrite = false;
1940                        }
1941                    } else {
1942                        if (!importCase) {
1943                            // direct "overwrite" of a resource is possible only during import,
1944                            // or if the resource has been deleted
1945                            throw new CmsVfsResourceAlreadyExistsException(
1946                                org.opencms.db.generic.Messages.get().container(
1947                                    org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
1948                                    dbc.removeSiteRoot(resource.getRootPath())));
1949                        }
1950                        // the resource already exists
1951                        if (!resource.isFolder()
1952                            && useLostAndFound
1953                            && (!currentResourceByName.getResourceId().equals(resource.getResourceId()))) {
1954                            // semantic change: the current resource is moved to L&F and the imported resource will overwrite the old one
1955                            // will leave the resource with state deleted,
1956                            // but it does not matter, since the state will be set later again
1957                            moveToLostAndFound(dbc, currentResourceByName, false);
1958                        }
1959                    }
1960                    if (!overwrite) {
1961                        // lock the resource, will throw an exception if not lockable
1962                        lockResource(dbc, currentResourceByName, CmsLockType.EXCLUSIVE);
1963
1964                        // trigger createResource instead of writeResource
1965                        currentResourceByName = null;
1966                    }
1967                }
1968                // if null, create new resource, if not null write resource
1969                CmsResource overwrittenResource = currentResourceByName;
1970
1971                // extract the name (without path)
1972                String targetName = CmsResource.getName(resourcePath);
1973
1974                int contentLength;
1975
1976                // modify target name and content length in case of folder creation
1977                if (resource.isFolder()) {
1978                    // folders never have any content
1979                    contentLength = -1;
1980                    // must cut of trailing '/' for folder creation (or name check fails)
1981                    if (CmsResource.isFolder(targetName)) {
1982                        targetName = targetName.substring(0, targetName.length() - 1);
1983                    }
1984                } else {
1985                    // otherwise ensure content and content length are set correctly
1986                    if (content != null) {
1987                        // if a content is provided, in each case the length is the length of this content
1988                        contentLength = content.length;
1989                    } else if (overwrittenResource != null) {
1990                        // we have no content, but an already existing resource - length remains unchanged
1991                        contentLength = overwrittenResource.getLength();
1992                    } else {
1993                        // we have no content - length is used as set in the resource
1994                        contentLength = resource.getLength();
1995                    }
1996                }
1997
1998                // check if the target name is valid (forbidden chars etc.),
1999                // if not throw an exception
2000                // must do this here since targetName is modified in folder case (see above)
2001                CmsResource.checkResourceName(targetName);
2002
2003                // set structure and resource ids as given
2004                CmsUUID structureId = resource.getStructureId();
2005                CmsUUID resourceId = resource.getResourceId();
2006
2007                // decide which structure id to use
2008                if (overwrittenResource != null) {
2009                    // resource exists, re-use existing ids
2010                    structureId = overwrittenResource.getStructureId();
2011                }
2012                if (structureId.isNullUUID()) {
2013                    // need a new structure id
2014                    structureId = new CmsUUID();
2015                }
2016
2017                // decide which resource id to use
2018                if (overwrittenResource != null) {
2019                    // if we are overwriting we have to assure the resource id is the same
2020                    resourceId = overwrittenResource.getResourceId();
2021                }
2022                if (resourceId.isNullUUID()) {
2023                    // need a new resource id
2024                    resourceId = new CmsUUID();
2025                }
2026
2027                try {
2028                    // check online resource
2029                    CmsResource onlineResource = getVfsDriver(
2030                        dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resourcePath, true);
2031                    // only allow to overwrite with different id if importing (createResource will set the right id)
2032                    try {
2033                        CmsResource offlineResource = getVfsDriver(dbc).readResource(
2034                            dbc,
2035                            dbc.currentProject().getUuid(),
2036                            onlineResource.getStructureId(),
2037                            true);
2038                        if (!offlineResource.getRootPath().equals(onlineResource.getRootPath())) {
2039                            throw new CmsVfsOnlineResourceAlreadyExistsException(
2040                                Messages.get().container(
2041                                    Messages.ERR_ONLINE_RESOURCE_EXISTS_2,
2042                                    dbc.removeSiteRoot(resourcePath),
2043                                    dbc.removeSiteRoot(offlineResource.getRootPath())));
2044                        }
2045                    } catch (CmsVfsResourceNotFoundException e) {
2046                        // there is no problem for now
2047                        // but should never happen
2048                        if (LOG.isErrorEnabled()) {
2049                            LOG.error(e.getLocalizedMessage(), e);
2050                        }
2051                    }
2052                } catch (CmsVfsResourceNotFoundException e) {
2053                    // ok, there is no online entry to worry about
2054                }
2055
2056                // now create a resource object with all informations
2057                newResource = new CmsResource(
2058                    structureId,
2059                    resourceId,
2060                    resourcePath,
2061                    resource.getTypeId(),
2062                    resource.isFolder(),
2063                    resource.getFlags(),
2064                    dbc.currentProject().getUuid(),
2065                    resource.getState(),
2066                    resource.getDateCreated(),
2067                    resource.getUserCreated(),
2068                    resource.getDateLastModified(),
2069                    resource.getUserLastModified(),
2070                    resource.getDateReleased(),
2071                    resource.getDateExpired(),
2072                    1,
2073                    contentLength,
2074                    resource.getDateContent(),
2075                    resource.getVersion()); // version number does not matter since it will be computed later
2076
2077                // ensure date is updated only if required
2078                if (resource.isTouched()) {
2079                    // this will trigger the internal "is touched" state on the new resource
2080                    newResource.setDateLastModified(resource.getDateLastModified());
2081                }
2082
2083                if (resource.isFile()) {
2084                    // check if a sibling to the imported resource lies in a marked site
2085                    if (labelResource(dbc, resource, resourcePath, 2)) {
2086                        int flags = resource.getFlags();
2087                        flags |= CmsResource.FLAG_LABELED;
2088                        resource.setFlags(flags);
2089                    }
2090                    // ensure siblings don't overwrite existing resource records
2091                    if (content == null) {
2092                        newResource.setState(CmsResource.STATE_KEEP);
2093                    }
2094                }
2095
2096                // delete all relations for the resource, before writing the content
2097                getVfsDriver(
2098                    dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), newResource, CmsRelationFilter.TARGETS);
2099                if (overwrittenResource == null) {
2100                    CmsLock lock = getLock(dbc, newResource);
2101                    if (lock.getEditionLock().isExclusive()) {
2102                        unlockResource(dbc, newResource, true, false);
2103                    }
2104                    // resource does not exist.
2105                    newResource = getVfsDriver(
2106                        dbc).createResource(dbc, dbc.currentProject().getUuid(), newResource, content);
2107                } else {
2108                    // resource already exists.
2109                    // probably the resource is a merged page file that gets overwritten during import, or it gets
2110                    // overwritten by a copy operation. if so, the structure & resource state are not modified to changed.
2111                    int updateStates = (overwrittenResource.getState().isNew()
2112                    ? CmsDriverManager.NOTHING_CHANGED
2113                    : CmsDriverManager.UPDATE_ALL);
2114                    getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), newResource, updateStates);
2115
2116                    if ((content != null) && resource.isFile()) {
2117                        // also update file content if required
2118                        getVfsDriver(dbc).writeContent(dbc, newResource.getResourceId(), content);
2119                    }
2120                }
2121
2122                // write the properties (internal operation, no events or duplicate permission checks)
2123                writePropertyObjects(dbc, newResource, properties, false);
2124
2125                // lock the created resource
2126                try {
2127                    // if it is locked by another user (copied or moved resource) this lock should be preserved and
2128                    // the exception is OK: locks on created resources are a slave feature to original locks
2129                    lockResource(dbc, newResource, CmsLockType.EXCLUSIVE);
2130                } catch (CmsLockException cle) {
2131                    if (LOG.isDebugEnabled()) {
2132                        LOG.debug(
2133                            Messages.get().getBundle().key(
2134                                Messages.ERR_CREATE_RESOURCE_LOCK_1,
2135                                new Object[] {dbc.removeSiteRoot(newResource.getRootPath())}));
2136                    }
2137                }
2138
2139                if (!importCase) {
2140                    log(
2141                        dbc,
2142                        new CmsLogEntry(
2143                            dbc,
2144                            newResource.getStructureId(),
2145                            CmsLogEntryType.RESOURCE_CREATED,
2146                            new String[] {resource.getRootPath()}),
2147                        false);
2148                } else {
2149                    log(
2150                        dbc,
2151                        new CmsLogEntry(
2152                            dbc,
2153                            newResource.getStructureId(),
2154                            CmsLogEntryType.RESOURCE_IMPORTED,
2155                            new String[] {resource.getRootPath()}),
2156                        false);
2157                }
2158            }
2159        } finally {
2160            // clear the internal caches
2161            m_monitor.clearAccessControlListCache();
2162            m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
2163
2164            if (newResource != null) {
2165                // fire an event that a new resource has been created
2166                OpenCms.fireCmsEvent(
2167                    new CmsEvent(
2168                        I_CmsEventListener.EVENT_RESOURCE_CREATED,
2169                        Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, newResource)));
2170            }
2171        }
2172        return newResource;
2173    }
2174
2175    /**
2176     * Creates a new resource of the given resource type
2177     * with the provided content and properties.<p>
2178     *
2179     * If the provided content is null and the resource is not a folder,
2180     * the content will be set to an empty byte array.<p>
2181     *
2182     * @param dbc the current database context
2183     * @param resourcename the name of the resource to create (full path)
2184     * @param type the type of the resource to create
2185     * @param content the content for the new resource
2186     * @param properties the properties for the new resource
2187     *
2188     * @return the created resource
2189     *
2190     * @throws CmsException if something goes wrong
2191     * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0
2192     *
2193     * @see CmsObject#createResource(String, int, byte[], List)
2194     * @see CmsObject#createResource(String, int)
2195     * @see I_CmsResourceType#createResource(CmsObject, CmsSecurityManager, String, byte[], List)
2196     */
2197    @SuppressWarnings("javadoc")
2198    public CmsResource createResource(
2199        CmsDbContext dbc,
2200        String resourcename,
2201        int type,
2202        byte[] content,
2203        List<CmsProperty> properties)
2204    throws CmsException, CmsIllegalArgumentException {
2205
2206        String targetName = resourcename;
2207
2208        if (content == null) {
2209            // name based resource creation MUST have a content
2210            content = new byte[0];
2211        }
2212        int size;
2213
2214        if (CmsFolder.isFolderType(type)) {
2215            // must cut of trailing '/' for folder creation
2216            if (CmsResource.isFolder(targetName)) {
2217                targetName = targetName.substring(0, targetName.length() - 1);
2218            }
2219            size = -1;
2220        } else {
2221            size = content.length;
2222        }
2223
2224        // create a new resource
2225        CmsResource newResource = new CmsResource(
2226            CmsUUID.getNullUUID(), // uuids will be "corrected" later
2227            CmsUUID.getNullUUID(),
2228            targetName,
2229            type,
2230            CmsFolder.isFolderType(type),
2231            0,
2232            dbc.currentProject().getUuid(),
2233            CmsResource.STATE_NEW,
2234            0,
2235            dbc.currentUser().getId(),
2236            0,
2237            dbc.currentUser().getId(),
2238            CmsResource.DATE_RELEASED_DEFAULT,
2239            CmsResource.DATE_EXPIRED_DEFAULT,
2240            1,
2241            size,
2242            0, // version number does not matter since it will be computed later
2243            0); // content time will be corrected later
2244
2245        return createResource(dbc, targetName, newResource, content, properties, false);
2246    }
2247
2248    /**
2249     * Creates a new sibling of the source resource.<p>
2250     *
2251     * @param dbc the current database context
2252     * @param source the resource to create a sibling for
2253     * @param destination the name of the sibling to create with complete path
2254     * @param properties the individual properties for the new sibling
2255     *
2256     * @return the new created sibling
2257     *
2258     * @throws CmsException if something goes wrong
2259     *
2260     * @see CmsObject#createSibling(String, String, List)
2261     * @see I_CmsResourceType#createSibling(CmsObject, CmsSecurityManager, CmsResource, String, List)
2262     */
2263    public CmsResource createSibling(
2264        CmsDbContext dbc,
2265        CmsResource source,
2266        String destination,
2267        List<CmsProperty> properties)
2268    throws CmsException {
2269
2270        if (source.isFolder()) {
2271            throw new CmsVfsException(Messages.get().container(Messages.ERR_VFS_FOLDERS_DONT_SUPPORT_SIBLINGS_0));
2272        }
2273
2274        // determine destination folder and resource name
2275        String destinationFoldername = CmsResource.getParentFolder(destination);
2276
2277        // read the destination folder (will also check read permissions)
2278        CmsFolder destinationFolder = readFolder(dbc, destinationFoldername, CmsResourceFilter.IGNORE_EXPIRATION);
2279
2280        // no further permission check required here, will be done in createResource()
2281
2282        // check the resource flags
2283        int flags = source.getFlags();
2284        if (labelResource(dbc, source, destination, 1)) {
2285            // set "labeled" link flag for new resource
2286            flags |= CmsResource.FLAG_LABELED;
2287        }
2288
2289        // create the new resource
2290        CmsResource newResource = new CmsResource(
2291            new CmsUUID(),
2292            source.getResourceId(),
2293            destination,
2294            source.getTypeId(),
2295            source.isFolder(),
2296            flags,
2297            dbc.currentProject().getUuid(),
2298            CmsResource.STATE_KEEP,
2299            source.getDateCreated(), // ensures current resource record remains untouched
2300            source.getUserCreated(),
2301            source.getDateLastModified(),
2302            source.getUserLastModified(),
2303            source.getDateReleased(),
2304            source.getDateExpired(),
2305            source.getSiblingCount() + 1,
2306            source.getLength(),
2307            source.getDateContent(),
2308            source.getVersion()); // version number does not matter since it will be computed later
2309
2310        // trigger "is touched" state on resource (will ensure modification date is kept unchanged)
2311        newResource.setDateLastModified(newResource.getDateLastModified());
2312
2313        log(
2314            dbc,
2315            new CmsLogEntry(
2316                dbc,
2317                newResource.getStructureId(),
2318                CmsLogEntryType.RESOURCE_CLONED,
2319                new String[] {newResource.getRootPath()}),
2320            false);
2321        // create the resource (null content signals creation of sibling)
2322        newResource = createResource(dbc, destination, newResource, null, properties, false);
2323
2324        // copy relations
2325        copyRelations(dbc, source, newResource);
2326
2327        // clear the caches
2328        m_monitor.clearAccessControlListCache();
2329
2330        List<CmsResource> modifiedResources = new ArrayList<CmsResource>();
2331        modifiedResources.add(source);
2332        modifiedResources.add(newResource);
2333        modifiedResources.add(destinationFolder);
2334        Map<String, Object> eventData = new HashMap<>();
2335        eventData.put(I_CmsEventListener.KEY_RESOURCES, modifiedResources);
2336        eventData.put(I_CmsEventListener.KEY_CHANGE, I_CmsEventListener.VALUE_CREATE_SIBLING);
2337        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, eventData));
2338
2339        return newResource;
2340    }
2341
2342    /**
2343     * Creates the project for the temporary workplace files.<p>
2344     *
2345     * @param dbc the current database context
2346     *
2347     * @return the created project for the temporary workplace files
2348     *
2349     * @throws CmsException if something goes wrong
2350     */
2351    public CmsProject createTempfileProject(CmsDbContext dbc) throws CmsException {
2352
2353        // read the needed groups from the cms
2354        CmsGroup projectUserGroup = readGroup(dbc, dbc.currentProject().getGroupId());
2355        CmsGroup projectManagerGroup = readGroup(dbc, dbc.currentProject().getManagerGroupId());
2356
2357        CmsProject tempProject = getProjectDriver(dbc).createProject(
2358            dbc,
2359            new CmsUUID(),
2360            dbc.currentUser(),
2361            projectUserGroup,
2362            projectManagerGroup,
2363            I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME,
2364            Messages.get().getBundle(dbc.getRequestContext().getLocale()).key(
2365                Messages.GUI_WORKPLACE_TEMPFILE_PROJECT_DESC_0),
2366            CmsProject.PROJECT_FLAG_HIDDEN,
2367            CmsProject.PROJECT_TYPE_NORMAL);
2368        getProjectDriver(dbc).createProjectResource(dbc, tempProject.getUuid(), "/");
2369
2370        OpenCms.fireCmsEvent(
2371            new CmsEvent(
2372                I_CmsEventListener.EVENT_PROJECT_MODIFIED,
2373                Collections.<String, Object> singletonMap("project", tempProject)));
2374
2375        return tempProject;
2376    }
2377
2378    /**
2379     * Creates a new user.<p>
2380     *
2381     * @param dbc the current database context
2382     * @param name the name for the new user
2383     * @param password the password for the new user
2384     * @param description the description for the new user
2385     * @param additionalInfos the additional infos for the user
2386     *
2387     * @return the created user
2388     *
2389     * @see CmsObject#createUser(String, String, String, Map)
2390     *
2391     * @throws CmsException if something goes wrong
2392     * @throws CmsIllegalArgumentException if the name for the user is not valid
2393     */
2394    public CmsUser createUser(
2395        CmsDbContext dbc,
2396        String name,
2397        String password,
2398        String description,
2399        Map<String, Object> additionalInfos)
2400    throws CmsException, CmsIllegalArgumentException {
2401
2402        // no space before or after the name
2403        name = name.trim();
2404        // check the user name
2405        String userName = CmsOrganizationalUnit.getSimpleName(name);
2406        OpenCms.getValidationHandler().checkUserName(userName);
2407        if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) {
2408            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName));
2409        }
2410        // check the ou
2411        CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
2412        // check the password
2413        validatePassword(password);
2414
2415        Map<String, Object> info = new HashMap<String, Object>();
2416        if (additionalInfos != null) {
2417            info.putAll(additionalInfos);
2418        }
2419        if (description != null) {
2420            info.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description);
2421        }
2422        int flags = 0;
2423        if (ou.hasFlagWebuser()) {
2424            flags += I_CmsPrincipal.FLAG_USER_WEBUSER;
2425        }
2426        CmsUser user = getUserDriver(dbc).createUser(
2427            dbc,
2428            new CmsUUID(),
2429            name,
2430            OpenCms.getPasswordHandler().digest(password),
2431            " ",
2432            " ",
2433            " ",
2434            0,
2435            I_CmsPrincipal.FLAG_ENABLED + flags,
2436            0,
2437            info);
2438
2439        if (!dbc.getProjectId().isNullUUID()) {
2440            // user modified event is not needed
2441            return user;
2442        }
2443        // fire user modified event
2444        Map<String, Object> eventData = new HashMap<String, Object>();
2445        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
2446        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_CREATE_USER);
2447        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
2448        return user;
2449    }
2450
2451    /**
2452     * Deletes aliases indicated by a filter.<p>
2453     *
2454     * @param dbc the current database context
2455     * @param project the current project
2456     * @param filter the filter which describes which aliases to delete
2457     *
2458     * @throws CmsException if something goes wrong
2459     */
2460    public void deleteAliases(CmsDbContext dbc, CmsProject project, CmsAliasFilter filter) throws CmsException {
2461
2462        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
2463        vfsDriver.deleteAliases(dbc, project, filter);
2464    }
2465
2466    /**
2467     * Deletes all property values of a file or folder.<p>
2468     *
2469     * If there are no other siblings than the specified resource,
2470     * both the structure and resource property values get deleted.
2471     * If the specified resource has siblings, only the structure
2472     * property values get deleted.<p>
2473     *
2474     * @param dbc the current database context
2475     * @param resourcename the name of the resource for which all properties should be deleted
2476     *
2477     * @throws CmsException if operation was not successful
2478     */
2479    public void deleteAllProperties(CmsDbContext dbc, String resourcename) throws CmsException {
2480
2481        CmsResource resource = null;
2482        List<CmsResource> resources = new ArrayList<CmsResource>();
2483
2484        try {
2485            // read the resource
2486            resource = readResource(dbc, resourcename, CmsResourceFilter.IGNORE_EXPIRATION);
2487
2488            // check the security
2489            m_securityManager.checkPermissions(
2490                dbc,
2491                resource,
2492                CmsPermissionSet.ACCESS_WRITE,
2493                false,
2494                CmsResourceFilter.ALL);
2495
2496            // delete the property values
2497            if (resource.getSiblingCount() > 1) {
2498                // the resource has siblings- delete only the (structure) properties of this sibling
2499                getVfsDriver(dbc).deletePropertyObjects(
2500                    dbc,
2501                    dbc.currentProject().getUuid(),
2502                    resource,
2503                    CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES);
2504                resources.addAll(readSiblings(dbc, resource, CmsResourceFilter.ALL));
2505
2506            } else {
2507                // the resource has no other siblings- delete all (structure+resource) properties
2508                getVfsDriver(dbc).deletePropertyObjects(
2509                    dbc,
2510                    dbc.currentProject().getUuid(),
2511                    resource,
2512                    CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES);
2513                resources.add(resource);
2514            }
2515        } finally {
2516            // clear the driver manager cache
2517            m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
2518
2519            // fire an event that all properties of a resource have been deleted
2520            OpenCms.fireCmsEvent(
2521                new CmsEvent(
2522                    I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED,
2523                    Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources)));
2524        }
2525    }
2526
2527    /**
2528     * Deletes all entries in the published resource table.<p>
2529     *
2530     * @param dbc the current database context
2531     * @param linkType the type of resource deleted (0= non-paramter, 1=parameter)
2532     *
2533     * @throws CmsException if something goes wrong
2534     */
2535    public void deleteAllStaticExportPublishedResources(CmsDbContext dbc, int linkType) throws CmsException {
2536
2537        getProjectDriver(dbc).deleteAllStaticExportPublishedResources(dbc, linkType);
2538    }
2539
2540    /**
2541     * Deletes a group, where all permissions, users and children of the group
2542     * are transfered to a replacement group.<p>
2543     *
2544     * @param dbc the current request context
2545     * @param group the id of the group to be deleted
2546     * @param replacementId the id of the group to be transfered, can be <code>null</code>
2547     *
2548     * @throws CmsException if operation was not successful
2549     * @throws CmsDataAccessException if group to be deleted contains user
2550     */
2551    public void deleteGroup(CmsDbContext dbc, CmsGroup group, CmsUUID replacementId)
2552    throws CmsDataAccessException, CmsException {
2553
2554        CmsGroup replacementGroup = null;
2555        if (replacementId != null) {
2556            replacementGroup = readGroup(dbc, replacementId);
2557        }
2558        // get all child groups of the group
2559        List<CmsGroup> children = getChildren(dbc, group, false);
2560        // get all users in this group
2561        List<CmsUser> users = getUsersOfGroup(dbc, group.getName(), true, true, group.isRole());
2562        // get online project
2563        CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
2564        if (replacementGroup == null) {
2565            // remove users
2566            Iterator<CmsUser> itUsers = users.iterator();
2567            while (itUsers.hasNext()) {
2568                CmsUser user = itUsers.next();
2569                if (userInGroup(dbc, user.getName(), group.getName(), group.isRole())) {
2570                    removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole());
2571                }
2572            }
2573            // transfer children to grandfather if possible
2574            CmsUUID parentId = group.getParentId();
2575            if (parentId == null) {
2576                parentId = CmsUUID.getNullUUID();
2577            }
2578            Iterator<CmsGroup> itChildren = children.iterator();
2579            while (itChildren.hasNext()) {
2580                CmsGroup child = itChildren.next();
2581                child.setParentId(parentId);
2582                writeGroup(dbc, child);
2583            }
2584        } else {
2585            // move children
2586            Iterator<CmsGroup> itChildren = children.iterator();
2587            while (itChildren.hasNext()) {
2588                CmsGroup child = itChildren.next();
2589                child.setParentId(replacementId);
2590                writeGroup(dbc, child);
2591            }
2592            // move users
2593            Iterator<CmsUser> itUsers = users.iterator();
2594            while (itUsers.hasNext()) {
2595                CmsUser user = itUsers.next();
2596                addUserToGroup(dbc, user.getName(), replacementGroup.getName(), group.isRole());
2597                removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole());
2598            }
2599            // transfer for offline
2600            transferPrincipalResources(dbc, dbc.currentProject(), group.getId(), replacementId, true);
2601            // transfer for online
2602            transferPrincipalResources(dbc, onlineProject, group.getId(), replacementId, true);
2603        }
2604        // remove the group
2605        getUserDriver(
2606            dbc).removeAccessControlEntriesForPrincipal(dbc, dbc.currentProject(), onlineProject, group.getId());
2607        getUserDriver(dbc).deleteGroup(dbc, group.getName());
2608        // backup the group
2609        getHistoryDriver(dbc).writePrincipal(dbc, group);
2610        if (OpenCms.getSubscriptionManager().isEnabled()) {
2611            // delete all subscribed resources for group
2612            unsubscribeAllResourcesFor(dbc, OpenCms.getSubscriptionManager().getPoolName(), group);
2613        }
2614
2615        // clear the relevant caches
2616        m_monitor.uncacheGroup(group);
2617        m_monitor.flushCache(
2618            CmsMemoryMonitor.CacheType.USERGROUPS,
2619            CmsMemoryMonitor.CacheType.USER_LIST,
2620            CmsMemoryMonitor.CacheType.ACL);
2621
2622        if (!dbc.getProjectId().isNullUUID()) {
2623            // group modified event is not needed
2624            return;
2625        }
2626        // fire group modified event
2627        Map<String, Object> eventData = new HashMap<String, Object>();
2628        eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
2629        eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
2630        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_DELETE);
2631        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData));
2632    }
2633
2634    /**
2635     * Deletes the versions from the history tables, keeping the given number of versions per resource.<p>
2636     *
2637     * if the <code>cleanUp</code> option is set, additionally versions of deleted resources will be removed.<p>
2638     *
2639     * @param dbc the current database context
2640     * @param versionsToKeep number of versions to keep, is ignored if negative
2641     * @param versionsDeleted number of versions to keep for deleted resources, is ignored if negative
2642     * @param timeDeleted deleted resources older than this will also be deleted, is ignored if negative
2643     * @param report the report for output logging
2644     *
2645     * @throws CmsException if operation was not successful
2646     */
2647    public void deleteHistoricalVersions(
2648        CmsDbContext dbc,
2649        int versionsToKeep,
2650        int versionsDeleted,
2651        long timeDeleted,
2652        I_CmsReport report)
2653    throws CmsException {
2654
2655        report.println(Messages.get().container(Messages.RPT_START_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE);
2656        if (versionsToKeep >= 0) {
2657            report.println(
2658                Messages.get().container(Messages.RPT_START_DELETE_ACT_VERSIONS_1, new Integer(versionsToKeep)),
2659                I_CmsReport.FORMAT_HEADLINE);
2660
2661            List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllNotDeletedEntries(dbc);
2662            if (resources.isEmpty()) {
2663                report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK);
2664            }
2665            int n = resources.size();
2666            int m = 1;
2667            Iterator<I_CmsHistoryResource> itResources = resources.iterator();
2668            while (itResources.hasNext()) {
2669                I_CmsHistoryResource histResource = itResources.next();
2670
2671                report.print(
2672                    org.opencms.report.Messages.get().container(
2673                        org.opencms.report.Messages.RPT_SUCCESSION_2,
2674                        String.valueOf(m),
2675                        String.valueOf(n)),
2676                    I_CmsReport.FORMAT_NOTE);
2677                report.print(
2678                    org.opencms.report.Messages.get().container(
2679                        org.opencms.report.Messages.RPT_ARGUMENT_1,
2680                        dbc.removeSiteRoot(histResource.getRootPath())));
2681                report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
2682
2683                try {
2684                    int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsToKeep, -1);
2685
2686                    report.print(
2687                        Messages.get().container(Messages.RPT_VERSION_DELETING_1, new Integer(deleted)),
2688                        I_CmsReport.FORMAT_NOTE);
2689                    report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
2690                    report.println(
2691                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
2692                        I_CmsReport.FORMAT_OK);
2693                } catch (CmsDataAccessException e) {
2694                    report.println(
2695                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0),
2696                        I_CmsReport.FORMAT_ERROR);
2697
2698                    if (LOG.isDebugEnabled()) {
2699                        LOG.debug(e.getLocalizedMessage(), e);
2700                    }
2701                }
2702
2703                m++;
2704            }
2705
2706            report.println(
2707                Messages.get().container(Messages.RPT_END_DELETE_ACT_VERSIONS_0),
2708                I_CmsReport.FORMAT_HEADLINE);
2709        }
2710        if ((versionsDeleted >= 0) || (timeDeleted >= 0)) {
2711            if (timeDeleted >= 0) {
2712                report.println(
2713                    Messages.get().container(
2714                        Messages.RPT_START_DELETE_DEL_VERSIONS_2,
2715                        new Integer(versionsDeleted),
2716                        new Date(timeDeleted)),
2717                    I_CmsReport.FORMAT_HEADLINE);
2718            } else {
2719                report.println(
2720                    Messages.get().container(Messages.RPT_START_DELETE_DEL_VERSIONS_1, new Integer(versionsDeleted)),
2721                    I_CmsReport.FORMAT_HEADLINE);
2722            }
2723            List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllDeletedEntries(dbc);
2724            if (resources.isEmpty()) {
2725                report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK);
2726            }
2727            int n = resources.size();
2728            int m = 1;
2729            Iterator<I_CmsHistoryResource> itResources = resources.iterator();
2730            while (itResources.hasNext()) {
2731                I_CmsHistoryResource histResource = itResources.next();
2732
2733                report.print(
2734                    org.opencms.report.Messages.get().container(
2735                        org.opencms.report.Messages.RPT_SUCCESSION_2,
2736                        String.valueOf(m),
2737                        String.valueOf(n)),
2738                    I_CmsReport.FORMAT_NOTE);
2739                report.print(
2740                    org.opencms.report.Messages.get().container(
2741                        org.opencms.report.Messages.RPT_ARGUMENT_1,
2742                        dbc.removeSiteRoot(histResource.getRootPath())));
2743                report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
2744
2745                try {
2746                    int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsDeleted, timeDeleted);
2747
2748                    report.print(
2749                        Messages.get().container(Messages.RPT_VERSION_DELETING_1, new Integer(deleted)),
2750                        I_CmsReport.FORMAT_NOTE);
2751                    report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
2752                    report.println(
2753                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
2754                        I_CmsReport.FORMAT_OK);
2755                } catch (CmsDataAccessException e) {
2756                    report.println(
2757                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0),
2758                        I_CmsReport.FORMAT_ERROR);
2759
2760                    if (LOG.isDebugEnabled()) {
2761                        LOG.debug(e.getLocalizedMessage(), e);
2762                    }
2763                }
2764
2765                m++;
2766            }
2767            report.println(
2768                Messages.get().container(Messages.RPT_END_DELETE_DEL_VERSIONS_0),
2769                I_CmsReport.FORMAT_HEADLINE);
2770        }
2771        report.println(Messages.get().container(Messages.RPT_END_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE);
2772    }
2773
2774    /**
2775     * Deletes all log entries matching the given filter.<p>
2776     *
2777     * @param dbc the current db context
2778     * @param filter the filter to use for deletion
2779     *
2780     * @throws CmsException if something goes wrong
2781     *
2782     * @see CmsSecurityManager#deleteLogEntries(CmsRequestContext, CmsLogFilter)
2783     */
2784    public void deleteLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException {
2785
2786        updateLog(dbc);
2787        m_projectDriver.deleteLog(dbc, filter);
2788    }
2789
2790    /**
2791     * Deletes an organizational unit.<p>
2792     *
2793     * Only organizational units that contain no suborganizational unit can be deleted.<p>
2794     *
2795     * The organizational unit can not be delete if it is used in the request context,
2796     * or if the current user belongs to it.<p>
2797     *
2798     * All users and groups in the given organizational unit will be deleted.<p>
2799     *
2800     * @param dbc the current db context
2801     * @param organizationalUnit the organizational unit to delete
2802     *
2803     * @throws CmsException if operation was not successful
2804     *
2805     * @see org.opencms.security.CmsOrgUnitManager#deleteOrganizationalUnit(CmsObject, String)
2806     */
2807    public void deleteOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit)
2808    throws CmsException {
2809
2810        // check organizational unit in context
2811        if (dbc.getRequestContext().getOuFqn().equals(organizationalUnit.getName())) {
2812            throw new CmsDbConsistencyException(
2813                Messages.get().container(Messages.ERR_ORGUNIT_DELETE_IN_CONTEXT_1, organizationalUnit.getName()));
2814        }
2815        // check organizational unit for user
2816        if (dbc.currentUser().getOuFqn().equals(organizationalUnit.getName())) {
2817            throw new CmsDbConsistencyException(
2818                Messages.get().container(Messages.ERR_ORGUNIT_DELETE_CURRENT_USER_1, organizationalUnit.getName()));
2819        }
2820        // check sub organizational units
2821        if (!getOrganizationalUnits(dbc, organizationalUnit, true).isEmpty()) {
2822            throw new CmsDbConsistencyException(
2823                Messages.get().container(Messages.ERR_ORGUNIT_DELETE_SUB_ORGUNITS_1, organizationalUnit.getName()));
2824        }
2825        // check groups
2826        List<CmsGroup> groups = getGroups(dbc, organizationalUnit, true, false);
2827        Iterator<CmsGroup> itGroups = groups.iterator();
2828        while (itGroups.hasNext()) {
2829            CmsGroup group = itGroups.next();
2830            if (!OpenCms.getDefaultUsers().isDefaultGroup(group.getName())) {
2831                throw new CmsDbConsistencyException(
2832                    Messages.get().container(Messages.ERR_ORGUNIT_DELETE_GROUPS_1, organizationalUnit.getName()));
2833            }
2834        }
2835        // check users
2836        if (!getUsers(dbc, organizationalUnit, true).isEmpty()) {
2837            throw new CmsDbConsistencyException(
2838                Messages.get().container(Messages.ERR_ORGUNIT_DELETE_USERS_1, organizationalUnit.getName()));
2839        }
2840
2841        // delete default groups if needed
2842        itGroups = groups.iterator();
2843        while (itGroups.hasNext()) {
2844            CmsGroup group = itGroups.next();
2845            deleteGroup(dbc, group, null);
2846        }
2847
2848        // delete projects
2849        Iterator<CmsProject> itProjects = getProjectDriver(dbc).readProjects(
2850            dbc,
2851            organizationalUnit.getName()).iterator();
2852        while (itProjects.hasNext()) {
2853            CmsProject project = itProjects.next();
2854            deleteProject(dbc, project, false);
2855        }
2856
2857        // delete roles
2858        Iterator<CmsGroup> itRoles = getGroups(dbc, organizationalUnit, true, true).iterator();
2859        while (itRoles.hasNext()) {
2860            CmsGroup role = itRoles.next();
2861            deleteGroup(dbc, role, null);
2862        }
2863
2864        // create a publish list for the 'virtual' publish event
2865        CmsResource resource = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT);
2866        CmsPublishList pl = new CmsPublishList(resource, false);
2867        pl.add(resource, false);
2868
2869        // remove the organizational unit itself
2870        getUserDriver(dbc).deleteOrganizationalUnit(dbc, organizationalUnit);
2871
2872        // write the publish history entry
2873        getProjectDriver(dbc).writePublishHistory(
2874            dbc,
2875            pl.getPublishHistoryId(),
2876            new CmsPublishedResource(resource, -1, CmsResourceState.STATE_DELETED));
2877
2878        // flush relevant caches
2879        m_monitor.clearPrincipalsCache();
2880        m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
2881
2882        // fire the 'virtual' publish event
2883        Map<String, Object> eventData = new HashMap<String, Object>();
2884        eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString());
2885        eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
2886        eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
2887        CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData);
2888        OpenCms.fireCmsEvent(afterPublishEvent);
2889
2890        m_lockManager.removeDeletedResource(dbc, resource.getRootPath());
2891
2892        if (!dbc.getProjectId().isNullUUID()) {
2893            // OU modified event is not needed
2894            return;
2895        }
2896        // fire OU modified event
2897        Map<String, Object> event2Data = new HashMap<String, Object>();
2898        event2Data.put(I_CmsEventListener.KEY_OU_NAME, organizationalUnit.getName());
2899        event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_DELETE);
2900        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data));
2901
2902    }
2903
2904    /**
2905     * Deletes a project.<p>
2906     *
2907     * Only the admin or the owner of the project can do this.
2908     *
2909     * @param dbc the current database context
2910     * @param deleteProject the project to be deleted
2911     *
2912     * @throws CmsException if something goes wrong
2913     */
2914    public void deleteProject(CmsDbContext dbc, CmsProject deleteProject) throws CmsException {
2915
2916        deleteProject(dbc, deleteProject, true);
2917    }
2918
2919    /**
2920     * Deletes a project.<p>
2921     *
2922     * Only the admin or the owner of the project can do this.
2923     *
2924     * @param dbc the current database context
2925     * @param deleteProject the project to be deleted
2926     * @param resetResources if true, the resources of the project to delete will be reset to their online state, or deleted if they have no online state
2927     *
2928     * @throws CmsException if something goes wrong
2929     */
2930    public void deleteProject(CmsDbContext dbc, CmsProject deleteProject, boolean resetResources) throws CmsException {
2931
2932        CmsUUID projectId = deleteProject.getUuid();
2933
2934        if (resetResources) {
2935            // changed/new/deleted files in the specified project
2936            List<CmsResource> modifiedFiles = readChangedResourcesInsideProject(dbc, projectId, RCPRM_FILES_ONLY_MODE);
2937            // changed/new/deleted folders in the specified project
2938            List<CmsResource> modifiedFolders = readChangedResourcesInsideProject(
2939                dbc,
2940                projectId,
2941                RCPRM_FOLDERS_ONLY_MODE);
2942            resetResourcesInProject(dbc, projectId, modifiedFiles, modifiedFolders);
2943        }
2944
2945        // unlock all resources in the project
2946        m_lockManager.removeResourcesInProject(deleteProject.getUuid(), true);
2947        m_monitor.clearAccessControlListCache();
2948        m_monitor.clearResourceCache();
2949
2950        // set project to online project if current project is the one which will be deleted
2951        if (projectId.equals(dbc.currentProject().getUuid())) {
2952            dbc.getRequestContext().setCurrentProject(readProject(dbc, CmsProject.ONLINE_PROJECT_ID));
2953        }
2954
2955        // delete the project itself
2956        getProjectDriver(dbc).deleteProject(dbc, deleteProject);
2957        m_monitor.uncacheProject(deleteProject);
2958
2959        // fire the corresponding event
2960        OpenCms.fireCmsEvent(
2961            new CmsEvent(
2962                I_CmsEventListener.EVENT_PROJECT_MODIFIED,
2963                Collections.<String, Object> singletonMap("project", deleteProject)));
2964
2965    }
2966
2967    /**
2968     * Deletes a property definition.<p>
2969     *
2970     * @param dbc the current database context
2971     * @param name the name of the property definition to delete
2972     *
2973     * @throws CmsException if something goes wrong
2974     */
2975    public void deletePropertyDefinition(CmsDbContext dbc, String name) throws CmsException {
2976
2977        CmsPropertyDefinition propertyDefinition = null;
2978
2979        try {
2980            // first read and then delete the metadefinition.
2981            propertyDefinition = readPropertyDefinition(dbc, name);
2982            getVfsDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition);
2983            getHistoryDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition);
2984        } finally {
2985
2986            // fire an event that a property of a resource has been deleted
2987            OpenCms.fireCmsEvent(
2988                new CmsEvent(
2989                    I_CmsEventListener.EVENT_PROPERTY_DEFINITION_MODIFIED,
2990                    Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition)));
2991        }
2992    }
2993
2994    /**
2995     * Deletes a publish job identified by its history id.<p>
2996     *
2997     * @param dbc the current database context
2998     * @param publishHistoryId the history id identifying the publish job
2999     *
3000     * @throws CmsException if something goes wrong
3001     */
3002    public void deletePublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
3003
3004        getProjectDriver(dbc).deletePublishJob(dbc, publishHistoryId);
3005    }
3006
3007    /**
3008     * Deletes the publish list assigned to a publish job.<p>
3009     *
3010     * @param dbc the current database context
3011     * @param publishHistoryId the history id identifying the publish job
3012     * @throws CmsException if something goes wrong
3013     */
3014    public void deletePublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
3015
3016        getProjectDriver(dbc).deletePublishList(dbc, publishHistoryId);
3017    }
3018
3019    /**
3020     * Deletes all relations for the given resource matching the given filter.<p>
3021     *
3022     * @param dbc the current db context
3023     * @param resource the resource to delete the relations for
3024     * @param filter the filter to use for deletion
3025     *
3026     * @throws CmsException if something goes wrong
3027     *
3028     * @see CmsSecurityManager#deleteRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter)
3029     */
3030    public void deleteRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter)
3031    throws CmsException {
3032
3033        if (filter.includesDefinedInContent()) {
3034            throw new CmsIllegalArgumentException(
3035                Messages.get().container(
3036                    Messages.ERR_DELETE_RELATION_IN_CONTENT_2,
3037                    dbc.removeSiteRoot(resource.getRootPath()),
3038                    filter.getTypes()));
3039        }
3040        getVfsDriver(dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), resource, filter);
3041        setDateLastModified(dbc, resource, System.currentTimeMillis());
3042        log(
3043            dbc,
3044            new CmsLogEntry(
3045                dbc,
3046                resource.getStructureId(),
3047                CmsLogEntryType.RESOURCE_REMOVE_RELATION,
3048                new String[] {resource.getRootPath(), filter.toString()}),
3049            false);
3050    }
3051
3052    /**
3053     * Deletes a resource.<p>
3054     *
3055     * The <code>siblingMode</code> parameter controls how to handle siblings
3056     * during the delete operation.
3057     * Possible values for this parameter are:
3058     * <ul>
3059     * <li><code>{@link CmsResource#DELETE_REMOVE_SIBLINGS}</code></li>
3060     * <li><code>{@link CmsResource#DELETE_PRESERVE_SIBLINGS}</code></li>
3061     * </ul><p>
3062     *
3063     * @param dbc the current database context
3064     * @param resource the name of the resource to delete (full path)
3065     * @param siblingMode indicates how to handle siblings of the deleted resource
3066     *
3067     * @throws CmsException if something goes wrong
3068     *
3069     * @see CmsObject#deleteResource(String, CmsResource.CmsResourceDeleteMode)
3070     * @see I_CmsResourceType#deleteResource(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode)
3071     */
3072    public void deleteResource(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceDeleteMode siblingMode)
3073    throws CmsException {
3074
3075        // upgrade a potential inherited, non-shared lock into a common lock
3076        CmsLock currentLock = getLock(dbc, resource);
3077        if (currentLock.getEditionLock().isDirectlyInherited()) {
3078            // upgrade the lock status if required
3079            lockResource(dbc, resource, CmsLockType.EXCLUSIVE);
3080        }
3081
3082        // check if siblings of the resource exist and must be deleted as well
3083        if (resource.isFolder()) {
3084            // folder can have no siblings
3085            siblingMode = CmsResource.DELETE_PRESERVE_SIBLINGS;
3086        }
3087
3088        // if selected, add all siblings of this resource to the list of resources to be deleted
3089        boolean allSiblingsRemoved;
3090        List<CmsResource> resources;
3091        if (siblingMode == CmsResource.DELETE_REMOVE_SIBLINGS) {
3092            resources = new ArrayList<CmsResource>(readSiblings(dbc, resource, CmsResourceFilter.ALL));
3093            allSiblingsRemoved = true;
3094
3095            // ensure that the resource requested to be deleted is the last resource that gets actually deleted
3096            // to keep the shared locks of the siblings while those get deleted.
3097            resources.remove(resource);
3098            resources.add(resource);
3099        } else {
3100            // only delete the resource, no siblings
3101            resources = Collections.singletonList(resource);
3102            allSiblingsRemoved = false;
3103        }
3104
3105        int size = resources.size();
3106        // if we have only one resource no further check is required
3107        if (size > 1) {
3108            CmsMultiException me = new CmsMultiException();
3109            // ensure that each sibling is unlocked or locked by the current user
3110            for (int i = 0; i < size; i++) {
3111                CmsResource currentResource = resources.get(i);
3112                currentLock = getLock(dbc, currentResource);
3113                if (!currentLock.getEditionLock().isUnlocked() && !currentLock.isOwnedBy(dbc.currentUser())) {
3114                    // the resource is locked by a user different from the current user
3115                    CmsRequestContext context = dbc.getRequestContext();
3116                    me.addException(
3117                        new CmsLockException(
3118                            org.opencms.lock.Messages.get().container(
3119                                org.opencms.lock.Messages.ERR_SIBLING_LOCKED_2,
3120                                context.getSitePath(currentResource),
3121                                context.getSitePath(resource))));
3122                }
3123            }
3124            if (!me.getExceptions().isEmpty()) {
3125                throw me;
3126            }
3127        }
3128
3129        boolean removeAce = true;
3130
3131        if (resource.isFolder()) {
3132            // check if the folder has any resources in it
3133            Iterator<CmsResource> childResources = getVfsDriver(
3134                dbc).readChildResources(dbc, dbc.currentProject(), resource, true, true).iterator();
3135
3136            CmsUUID projectId = CmsProject.ONLINE_PROJECT_ID;
3137            if (dbc.currentProject().isOnlineProject()) {
3138                projectId = CmsUUID.getOpenCmsUUID(); // HACK: to get an offline project id
3139            }
3140
3141            // collect the names of the resources inside the folder, excluding the moved resources
3142            StringBuffer errorResNames = new StringBuffer(128);
3143            while (childResources.hasNext()) {
3144                CmsResource errorRes = childResources.next();
3145                if (errorRes.getState().isDeleted()) {
3146                    continue;
3147                }
3148                // if deleting offline, or not moved, or just renamed inside the deleted folder
3149                // so, it may remain some orphan online entries for moved resources
3150                // which will be fixed during the publishing of the moved resources
3151                boolean error = !dbc.currentProject().isOnlineProject();
3152                if (!error) {
3153                    try {
3154                        String originalPath = getVfsDriver(
3155                            dbc).readResource(dbc, projectId, errorRes.getRootPath(), true).getRootPath();
3156                        error = originalPath.equals(errorRes.getRootPath())
3157                            || originalPath.startsWith(resource.getRootPath());
3158                    } catch (CmsVfsResourceNotFoundException e) {
3159                        // ignore
3160                    }
3161                }
3162                if (error) {
3163                    if (errorResNames.length() != 0) {
3164                        errorResNames.append(", ");
3165                    }
3166                    errorResNames.append("[" + dbc.removeSiteRoot(errorRes.getRootPath()) + "]");
3167                }
3168            }
3169
3170            // the current implementation only deletes empty folders
3171            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(errorResNames.toString())) {
3172                throw new CmsVfsException(
3173                    org.opencms.db.generic.Messages.get().container(
3174                        org.opencms.db.generic.Messages.ERR_DELETE_NONEMTY_FOLDER_2,
3175                        dbc.removeSiteRoot(resource.getRootPath()),
3176                        errorResNames.toString()));
3177            }
3178        }
3179
3180        // delete all collected resources
3181        for (int i = 0; i < size; i++) {
3182            CmsResource currentResource = resources.get(i);
3183
3184            // try to delete/remove the resource only if the user has write access to the resource
3185            // check permissions only for the sibling, the resource it self was already checked or
3186            // is to be removed without write permissions, ie. while deleting a folder
3187            if (!currentResource.equals(resource)
3188                && (I_CmsPermissionHandler.PERM_ALLOWED != m_securityManager.hasPermissions(
3189                    dbc,
3190                    currentResource,
3191                    CmsPermissionSet.ACCESS_WRITE,
3192                    LockCheck.yes,
3193                    CmsResourceFilter.ALL))) {
3194
3195                // no write access to sibling - must keep ACE (see below)
3196                allSiblingsRemoved = false;
3197            } else {
3198                // write access to sibling granted
3199                boolean existsOnline = (getVfsDriver(dbc).validateStructureIdExists(
3200                    dbc,
3201                    CmsProject.ONLINE_PROJECT_ID,
3202                    currentResource.getStructureId()) || !(currentResource.getState().equals(CmsResource.STATE_NEW)));
3203                if (!existsOnline) {
3204                    // the resource does not exist online => remove the resource
3205                    // this means the resource is "new" (blue) in the offline project
3206
3207                    // delete all properties of this resource
3208                    deleteAllProperties(dbc, currentResource.getRootPath());
3209
3210                    if (currentResource.isFolder()) {
3211                        getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentResource);
3212                    } else {
3213                        // check labels
3214                        if (currentResource.isLabeled() && !labelResource(dbc, currentResource, null, 2)) {
3215                            // update the resource flags to "un label" the other siblings
3216                            int flags = currentResource.getFlags();
3217                            flags &= ~CmsResource.FLAG_LABELED;
3218                            currentResource.setFlags(flags);
3219                        }
3220                        getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentResource);
3221                    }
3222
3223                    // ensure an exclusive lock is removed in the lock manager for a deleted new resource,
3224                    // otherwise it would "stick" in the lock manager, preventing other users from creating
3225                    // a file with the same name (issue with temp files in editor)
3226                    m_lockManager.removeDeletedResource(dbc, currentResource.getRootPath());
3227                    // delete relations
3228                    getVfsDriver(dbc).deleteRelations(
3229                        dbc,
3230                        dbc.currentProject().getUuid(),
3231                        currentResource,
3232                        CmsRelationFilter.TARGETS);
3233                    getVfsDriver(dbc).deleteUrlNameMappingEntries(
3234                        dbc,
3235                        false,
3236                        CmsUrlNameMappingFilter.ALL.filterStructureId(currentResource.getStructureId()));
3237                    getVfsDriver(dbc).deleteAliases(
3238                        dbc,
3239                        dbc.currentProject(),
3240                        new CmsAliasFilter(null, null, currentResource.getStructureId()));
3241                    log(
3242                        dbc,
3243                        new CmsLogEntry(
3244                            dbc,
3245                            currentResource.getStructureId(),
3246                            CmsLogEntryType.RESOURCE_NEW_DELETED,
3247                            new String[] {currentResource.getRootPath()}),
3248                        true);
3249                } else {
3250                    // the resource exists online => mark the resource as deleted
3251                    // structure record is removed during next publish
3252                    // if one (or more) siblings are not removed, the ACE can not be removed
3253                    removeAce = false;
3254                    // set resource state to deleted
3255                    currentResource.setState(CmsResource.STATE_DELETED);
3256                    getVfsDriver(
3257                        dbc).writeResourceState(dbc, dbc.currentProject(), currentResource, UPDATE_STRUCTURE, false);
3258
3259                    // update the project ID
3260                    getVfsDriver(dbc).writeLastModifiedProjectId(
3261                        dbc,
3262                        dbc.currentProject(),
3263                        dbc.currentProject().getUuid(),
3264                        currentResource);
3265                    // log it
3266
3267                    log(
3268                        dbc,
3269                        new CmsLogEntry(
3270                            dbc,
3271                            currentResource.getStructureId(),
3272                            CmsLogEntryType.RESOURCE_DELETED,
3273                            new String[] {currentResource.getRootPath()}),
3274                        true);
3275                }
3276            }
3277        }
3278
3279        if ((resource.getSiblingCount() <= 1) || allSiblingsRemoved) {
3280            if (removeAce) {
3281                // remove the access control entries
3282                getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId());
3283            }
3284        }
3285
3286        // flush all caches
3287        m_monitor.clearAccessControlListCache();
3288        m_monitor.flushCache(
3289            CmsMemoryMonitor.CacheType.PROPERTY,
3290            CmsMemoryMonitor.CacheType.PROPERTY_LIST,
3291            CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
3292
3293        Map<String, Object> eventData = new HashMap<String, Object>();
3294        eventData.put(I_CmsEventListener.KEY_RESOURCES, resources);
3295        eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
3296        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_DELETED, eventData));
3297    }
3298
3299    /**
3300     * Deletes an entry in the published resource table.<p>
3301     *
3302     * @param dbc the current database context
3303     * @param resourceName The name of the resource to be deleted in the static export
3304     * @param linkType the type of resource deleted (0= non-parameter, 1=parameter)
3305     * @param linkParameter the parameters of the resource
3306     *
3307     * @throws CmsException if something goes wrong
3308     */
3309    public void deleteStaticExportPublishedResource(
3310        CmsDbContext dbc,
3311        String resourceName,
3312        int linkType,
3313        String linkParameter)
3314    throws CmsException {
3315
3316        getProjectDriver(dbc).deleteStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter);
3317    }
3318
3319    /**
3320     * Deletes a user, where all permissions and resources attributes of the user
3321     * were transfered to a replacement user, if given.<p>
3322     *
3323     * Only users, which are in the group "administrators" are granted.<p>
3324     *
3325     * @param dbc the current database context
3326     * @param project the current project
3327     * @param username the name of the user to be deleted
3328     * @param replacementUsername the name of the user to be transfered, can be <code>null</code>
3329     *
3330     * @throws CmsException if operation was not successful
3331     */
3332    public void deleteUser(CmsDbContext dbc, CmsProject project, String username, String replacementUsername)
3333    throws CmsException {
3334
3335        // Test if the users exists
3336        CmsUser user = readUser(dbc, username);
3337        CmsUser replacementUser = null;
3338        if (replacementUsername != null) {
3339            replacementUser = readUser(dbc, replacementUsername);
3340        }
3341
3342        CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
3343        boolean withACEs = true;
3344        if (replacementUser == null) {
3345            withACEs = false;
3346            replacementUser = readUser(dbc, OpenCms.getDefaultUsers().getUserDeletedResource());
3347        }
3348
3349        boolean isVfsManager = m_securityManager.hasRole(dbc, replacementUser, CmsRole.VFS_MANAGER);
3350
3351        // iterate groups and roles
3352        for (int i = 0; i < 2; i++) {
3353            boolean readRoles = i != 0;
3354            Iterator<CmsGroup> itGroups = getGroupsOfUser(
3355                dbc,
3356                username,
3357                "",
3358                true,
3359                readRoles,
3360                true,
3361                dbc.getRequestContext().getRemoteAddress()).iterator();
3362            while (itGroups.hasNext()) {
3363                CmsGroup group = itGroups.next();
3364                if (!isVfsManager) {
3365                    // add replacement user to user groups
3366                    if (!userInGroup(dbc, replacementUser.getName(), group.getName(), readRoles)) {
3367                        addUserToGroup(dbc, replacementUser.getName(), group.getName(), readRoles);
3368                    }
3369                }
3370                // remove user from groups
3371                if (userInGroup(dbc, username, group.getName(), readRoles)) {
3372                    // we need this additional check because removing a user from a group
3373                    // may also automatically remove him from other groups if the group was
3374                    // associated with a role.
3375                    removeUserFromGroup(dbc, username, group.getName(), readRoles);
3376                }
3377            }
3378        }
3379        // remove all locks set for the deleted user
3380        m_lockManager.removeLocks(user.getId());
3381        // offline
3382        if (dbc.getProjectId().isNullUUID()) {
3383            // offline project available
3384            transferPrincipalResources(dbc, project, user.getId(), replacementUser.getId(), withACEs);
3385        }
3386        // online
3387        transferPrincipalResources(dbc, onlineProject, user.getId(), replacementUser.getId(), withACEs);
3388        getUserDriver(dbc).removeAccessControlEntriesForPrincipal(dbc, project, onlineProject, user.getId());
3389        getHistoryDriver(dbc).writePrincipal(dbc, user);
3390        getUserDriver(dbc).deleteUser(dbc, username);
3391        // delete user from cache
3392        m_monitor.clearUserCache(user);
3393
3394        if (!dbc.getProjectId().isNullUUID()) {
3395            // user modified event is not needed
3396            return;
3397        }
3398        // fire user modified event
3399        Map<String, Object> eventData = new HashMap<String, Object>();
3400        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
3401        eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
3402        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_DELETE_USER);
3403        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
3404    }
3405
3406    /**
3407     * Destroys this driver manager and releases all allocated resources.<p>
3408     */
3409    public void destroy() {
3410
3411        try {
3412            if (m_projectDriver != null) {
3413                try {
3414                    m_projectDriver.destroy();
3415                } catch (Throwable t) {
3416                    LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_PROJECT_DRIVER_0), t);
3417                }
3418                m_projectDriver = null;
3419            }
3420            if (m_userDriver != null) {
3421                try {
3422                    m_userDriver.destroy();
3423                } catch (Throwable t) {
3424                    LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_USER_DRIVER_0), t);
3425                }
3426                m_userDriver = null;
3427            }
3428            if (m_vfsDriver != null) {
3429                try {
3430                    m_vfsDriver.destroy();
3431                } catch (Throwable t) {
3432                    LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_VFS_DRIVER_0), t);
3433                }
3434                m_vfsDriver = null;
3435            }
3436            if (m_historyDriver != null) {
3437                try {
3438                    m_historyDriver.destroy();
3439                } catch (Throwable t) {
3440                    LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_HISTORY_DRIVER_0), t);
3441                }
3442                m_historyDriver = null;
3443            }
3444
3445            if (m_pools != null) {
3446                for (CmsDbPoolV11 pool : m_pools.values()) {
3447                    try {
3448                        pool.close();
3449                        if (CmsLog.INIT.isDebugEnabled()) {
3450                            CmsLog.INIT.debug(Messages.get().getBundle().key(Messages.INIT_CLOSE_CONN_POOL_1, pool));
3451                        }
3452
3453                    } catch (Throwable t) {
3454                        LOG.error(Messages.get().getBundle().key(Messages.LOG_CLOSE_CONN_POOL_ERROR_1, pool), t);
3455                    }
3456                }
3457                m_pools.clear();
3458            }
3459
3460            m_monitor.clearCache();
3461
3462            m_lockManager = null;
3463            m_htmlLinkValidator = null;
3464        } catch (Throwable t) {
3465            // ignore
3466        }
3467        if (CmsLog.INIT.isInfoEnabled()) {
3468            CmsLog.INIT.info(
3469                Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_DESTROY_1, getClass().getName()));
3470        }
3471    }
3472
3473    /**
3474     * Tests if a resource with the given resourceId does already exist in the Database.<p>
3475     *
3476     * @param dbc the current database context
3477     * @param resourceId the resource id to test for
3478     * @return true if a resource with the given id was found, false otherweise
3479     * @throws CmsException if something goes wrong
3480     */
3481    public boolean existsResourceId(CmsDbContext dbc, CmsUUID resourceId) throws CmsException {
3482
3483        return getVfsDriver(dbc).validateResourceIdExists(dbc, dbc.currentProject().getUuid(), resourceId);
3484    }
3485
3486    /**
3487     * Fills the given publish list with the the VFS resources that actually get published.<p>
3488     *
3489     * Please refer to the source code of this method for the rules on how to decide whether a
3490     * new/changed/deleted <code>{@link CmsResource}</code> object can be published or not.<p>
3491     *
3492     * @param dbc the current database context
3493     * @param publishList must be initialized with basic publish information (Project or direct publish operation),
3494     *                    the given publish list will be filled with all new/changed/deleted files from the current
3495     *                    (offline) project that will be actually published
3496     *
3497     * @throws CmsException if something goes wrong
3498     *
3499     * @see org.opencms.db.CmsPublishList
3500     */
3501    public void fillPublishList(CmsDbContext dbc, CmsPublishList publishList) throws CmsException {
3502
3503        if (!publishList.isDirectPublish()) {
3504            // when publishing a project
3505            // all modified resources with the last change done in the current project are candidates if unlocked
3506            List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree(
3507                dbc,
3508                dbc.currentProject().getUuid(),
3509                CmsDriverManager.READ_IGNORE_PARENT,
3510                CmsDriverManager.READ_IGNORE_TYPE,
3511                CmsResource.STATE_UNCHANGED,
3512                CmsDriverManager.READ_IGNORE_TIME,
3513                CmsDriverManager.READ_IGNORE_TIME,
3514                CmsDriverManager.READ_IGNORE_TIME,
3515                CmsDriverManager.READ_IGNORE_TIME,
3516                CmsDriverManager.READ_IGNORE_TIME,
3517                CmsDriverManager.READ_IGNORE_TIME,
3518                CmsDriverManager.READMODE_INCLUDE_TREE
3519                    | CmsDriverManager.READMODE_INCLUDE_PROJECT
3520                    | CmsDriverManager.READMODE_EXCLUDE_STATE
3521                    | CmsDriverManager.READMODE_ONLY_FOLDERS);
3522
3523            List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree(
3524                dbc,
3525                dbc.currentProject().getUuid(),
3526                CmsDriverManager.READ_IGNORE_PARENT,
3527                CmsDriverManager.READ_IGNORE_TYPE,
3528                CmsResource.STATE_UNCHANGED,
3529                CmsDriverManager.READ_IGNORE_TIME,
3530                CmsDriverManager.READ_IGNORE_TIME,
3531                CmsDriverManager.READ_IGNORE_TIME,
3532                CmsDriverManager.READ_IGNORE_TIME,
3533                CmsDriverManager.READ_IGNORE_TIME,
3534                CmsDriverManager.READ_IGNORE_TIME,
3535                CmsDriverManager.READMODE_INCLUDE_TREE
3536                    | CmsDriverManager.READMODE_INCLUDE_PROJECT
3537                    | CmsDriverManager.READMODE_EXCLUDE_STATE
3538                    | CmsDriverManager.READMODE_ONLY_FILES);
3539            CmsRequestContext context = dbc.getRequestContext();
3540            if ((context != null)
3541                && (context.getAttribute(CmsDefaultWorkflowManager.ATTR_CHECK_PUBLISH_RESOURCE_LIMIT) != null)) {
3542
3543                // check if total size and if it exceeds the resource limit and the request
3544                // context attribute is set, throw an exception.
3545                // we do it here since filterResources() can be very expensive on large resource lists
3546
3547                int limit = OpenCms.getWorkflowManager().getResourceLimit();
3548                int total = fileList.size() + folderList.size();
3549                if (total > limit) {
3550                    throw new CmsTooManyPublishResourcesException(total);
3551                }
3552            }
3553            publishList.addAll(filterResources(dbc, null, folderList), true);
3554            publishList.addAll(filterResources(dbc, publishList, fileList), true);
3555        } else {
3556            // this is a direct publish
3557            Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator();
3558            while (it.hasNext()) {
3559                // iterate all resources in the direct publish list
3560                CmsResource directPublishResource = it.next();
3561                if (directPublishResource.isFolder()) {
3562                    // when publishing a folder directly,
3563                    // the folder and all modified resources within the tree below this folder
3564                    // and with the last change done in the current project are candidates if lockable
3565                    CmsLock lock = getLock(dbc, directPublishResource);
3566                    if (!directPublishResource.getState().isUnchanged() && lock.isLockableBy(dbc.currentUser())) {
3567
3568                        try {
3569                            m_securityManager.checkPermissions(
3570                                dbc,
3571                                directPublishResource,
3572                                CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
3573                                false,
3574                                CmsResourceFilter.ALL);
3575                            publishList.add(directPublishResource, true);
3576                        } catch (CmsException e) {
3577                            // skip if not enough permissions
3578                        }
3579                    }
3580                    boolean shouldPublishDeletedSubResources = publishList.isUserPublishList()
3581                        && directPublishResource.getState().isDeleted();
3582                    if (publishList.isPublishSubResources() || shouldPublishDeletedSubResources) {
3583                        addSubResources(dbc, publishList, directPublishResource, resource -> true);
3584                    }
3585                } else if (directPublishResource.isFile() && !directPublishResource.getState().isUnchanged()) {
3586
3587                    // when publishing a file directly this file is the only candidate
3588                    // if it is modified and lockable
3589                    CmsLock lock = getLock(dbc, directPublishResource);
3590                    if (lock.isLockableBy(dbc.currentUser())) {
3591                        // check permissions
3592                        try {
3593                            m_securityManager.checkPermissions(
3594                                dbc,
3595                                directPublishResource,
3596                                CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
3597                                false,
3598                                CmsResourceFilter.ALL);
3599                            publishList.add(directPublishResource, true);
3600                        } catch (CmsException e) {
3601                            // skip if not enough permissions
3602                        }
3603                    }
3604                }
3605            }
3606        }
3607
3608        // Step 2: if desired, extend the list of files to publish with related siblings
3609        if (publishList.isPublishSiblings()) {
3610            List<CmsResource> publishFiles = publishList.getFileList();
3611            int size = publishFiles.size();
3612
3613            // Improved: first calculate closure of all siblings, then filter and add them
3614            Set<CmsResource> siblingsClosure = new HashSet<CmsResource>(publishFiles);
3615            for (int i = 0; i < size; i++) {
3616                CmsResource currentFile = publishFiles.get(i);
3617                if (currentFile.getSiblingCount() > 1) {
3618                    siblingsClosure.addAll(readSiblings(dbc, currentFile, CmsResourceFilter.ALL_MODIFIED));
3619                }
3620            }
3621            publishList.addAll(filterSiblings(dbc, publishList, siblingsClosure), true);
3622        }
3623        publishList.initialize();
3624    }
3625
3626    /**
3627     * Returns the list of access control entries of a resource given its name.<p>
3628     *
3629     * @param dbc the current database context
3630     * @param resource the resource to read the access control entries for
3631     * @param getInherited true if the result should include all access control entries inherited by parent folders
3632     *
3633     * @return a list of <code>{@link CmsAccessControlEntry}</code> objects defining all permissions for the given resource
3634     *
3635     * @throws CmsException if something goes wrong
3636     */
3637    public List<CmsAccessControlEntry> getAccessControlEntries(
3638        CmsDbContext dbc,
3639        CmsResource resource,
3640        boolean getInherited)
3641    throws CmsException {
3642
3643        // get the ACE of the resource itself
3644        I_CmsUserDriver userDriver = getUserDriver(dbc);
3645        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
3646        List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries(
3647            dbc,
3648            dbc.currentProject(),
3649            resource.getResourceId(),
3650            false);
3651
3652        // sort and check if we got the 'overwrite all' ace to stop looking up
3653        boolean overwriteAll = sortAceList(ace);
3654
3655        // get the ACE of each parent folder
3656        // Note: for the immediate parent, get non-inherited access control entries too,
3657        // if the resource is not a folder
3658        String parentPath = CmsResource.getParentFolder(resource.getRootPath());
3659        int d = (resource.isFolder()) ? 1 : 0;
3660
3661        while (!overwriteAll && getInherited && (parentPath != null)) {
3662            resource = vfsDriver.readFolder(dbc, dbc.currentProject().getUuid(), parentPath);
3663            List<CmsAccessControlEntry> entries = userDriver.readAccessControlEntries(
3664                dbc,
3665                dbc.currentProject(),
3666                resource.getResourceId(),
3667                d > 0);
3668
3669            // sort and check if we got the 'overwrite all' ace to stop looking up
3670            overwriteAll = sortAceList(entries);
3671
3672            for (CmsAccessControlEntry e : entries) {
3673                e.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED);
3674            }
3675
3676            ace.addAll(entries);
3677            parentPath = CmsResource.getParentFolder(resource.getRootPath());
3678            d++;
3679        }
3680
3681        return ace;
3682    }
3683
3684    /**
3685     * Returns the full access control list of a given resource.<p>
3686     *
3687     * @param dbc the current database context
3688     * @param resource the resource
3689     *
3690     * @return the access control list of the resource
3691     *
3692     * @throws CmsException if something goes wrong
3693     */
3694    public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource) throws CmsException {
3695
3696        return getAccessControlList(dbc, resource, false);
3697    }
3698
3699    /**
3700     * Returns the access control list of a given resource.<p>
3701     *
3702     * If <code>inheritedOnly</code> is set, only inherited access control entries
3703     * are returned.<p>
3704     *
3705     * Note: For file resources, *all* permissions set at the immediate parent folder are inherited,
3706     * not only these marked to inherit.
3707     *
3708     * @param dbc the current database context
3709     * @param resource the resource
3710     * @param inheritedOnly skip non-inherited entries if set
3711     *
3712     * @return the access control list of the resource
3713     *
3714     * @throws CmsException if something goes wrong
3715     */
3716    public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource, boolean inheritedOnly)
3717    throws CmsException {
3718
3719        return getAccessControlList(dbc, resource, inheritedOnly, resource.isFolder(), 0);
3720    }
3721
3722    /**
3723     * Returns the number of active connections managed by a pool.<p>
3724     *
3725     * @param dbPoolUrl the url of a pool
3726     * @return the number of active connections
3727     * @throws CmsDbException if something goes wrong
3728     */
3729    public int getActiveConnections(String dbPoolUrl) throws CmsDbException {
3730
3731        CmsDbPoolV11 pool = m_pools.get(dbPoolUrl);
3732        if (pool == null) {
3733            CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl);
3734            throw new CmsDbException(message);
3735        }
3736        try {
3737            return pool.getActiveConnections();
3738        } catch (Exception exc) {
3739            CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl);
3740            throw new CmsDbException(message, exc);
3741        }
3742
3743    }
3744
3745    /**
3746     * Reads all access control entries.<p>
3747     *
3748     * @param dbc the current database context
3749     * @return all access control entries for the current project (offline/online)
3750     *
3751     * @throws CmsException if something goes wrong
3752     */
3753    public List<CmsAccessControlEntry> getAllAccessControlEntries(CmsDbContext dbc) throws CmsException {
3754
3755        I_CmsUserDriver userDriver = getUserDriver(dbc);
3756        List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries(
3757            dbc,
3758            dbc.currentProject(),
3759            CmsAccessControlEntry.PRINCIPAL_READALL_ID,
3760            false);
3761        return ace;
3762    }
3763
3764    /**
3765     * Returns all projects which are owned by the current user or which are
3766     * accessible by the current user.<p>
3767     *
3768     * @param dbc the current database context
3769     * @param orgUnit the organizational unit to search project in
3770     * @param includeSubOus if to include sub organizational units
3771     *
3772     * @return a list of objects of type <code>{@link CmsProject}</code>
3773     *
3774     * @throws CmsException if something goes wrong
3775     */
3776    public List<CmsProject> getAllAccessibleProjects(
3777        CmsDbContext dbc,
3778        CmsOrganizationalUnit orgUnit,
3779        boolean includeSubOus)
3780    throws CmsException {
3781
3782        Set<CmsProject> projects = new HashSet<CmsProject>();
3783
3784        // get the ous where the user has the project manager role
3785        List<CmsOrganizationalUnit> ous = getOrgUnitsForRole(
3786            dbc,
3787            CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()),
3788            includeSubOus);
3789
3790        // get the groups of the user if needed
3791        Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>();
3792        Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator();
3793        while (itGroups.hasNext()) {
3794            CmsGroup group = itGroups.next();
3795            userGroupIds.add(group.getId());
3796        }
3797
3798        // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus'
3799        // get all projects that might come in question
3800        projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName()));
3801
3802        // filter hidden and not accessible projects
3803        Iterator<CmsProject> itProjects = projects.iterator();
3804        while (itProjects.hasNext()) {
3805            CmsProject project = itProjects.next();
3806            boolean accessible = true;
3807            // if hidden
3808            accessible = accessible && !project.isHidden();
3809
3810            if (!includeSubOus) {
3811                // if not exact in the given ou
3812                accessible = accessible && project.getOuFqn().equals(orgUnit.getName());
3813            } else {
3814                // if not in the given ou
3815                accessible = accessible && project.getOuFqn().startsWith(orgUnit.getName());
3816            }
3817
3818            if (!accessible) {
3819                itProjects.remove();
3820                continue;
3821            }
3822
3823            accessible = false;
3824            // online project
3825            accessible = accessible || project.isOnlineProject();
3826            // if owner
3827            accessible = accessible || project.getOwnerId().equals(dbc.currentUser().getId());
3828
3829            // project managers
3830            Iterator<CmsOrganizationalUnit> itOus = ous.iterator();
3831            while (!accessible && itOus.hasNext()) {
3832                CmsOrganizationalUnit ou = itOus.next();
3833                // for project managers check visibility
3834                accessible = accessible || project.getOuFqn().startsWith(ou.getName());
3835            }
3836
3837            if (!accessible) {
3838                // if direct user or manager of project
3839                CmsUUID groupId = null;
3840                if (userGroupIds.contains(project.getGroupId())) {
3841                    groupId = project.getGroupId();
3842                } else if (userGroupIds.contains(project.getManagerGroupId())) {
3843                    groupId = project.getManagerGroupId();
3844                }
3845                if (groupId != null) {
3846                    String oufqn = readGroup(dbc, groupId).getOuFqn();
3847                    accessible = accessible || (oufqn.startsWith(dbc.getRequestContext().getOuFqn()));
3848                }
3849            }
3850            if (!accessible) {
3851                // remove not accessible project
3852                itProjects.remove();
3853            }
3854        }
3855
3856        List<CmsProject> accessibleProjects = new ArrayList<CmsProject>(projects);
3857        // sort the list of projects based on the project name
3858        Collections.sort(accessibleProjects);
3859        // ensure the online project is in first place
3860        CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
3861        if (accessibleProjects.contains(onlineProject)) {
3862            accessibleProjects.remove(onlineProject);
3863        }
3864        accessibleProjects.add(0, onlineProject);
3865
3866        return accessibleProjects;
3867    }
3868
3869    /**
3870     * Returns a list with all projects from history.<p>
3871     *
3872     * @param dbc the current database context
3873     *
3874     * @return list of <code>{@link CmsHistoryProject}</code> objects
3875     *           with all projects from history.
3876     *
3877     * @throws CmsException if operation was not successful
3878     */
3879    public List<CmsHistoryProject> getAllHistoricalProjects(CmsDbContext dbc) throws CmsException {
3880
3881        // user is allowed to access all existing projects for the ous he has the project_manager role
3882        Set<CmsOrganizationalUnit> manOus = new HashSet<CmsOrganizationalUnit>(
3883            getOrgUnitsForRole(dbc, CmsRole.PROJECT_MANAGER, true));
3884
3885        List<CmsHistoryProject> projects = getHistoryDriver(dbc).readProjects(dbc);
3886        Iterator<CmsHistoryProject> itProjects = projects.iterator();
3887        while (itProjects.hasNext()) {
3888            CmsHistoryProject project = itProjects.next();
3889            if (project.isHidden()) {
3890                // project is hidden
3891                itProjects.remove();
3892                continue;
3893            }
3894            if (!project.getOuFqn().startsWith(dbc.currentUser().getOuFqn())) {
3895                // project is not visible from the users ou
3896                itProjects.remove();
3897                continue;
3898            }
3899            CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, project.getOuFqn());
3900            if (manOus.contains(ou)) {
3901                // user is project manager for this project
3902                continue;
3903            } else if (project.getOwnerId().equals(dbc.currentUser().getId())) {
3904                // user is owner of the project
3905                continue;
3906            } else {
3907                boolean found = false;
3908                Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator();
3909                while (itGroups.hasNext()) {
3910                    CmsGroup group = itGroups.next();
3911                    if (project.getManagerGroupId().equals(group.getId())) {
3912                        found = true;
3913                        break;
3914                    }
3915                }
3916                if (found) {
3917                    // user is member of the manager group of the project
3918                    continue;
3919                }
3920            }
3921            itProjects.remove();
3922        }
3923        return projects;
3924    }
3925
3926    /**
3927     * Returns all projects which are owned by the current user or which are manageable
3928     * for the group of the user.<p>
3929     *
3930     * @param dbc the current database context
3931     * @param orgUnit the organizational unit to search project in
3932     * @param includeSubOus if to include sub organizational units
3933     *
3934     * @return a list of objects of type <code>{@link CmsProject}</code>
3935     *
3936     * @throws CmsException if operation was not successful
3937     */
3938    public List<CmsProject> getAllManageableProjects(
3939        CmsDbContext dbc,
3940        CmsOrganizationalUnit orgUnit,
3941        boolean includeSubOus)
3942    throws CmsException {
3943
3944        Set<CmsProject> projects = new HashSet<CmsProject>();
3945
3946        // get the ous where the user has the project manager role
3947        List<CmsOrganizationalUnit> ous = getOrgUnitsForRole(
3948            dbc,
3949            CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()),
3950            includeSubOus);
3951
3952        // get the groups of the user if needed
3953        Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>();
3954        Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator();
3955        while (itGroups.hasNext()) {
3956            CmsGroup group = itGroups.next();
3957            userGroupIds.add(group.getId());
3958        }
3959
3960        // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus'
3961        // get all projects that might come in question
3962        projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName()));
3963
3964        // filter hidden and not manageable projects
3965        Iterator<CmsProject> itProjects = projects.iterator();
3966        while (itProjects.hasNext()) {
3967            CmsProject project = itProjects.next();
3968            boolean manageable = true;
3969            // if online
3970            manageable = manageable && !project.isOnlineProject();
3971            // if hidden
3972            manageable = manageable && !project.isHidden();
3973
3974            if (!includeSubOus) {
3975                // if not exact in the given ou
3976                manageable = manageable && project.getOuFqn().equals(orgUnit.getName());
3977            } else {
3978                // if not in the given ou
3979                manageable = manageable && project.getOuFqn().startsWith(orgUnit.getName());
3980            }
3981
3982            if (!manageable) {
3983                itProjects.remove();
3984                continue;
3985            }
3986
3987            manageable = false;
3988            // if owner
3989            manageable = manageable || project.getOwnerId().equals(dbc.currentUser().getId());
3990
3991            // project managers
3992            Iterator<CmsOrganizationalUnit> itOus = ous.iterator();
3993            while (!manageable && itOus.hasNext()) {
3994                CmsOrganizationalUnit ou = itOus.next();
3995                // for project managers check visibility
3996                manageable = manageable || project.getOuFqn().startsWith(ou.getName());
3997            }
3998
3999            if (!manageable) {
4000                // if manager of project
4001                if (userGroupIds.contains(project.getManagerGroupId())) {
4002                    String oufqn = readGroup(dbc, project.getManagerGroupId()).getOuFqn();
4003                    manageable = manageable || (oufqn.startsWith(dbc.getRequestContext().getOuFqn()));
4004                }
4005            }
4006            if (!manageable) {
4007                // remove not accessible project
4008                itProjects.remove();
4009            }
4010        }
4011
4012        List<CmsProject> manageableProjects = new ArrayList<CmsProject>(projects);
4013        // sort the list of projects based on the project name
4014        Collections.sort(manageableProjects);
4015        // ensure the online project is not in the list
4016        CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
4017        if (manageableProjects.contains(onlineProject)) {
4018            manageableProjects.remove(onlineProject);
4019        }
4020
4021        return manageableProjects;
4022    }
4023
4024    /**
4025     * Returns all child groups of a group.<p>
4026     *
4027     * @param dbc the current database context
4028     * @param group the group to get the child for
4029     * @param includeSubChildren if set also returns all sub-child groups of the given group
4030     *
4031     * @return a list of all child <code>{@link CmsGroup}</code> objects
4032     *
4033     * @throws CmsException if operation was not successful
4034     */
4035    public List<CmsGroup> getChildren(CmsDbContext dbc, CmsGroup group, boolean includeSubChildren)
4036    throws CmsException {
4037
4038        if (!includeSubChildren) {
4039            return getUserDriver(dbc).readChildGroups(dbc, group.getName());
4040        }
4041        Set<CmsGroup> allChildren = new TreeSet<CmsGroup>();
4042        // iterate all child groups
4043        Iterator<CmsGroup> it = getUserDriver(dbc).readChildGroups(dbc, group.getName()).iterator();
4044        while (it.hasNext()) {
4045            CmsGroup child = it.next();
4046            // add the group itself
4047            allChildren.add(child);
4048            // now get all sub-children for each group
4049            allChildren.addAll(getChildren(dbc, child, true));
4050        }
4051        return new ArrayList<CmsGroup>(allChildren);
4052    }
4053
4054    /**
4055     * Returns the date when the resource was last visited by the user.<p>
4056     *
4057     * @param dbc the database context
4058     * @param poolName the name of the database pool to use
4059     * @param user the user to check the date
4060     * @param resource the resource to check the date
4061     *
4062     * @return the date when the resource was last visited by the user
4063     *
4064     * @throws CmsException if something goes wrong
4065     */
4066    public long getDateLastVisitedBy(CmsDbContext dbc, String poolName, CmsUser user, CmsResource resource)
4067    throws CmsException {
4068
4069        return m_subscriptionDriver.getDateLastVisitedBy(dbc, poolName, user, resource);
4070    }
4071
4072    /**
4073     * Returns all groups of the given organizational unit.<p>
4074     *
4075     * @param dbc the current db context
4076     * @param orgUnit the organizational unit to get the groups for
4077     * @param includeSubOus if all groups of sub-organizational units should be retrieved too
4078     * @param readRoles if to read roles or groups
4079     *
4080     * @return all <code>{@link CmsGroup}</code> objects in the organizational unit
4081     *
4082     * @throws CmsException if operation was not successful
4083     *
4084     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
4085     * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
4086     */
4087    public List<CmsGroup> getGroups(
4088        CmsDbContext dbc,
4089        CmsOrganizationalUnit orgUnit,
4090        boolean includeSubOus,
4091        boolean readRoles)
4092    throws CmsException {
4093
4094        return getUserDriver(dbc).getGroups(dbc, orgUnit, includeSubOus, readRoles);
4095    }
4096
4097    /**
4098     * Returns the groups of an user filtered by the specified IP address.<p>
4099     *
4100     * @param dbc the current database context
4101     * @param username the name of the user
4102     * @param readRoles if to read roles or groups
4103     *
4104     * @return the groups of the given user, as a list of {@link CmsGroup} objects
4105     *
4106     * @throws CmsException if something goes wrong
4107     */
4108    public List<CmsGroup> getGroupsOfUser(CmsDbContext dbc, String username, boolean readRoles) throws CmsException {
4109
4110        return getGroupsOfUser(dbc, username, "", true, readRoles, false, dbc.getRequestContext().getRemoteAddress());
4111    }
4112
4113    /**
4114     * Returns the groups of an user filtered by the specified IP address.<p>
4115     *
4116     * @param dbc the current database context
4117     * @param username the name of the user
4118     * @param ouFqn the fully qualified name of the organizational unit to restrict the result set for
4119     * @param includeChildOus include groups of child organizational units
4120     * @param readRoles if to read roles or groups
4121     * @param directGroupsOnly if set only the direct assigned groups will be returned, if not also indirect groups
4122     * @param remoteAddress the IP address to filter the groups in the result list
4123     *
4124     * @return a list of <code>{@link CmsGroup}</code> objects
4125     *
4126     * @throws CmsException if operation was not successful
4127     */
4128    public List<CmsGroup> getGroupsOfUser(
4129        CmsDbContext dbc,
4130        String username,
4131        String ouFqn,
4132        boolean includeChildOus,
4133        boolean readRoles,
4134        boolean directGroupsOnly,
4135        String remoteAddress)
4136    throws CmsException {
4137
4138        CmsUser user = readUser(dbc, username);
4139        String prefix = ouFqn + "_" + includeChildOus + "_" + directGroupsOnly + "_" + readRoles + "_" + remoteAddress;
4140        String cacheKey = m_keyGenerator.getCacheKeyForUserGroups(prefix, dbc, user);
4141        List<CmsGroup> groups = m_monitor.getCachedUserGroups(user.getId(), cacheKey);
4142        if (groups == null) {
4143            // get all groups of the user
4144            List<CmsGroup> directGroups = getUserDriver(dbc).readGroupsOfUser(
4145                dbc,
4146                user.getId(),
4147                readRoles ? "" : ouFqn,
4148                readRoles ? true : includeChildOus,
4149                remoteAddress,
4150                readRoles);
4151            Set<CmsGroup> allGroups = new HashSet<CmsGroup>();
4152            if (!readRoles) {
4153                allGroups.addAll(directGroups);
4154            }
4155            if (!directGroupsOnly) {
4156                if (!readRoles) {
4157                    // now get all parents of the groups
4158                    for (int i = 0; i < directGroups.size(); i++) {
4159                        CmsGroup parent = getParent(dbc, directGroups.get(i).getName());
4160                        while ((parent != null) && (!allGroups.contains(parent))) {
4161                            if (parent.getOuFqn().startsWith(ouFqn)) {
4162                                allGroups.add(parent);
4163                            }
4164                            // read next parent group
4165                            parent = getParent(dbc, parent.getName());
4166                        }
4167                    }
4168                }
4169            }
4170            if (readRoles) {
4171                // for each for role
4172                for (int i = 0; i < directGroups.size(); i++) {
4173                    CmsGroup group = directGroups.get(i);
4174                    CmsRole role = CmsRole.valueOf(group);
4175                    if (!includeChildOus && role.getOuFqn().equals(ouFqn)) {
4176                        allGroups.add(group);
4177                    }
4178                    if (includeChildOus && role.getOuFqn().startsWith(ouFqn)) {
4179                        allGroups.add(group);
4180                    }
4181                    if (directGroupsOnly || (!includeChildOus && !role.getOuFqn().equals(ouFqn))) {
4182                        // if roles of child OUs are not requested and the role does not belong to the requested OU don't include the role children
4183                        continue;
4184                    }
4185                    CmsOrganizationalUnit currentOu = readOrganizationalUnit(dbc, group.getOuFqn());
4186                    boolean readChildRoleGroups = true;
4187                    if (currentOu.hasFlagWebuser() && role.forOrgUnit(null).equals(CmsRole.ACCOUNT_MANAGER)) {
4188                        readChildRoleGroups = false;
4189                    }
4190                    if (readChildRoleGroups) {
4191                        // get the child roles
4192                        Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator();
4193                        while (itChildRoles.hasNext()) {
4194                            CmsRole childRole = itChildRoles.next();
4195                            if (childRole.isSystemRole()) {
4196                                if (canReadRoleInOu(currentOu, childRole)) {
4197                                    // include system roles only
4198                                    try {
4199                                        allGroups.add(readGroup(dbc, childRole.getGroupName()));
4200                                    } catch (CmsDataAccessException e) {
4201                                        // should not happen, log error if it does
4202                                        LOG.error(e.getLocalizedMessage(), e);
4203                                    }
4204                                }
4205                            }
4206                        }
4207                    } else {
4208                        LOG.info("Skipping child role group check for web user OU " + currentOu.getName());
4209                    }
4210                    if (includeChildOus) {
4211                        // if needed include the roles of child ous
4212                        Iterator<CmsOrganizationalUnit> itSubOus = getOrganizationalUnits(
4213                            dbc,
4214                            readOrganizationalUnit(dbc, group.getOuFqn()),
4215                            true).iterator();
4216                        while (itSubOus.hasNext()) {
4217                            CmsOrganizationalUnit subOu = itSubOus.next();
4218                            // add role in child ou
4219                            try {
4220                                if (canReadRoleInOu(subOu, role)) {
4221                                    allGroups.add(readGroup(dbc, role.forOrgUnit(subOu.getName()).getGroupName()));
4222                                }
4223                            } catch (CmsDbEntryNotFoundException e) {
4224                                // ignore, this may happen while deleting an orgunit
4225                                if (LOG.isDebugEnabled()) {
4226                                    LOG.debug(e.getLocalizedMessage(), e);
4227                                }
4228                            }
4229                            // add child roles in child ous
4230                            Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator();
4231                            while (itChildRoles.hasNext()) {
4232                                CmsRole childRole = itChildRoles.next();
4233                                try {
4234                                    if (canReadRoleInOu(subOu, childRole)) {
4235                                        allGroups.add(
4236                                            readGroup(dbc, childRole.forOrgUnit(subOu.getName()).getGroupName()));
4237                                    }
4238                                } catch (CmsDbEntryNotFoundException e) {
4239                                    // ignore, this may happen while deleting an orgunit
4240                                    if (LOG.isDebugEnabled()) {
4241                                        LOG.debug(e.getLocalizedMessage(), e);
4242                                    }
4243                                }
4244                            }
4245                        }
4246                    }
4247                }
4248            }
4249            // make group list unmodifiable for caching
4250            groups = Collections.unmodifiableList(new ArrayList<CmsGroup>(allGroups));
4251            if (dbc.getProjectId().isNullUUID()) {
4252                m_monitor.getGroupListCache().setGroups(user, cacheKey, groups);
4253            }
4254        }
4255
4256        return groups;
4257    }
4258
4259    /**
4260     * Returns the history driver.<p>
4261     *
4262     * @return the history driver
4263     */
4264    public I_CmsHistoryDriver getHistoryDriver() {
4265
4266        return m_historyDriver;
4267    }
4268
4269    /**
4270     * Returns the history driver for a given database context.<p>
4271     *
4272     * @param dbc the database context
4273     * @return the history driver for the database context
4274     */
4275    public I_CmsHistoryDriver getHistoryDriver(CmsDbContext dbc) {
4276
4277        if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
4278            return m_historyDriver;
4279        }
4280        I_CmsHistoryDriver driver = dbc.getHistoryDriver(dbc.getProjectId());
4281        return driver != null ? driver : m_historyDriver;
4282
4283    }
4284
4285    /**
4286     * Returns the number of idle connections managed by a pool.<p>
4287     *
4288     * @param dbPoolUrl the url of a pool
4289     * @return the number of idle connections
4290     * @throws CmsDbException if something goes wrong
4291     */
4292    public int getIdleConnections(String dbPoolUrl) throws CmsDbException {
4293
4294        CmsDbPoolV11 pool = m_pools.get(dbPoolUrl);
4295        if (pool == null) {
4296            CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl);
4297            throw new CmsDbException(message);
4298        }
4299        try {
4300            return pool.getIdleConnections();
4301        } catch (Exception exc) {
4302            CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl);
4303            throw new CmsDbException(message, exc);
4304        }
4305
4306    }
4307
4308    /**
4309     * Returns the lock state of a resource.<p>
4310     *
4311     * @param dbc the current database context
4312     * @param resource the resource to return the lock state for
4313     *
4314     * @return the lock state of the resource
4315     *
4316     * @throws CmsException if something goes wrong
4317     */
4318    public CmsLock getLock(CmsDbContext dbc, CmsResource resource) throws CmsException {
4319
4320        return m_lockManager.getLock(dbc, resource);
4321    }
4322
4323    /**
4324     * Returns all locked resources in a given folder.<p>
4325     *
4326     * @param dbc the current database context
4327     * @param resource the folder to search in
4328     * @param filter the lock filter
4329     *
4330     * @return a list of locked resource paths (relative to current site)
4331     *
4332     * @throws CmsException if the current project is locked
4333     */
4334    public List<String> getLockedResources(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter)
4335    throws CmsException {
4336
4337        List<String> lockedResources = new ArrayList<String>();
4338        // get locked resources
4339        Iterator<CmsLock> it = m_lockManager.getLocks(dbc, resource.getRootPath(), filter).iterator();
4340        while (it.hasNext()) {
4341            CmsLock lock = it.next();
4342            lockedResources.add(dbc.removeSiteRoot(lock.getResourceName()));
4343        }
4344        Collections.sort(lockedResources);
4345        return lockedResources;
4346    }
4347
4348    /**
4349     * Returns all locked resources in a given folder.<p>
4350     *
4351     * @param dbc the current database context
4352     * @param resource the folder to search in
4353     * @param filter the lock filter
4354     *
4355     * @return a list of locked resources
4356     *
4357     * @throws CmsException if the current project is locked
4358     */
4359    public List<CmsResource> getLockedResourcesObjects(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter)
4360    throws CmsException {
4361
4362        return m_lockManager.getLockedResources(dbc, resource, filter);
4363    }
4364
4365    /**
4366     * Returns all locked resources in a given folder, but uses a cache for resource lookups.<p>
4367     *
4368     * @param dbc the current database context
4369     * @param resource the folder to search in
4370     * @param filter the lock filter
4371     * @param cache the cache to use for resource lookups
4372     *
4373     * @return a list of locked resources
4374     *
4375     * @throws CmsException if the current project is locked
4376     */
4377    public List<CmsResource> getLockedResourcesObjectsWithCache(
4378        CmsDbContext dbc,
4379        CmsResource resource,
4380        CmsLockFilter filter,
4381        Map<String, CmsResource> cache)
4382    throws CmsException {
4383
4384        return m_lockManager.getLockedResourcesWithCache(dbc, resource, filter, cache);
4385    }
4386
4387    /**
4388     * Returns all log entries matching the given filter.<p>
4389     *
4390     * @param dbc the current db context
4391     * @param filter the filter to match the log entries
4392     *
4393     * @return all log entries matching the given filter
4394     *
4395     * @throws CmsException if something goes wrong
4396     *
4397     * @see CmsSecurityManager#getLogEntries(CmsRequestContext, CmsLogFilter)
4398     */
4399    public List<CmsLogEntry> getLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException {
4400
4401        updateLog(dbc);
4402        return m_projectDriver.readLog(dbc, filter);
4403    }
4404
4405    /**
4406     * Returns the next publish tag for the published historical resources.<p>
4407     *
4408     * @param dbc the current database context
4409     *
4410     * @return the next available publish tag
4411     */
4412    public int getNextPublishTag(CmsDbContext dbc) {
4413
4414        return getHistoryDriver(dbc).readNextPublishTag(dbc);
4415    }
4416
4417    /**
4418     * Returns all child organizational units of the given parent organizational unit including
4419     * hierarchical deeper organization units if needed.<p>
4420     *
4421     * @param dbc the current db context
4422     * @param parent the parent organizational unit, or <code>null</code> for the root
4423     * @param includeChildren if hierarchical deeper organization units should also be returned
4424     *
4425     * @return a list of <code>{@link CmsOrganizationalUnit}</code> objects
4426     *
4427     * @throws CmsException if operation was not successful
4428     *
4429     * @see org.opencms.security.CmsOrgUnitManager#getOrganizationalUnits(CmsObject, String, boolean)
4430     */
4431    public List<CmsOrganizationalUnit> getOrganizationalUnits(
4432        CmsDbContext dbc,
4433        CmsOrganizationalUnit parent,
4434        boolean includeChildren)
4435    throws CmsException {
4436
4437        if (parent == null) {
4438            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PARENT_ORGUNIT_NULL_0));
4439        }
4440        return getUserDriver(dbc).getOrganizationalUnits(dbc, parent, includeChildren);
4441    }
4442
4443    /**
4444     * Returns all the organizational units for which the current user has the given role.<p>
4445     *
4446     * @param dbc the current database context
4447     * @param role the role to check
4448     * @param includeSubOus if sub organizational units should be included in the search
4449     *
4450     * @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects
4451     *
4452     * @throws CmsException if something goes wrong
4453     */
4454    public List<CmsOrganizationalUnit> getOrgUnitsForRole(CmsDbContext dbc, CmsRole role, boolean includeSubOus)
4455    throws CmsException {
4456
4457        String ouFqn = role.getOuFqn();
4458        if (ouFqn == null) {
4459            ouFqn = "";
4460            role = role.forOrgUnit("");
4461        }
4462        CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouFqn);
4463        List<CmsOrganizationalUnit> orgUnits = new ArrayList<CmsOrganizationalUnit>();
4464        if (m_securityManager.hasRole(dbc, dbc.currentUser(), role)) {
4465            orgUnits.add(ou);
4466        }
4467        if (includeSubOus) {
4468            Iterator<CmsOrganizationalUnit> it = getOrganizationalUnits(dbc, ou, true).iterator();
4469            while (it.hasNext()) {
4470                CmsOrganizationalUnit orgUnit = it.next();
4471                if (m_securityManager.hasRole(dbc, dbc.currentUser(), role.forOrgUnit(orgUnit.getName()))) {
4472                    orgUnits.add(orgUnit);
4473                }
4474            }
4475        }
4476        return orgUnits;
4477    }
4478
4479    /**
4480     * Returns the parent group of a group.<p>
4481     *
4482     * @param dbc the current database context
4483     * @param groupname the name of the group
4484     *
4485     * @return group the parent group or <code>null</code>
4486     *
4487     * @throws CmsException if operation was not successful
4488     */
4489    public CmsGroup getParent(CmsDbContext dbc, String groupname) throws CmsException {
4490
4491        CmsGroup group = readGroup(dbc, groupname);
4492        if (group.getParentId().isNullUUID()) {
4493            return null;
4494        }
4495
4496        // try to read from cache
4497        CmsGroup parent = m_monitor.getCachedGroup(group.getParentId().toString());
4498        if (parent == null) {
4499            parent = getUserDriver(dbc).readGroup(dbc, group.getParentId());
4500            m_monitor.cacheGroup(parent);
4501        }
4502        return parent;
4503    }
4504
4505    /**
4506     * Returns the set of permissions of the current user for a given resource.<p>
4507     *
4508     * @param dbc the current database context
4509     * @param resource the resource
4510     * @param user the user
4511     *
4512     * @return bit set with allowed permissions
4513     *
4514     * @throws CmsException if something goes wrong
4515     */
4516    public CmsPermissionSetCustom getPermissions(CmsDbContext dbc, CmsResource resource, CmsUser user)
4517    throws CmsException {
4518
4519        CmsAccessControlList acList = getAccessControlList(dbc, resource, false);
4520        return acList.getPermissions(user, getGroupsOfUser(dbc, user.getName(), false), getRolesForUser(dbc, user));
4521    }
4522
4523    /**
4524     * Returns the project driver.<p>
4525     *
4526     * @return the project driver
4527     */
4528    public I_CmsProjectDriver getProjectDriver() {
4529
4530        return m_projectDriver;
4531    }
4532
4533    /**
4534     * Returns the project driver for a given DB context.<p>
4535     *
4536     * @param dbc the database context
4537     *
4538     * @return the project driver for the database context
4539     */
4540    public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc) {
4541
4542        if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
4543            return m_projectDriver;
4544        }
4545        I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId());
4546        return driver != null ? driver : m_projectDriver;
4547    }
4548
4549    /**
4550     * Returns either the project driver for the DB context (if it has one) or a default project driver.<p>
4551     *
4552     * @param dbc the DB context
4553     * @param defaultDriver the driver which should be returned if there is no project driver for the DB context
4554     *
4555     * @return either the project driver for the DB context, or the default driver
4556     */
4557    public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc, I_CmsProjectDriver defaultDriver) {
4558
4559        if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
4560            return defaultDriver;
4561        }
4562        I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId());
4563        return driver != null ? driver : defaultDriver;
4564    }
4565
4566    /**
4567     * Returns the uuid id for the given id.<p>
4568     *
4569     * TODO: remove this method as soon as possible
4570     *
4571     * @param dbc the current database context
4572     * @param id the old project id
4573     *
4574     * @return the new uuid for the given id
4575     *
4576     * @throws CmsException if something goes wrong
4577     */
4578    public CmsUUID getProjectId(CmsDbContext dbc, int id) throws CmsException {
4579
4580        Iterator<CmsProject> itProjects = getAllAccessibleProjects(
4581            dbc,
4582            readOrganizationalUnit(dbc, ""),
4583            true).iterator();
4584        while (itProjects.hasNext()) {
4585            CmsProject project = itProjects.next();
4586            if (project.getUuid().hashCode() == id) {
4587                return project.getUuid();
4588            }
4589        }
4590        return null;
4591    }
4592
4593    /**
4594     * Returns the configuration read from the <code>opencms.properties</code> file.<p>
4595     *
4596     * @return the configuration read from the <code>opencms.properties</code> file
4597     */
4598    public CmsParameterConfiguration getPropertyConfiguration() {
4599
4600        return m_propertyConfiguration;
4601    }
4602
4603    /**
4604     * Returns a new publish list that contains the unpublished resources related
4605     * to all resources in the given publish list, the related resources exclude
4606     * all resources in the given publish list and also locked (by other users) resources.<p>
4607     *
4608     * @param dbc the current database context
4609     * @param publishList the publish list to exclude from result
4610     * @param filter the relation filter to use to get the related resources
4611     *
4612     * @return a new publish list that contains the related resources
4613     *
4614     * @throws CmsException if something goes wrong
4615     *
4616     * @see org.opencms.publish.CmsPublishManager#getRelatedResourcesToPublish(CmsObject, CmsPublishList)
4617     */
4618    public CmsPublishList getRelatedResourcesToPublish(
4619        CmsDbContext dbc,
4620        CmsPublishList publishList,
4621        CmsRelationFilter filter)
4622    throws CmsException {
4623
4624        Map<String, CmsResource> relations = new HashMap<String, CmsResource>();
4625
4626        // check if progress should be set in the thread
4627        A_CmsProgressThread thread = null;
4628        if (Thread.currentThread() instanceof A_CmsProgressThread) {
4629            thread = (A_CmsProgressThread)Thread.currentThread();
4630        }
4631
4632        // get all resources to publish
4633        List<CmsResource> publishResources = publishList.getAllResources();
4634        Iterator<CmsResource> itCheckList = publishResources.iterator();
4635        // iterate over them
4636        int count = 0;
4637        while (itCheckList.hasNext()) {
4638
4639            // set progress in thread
4640            count++;
4641            if (thread != null) {
4642
4643                if (thread.isInterrupted()) {
4644                    throw new CmsIllegalStateException(
4645                        org.opencms.workplace.commons.Messages.get().container(
4646                            org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0));
4647                }
4648                thread.setProgress((count * 20) / publishResources.size());
4649                thread.setDescription(
4650                    org.opencms.workplace.commons.Messages.get().getBundle().key(
4651                        org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP1_2,
4652                        new Integer(count),
4653                        new Integer(publishResources.size())));
4654            }
4655
4656            CmsResource checkResource = itCheckList.next();
4657            // get and iterate over all related resources
4658            Iterator<CmsRelation> itRelations = getRelationsForResource(dbc, checkResource, filter).iterator();
4659            while (itRelations.hasNext()) {
4660                CmsRelation relation = itRelations.next();
4661                try {
4662                    // get the target of the relation, see CmsRelation#getTarget(CmsObject, CmsResourceFilter)
4663                    CmsResource target;
4664                    try {
4665                        // first look up by id
4666                        target = readResource(dbc, relation.getTargetId(), CmsResourceFilter.ALL);
4667                    } catch (CmsVfsResourceNotFoundException e) {
4668                        // then look up by name, but from the root site
4669                        String storedSiteRoot = dbc.getRequestContext().getSiteRoot();
4670                        try {
4671                            dbc.getRequestContext().setSiteRoot("");
4672                            target = readResource(dbc, relation.getTargetPath(), CmsResourceFilter.ALL);
4673                        } finally {
4674                            dbc.getRequestContext().setSiteRoot(storedSiteRoot);
4675                        }
4676                    }
4677                    CmsLock lock = getLock(dbc, target);
4678                    // just add resources that may come in question
4679                    if (!publishResources.contains(target) // is not in the original list
4680                        && !relations.containsKey(target.getRootPath()) // has not been already added by another relation
4681                        && !target.getState().isUnchanged() // has been changed
4682                        && lock.isLockableBy(dbc.currentUser())) { // is lockable by current user
4683
4684                        relations.put(target.getRootPath(), target);
4685                        // now check the folder structure
4686                        CmsResource parent = getVfsDriver(dbc).readParentFolder(
4687                            dbc,
4688                            dbc.currentProject().getUuid(),
4689                            target.getStructureId());
4690                        while ((parent != null) && parent.getState().isNew()) {
4691                            // just add resources that may come in question
4692                            if (!publishResources.contains(parent) // is not in the original list
4693                                && !relations.containsKey(parent.getRootPath())) { // has not been already added by another relation
4694
4695                                relations.put(parent.getRootPath(), parent);
4696                            }
4697                            parent = getVfsDriver(dbc).readParentFolder(
4698                                dbc,
4699                                dbc.currentProject().getUuid(),
4700                                parent.getStructureId());
4701                        }
4702                    }
4703                } catch (CmsVfsResourceNotFoundException e) {
4704                    // ignore broken links
4705                    if (LOG.isDebugEnabled()) {
4706                        LOG.debug(e.getLocalizedMessage(), e);
4707                    }
4708                }
4709            }
4710        }
4711
4712        CmsPublishList ret = new CmsPublishList(publishList.getDirectPublishResources(), false, false);
4713        ret.addAll(relations.values(), false);
4714        ret.initialize();
4715        return ret;
4716    }
4717
4718    /**
4719     * Returns all relations for the given resource matching the given filter.<p>
4720     *
4721     * @param dbc the current db context
4722     * @param resource the resource to retrieve the relations for
4723     * @param filter the filter to match the relation
4724     *
4725     * @return all relations for the given resource matching the given filter
4726     *
4727     * @throws CmsException if something goes wrong
4728     *
4729     * @see CmsSecurityManager#getRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter)
4730     */
4731    public List<CmsRelation> getRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter)
4732    throws CmsException {
4733
4734        CmsUUID projectId = getProjectIdForContext(dbc);
4735        return getVfsDriver(dbc).readRelations(dbc, projectId, resource, filter);
4736    }
4737
4738    /**
4739     * Returns the list of organizational units the given resource belongs to.<p>
4740     *
4741     * @param dbc the current database context
4742     * @param resource the resource
4743     *
4744     * @return list of {@link CmsOrganizationalUnit} objects
4745     *
4746     * @throws CmsException if something goes wrong
4747     */
4748    public List<CmsOrganizationalUnit> getResourceOrgUnits(CmsDbContext dbc, CmsResource resource) throws CmsException {
4749
4750        boolean nullDbcProjectId = (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID();
4751        if (nullDbcProjectId && resourceOrgUnitCachingEnabled) {
4752            try {
4753                return m_monitor.getResourceOuCache().get(new ResourceOUCacheKey(this, dbc)).getResourceOrgUnits(
4754                    resource.getRootPath());
4755            } catch (ExecutionException e) {
4756                LOG.error(e.getLocalizedMessage(), e);
4757            }
4758        }
4759        List<CmsOrganizationalUnit> result = getVfsDriver(dbc).getResourceOus(
4760            dbc,
4761            dbc.currentProject().getUuid(),
4762            resource);
4763
4764        return result;
4765    }
4766
4767    /**
4768     * Returns all resources of the given organizational unit.<p>
4769     *
4770     * @param dbc the current db context
4771     * @param orgUnit the organizational unit to get all resources for
4772     *
4773     * @return all <code>{@link CmsResource}</code> objects in the organizational unit
4774     *
4775     * @throws CmsException if operation was not successful
4776     *
4777     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
4778     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
4779     * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
4780     */
4781    public List<CmsResource> getResourcesForOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit)
4782    throws CmsException {
4783
4784        return getUserDriver(dbc).getResourcesForOrganizationalUnit(dbc, orgUnit);
4785    }
4786
4787    /**
4788     * Returns all resources associated to a given principal via an ACE with the given permissions.<p>
4789     *
4790     * If the <code>includeAttr</code> flag is set it returns also all resources associated to
4791     * a given principal through some of following attributes.<p>
4792     *
4793     * <ul>
4794     *    <li>User Created</li>
4795     *    <li>User Last Modified</li>
4796     * </ul><p>
4797     *
4798     * @param dbc the current database context
4799     * @param project the to read the entries from
4800     * @param principalId the id of the principal
4801     * @param permissions a set of permissions to match, can be <code>null</code> for all ACEs
4802     * @param includeAttr a flag to include resources associated by attributes
4803     *
4804     * @return a set of <code>{@link CmsResource}</code> objects
4805     *
4806     * @throws CmsException if something goes wrong
4807     */
4808    public Set<CmsResource> getResourcesForPrincipal(
4809        CmsDbContext dbc,
4810        CmsProject project,
4811        CmsUUID principalId,
4812        CmsPermissionSet permissions,
4813        boolean includeAttr)
4814    throws CmsException {
4815
4816        Set<CmsResource> resources = new HashSet<CmsResource>(
4817            getVfsDriver(dbc).readResourcesForPrincipalACE(dbc, project, principalId));
4818        if (permissions != null) {
4819            Iterator<CmsResource> itRes = resources.iterator();
4820            while (itRes.hasNext()) {
4821                CmsAccessControlEntry ace = readAccessControlEntry(dbc, itRes.next(), principalId);
4822                if ((ace.getPermissions().getPermissions()
4823                    & permissions.getPermissions()) != permissions.getPermissions()) {
4824                    // remove if permissions does not match
4825                    itRes.remove();
4826                }
4827            }
4828        }
4829        if (includeAttr) {
4830            resources.addAll(getVfsDriver(dbc).readResourcesForPrincipalAttr(dbc, project, principalId));
4831        }
4832        return resources;
4833    }
4834
4835    /**
4836     * Gets the rewrite aliases matching a given filter.<p>
4837     *
4838     * @param dbc the current database context
4839     * @param filter the filter used for filtering rewrite aliases
4840     *
4841     * @return the rewrite aliases matching the given filter
4842     *
4843     * @throws CmsException if something goes wrong
4844     */
4845    public List<CmsRewriteAlias> getRewriteAliases(CmsDbContext dbc, CmsRewriteAliasFilter filter) throws CmsException {
4846
4847        return getVfsDriver(dbc).readRewriteAliases(dbc, filter);
4848    }
4849
4850    /**
4851     * Collects the groups which constitute a given role.<p>
4852     *
4853     * @param dbc the database context
4854     * @param roleGroupName the group related to the role
4855     * @param directUsersOnly if true, only the group belonging to the entry itself wil
4856     *
4857     * @return the set of groups which constitute the role
4858     *
4859     * @throws CmsException if something goes wrong
4860     */
4861    public Set<CmsGroup> getRoleGroups(CmsDbContext dbc, String roleGroupName, boolean directUsersOnly)
4862    throws CmsException {
4863
4864        return getRoleGroupsImpl(dbc, roleGroupName, directUsersOnly, new HashMap<String, Set<CmsGroup>>());
4865    }
4866
4867    /**
4868     * Collects the groups which constitute a given role.<p>
4869     *
4870     * @param dbc the database context
4871     * @param roleGroupName the group related to the role
4872     * @param directUsersOnly if true, only the group belonging to the entry itself wil
4873     * @param accumulator a map for memoizing return values of recursive calls
4874     *
4875     * @return the set of groups which constitute the role
4876     *
4877     * @throws CmsException if something goes wrong
4878     */
4879    public Set<CmsGroup> getRoleGroupsImpl(
4880        CmsDbContext dbc,
4881        String roleGroupName,
4882        boolean directUsersOnly,
4883        Map<String, Set<CmsGroup>> accumulator)
4884    throws CmsException {
4885
4886        Set<CmsGroup> result = new HashSet<CmsGroup>();
4887        if (accumulator.get(roleGroupName) != null) {
4888            return accumulator.get(roleGroupName);
4889        }
4890        CmsGroup group = readGroup(dbc, roleGroupName); // check that the group really exists
4891        if ((group == null) || (!group.isRole())) {
4892            throw new CmsDbEntryNotFoundException(
4893                Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, roleGroupName));
4894        }
4895        result.add(group);
4896        if (!directUsersOnly) {
4897            CmsRole role = CmsRole.valueOf(group);
4898            if (role.getParentRole() != null) {
4899                try {
4900                    String parentGroup = role.getParentRole().getGroupName();
4901                    // iterate the parent roles
4902                    result.addAll(getRoleGroupsImpl(dbc, parentGroup, directUsersOnly, accumulator));
4903                } catch (CmsDbEntryNotFoundException e) {
4904                    // ignore, this may happen while deleting an orgunit
4905                    if (LOG.isDebugEnabled()) {
4906                        LOG.debug(e.getLocalizedMessage(), e);
4907                    }
4908                }
4909            }
4910            String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn());
4911            if (parentOu != null) {
4912                // iterate the parent ou's
4913                result.addAll(getRoleGroupsImpl(dbc, parentOu + group.getSimpleName(), directUsersOnly, accumulator));
4914            }
4915        }
4916        accumulator.put(roleGroupName, result);
4917        return result;
4918    }
4919
4920    /**
4921     * Returns all roles the given user has for the given resource.<p>
4922     *
4923     * @param dbc the current database context
4924     * @param user the user to check
4925     * @param resource the resource to check the roles for
4926     *
4927     * @return a list of {@link CmsRole} objects
4928     *
4929     * @throws CmsException if something goes wrong
4930     */
4931    public List<CmsRole> getRolesForResource(CmsDbContext dbc, CmsUser user, CmsResource resource) throws CmsException {
4932
4933        // guest user has no role
4934        if (user.isGuestUser()) {
4935            return Collections.emptyList();
4936        }
4937
4938        // try to read from cache
4939        String key = user.getId().toString() + resource.getRootPath();
4940        List<CmsRole> result = m_monitor.getCachedRoleList(key);
4941        if (result != null) {
4942            return result;
4943        }
4944        result = new ArrayList<CmsRole>();
4945
4946        Iterator<CmsOrganizationalUnit> itOus = getResourceOrgUnits(dbc, resource).iterator();
4947        while (itOus.hasNext()) {
4948            CmsOrganizationalUnit ou = itOus.next();
4949
4950            // read all roles of the current user
4951            List<CmsGroup> groups = new ArrayList<CmsGroup>(
4952                getGroupsOfUser(
4953                    dbc,
4954                    user.getName(),
4955                    ou.getName(),
4956                    false,
4957                    true,
4958                    false,
4959                    dbc.getRequestContext().getRemoteAddress()));
4960            // check the roles applying to the given resource
4961            Iterator<CmsGroup> it = groups.iterator();
4962            while (it.hasNext()) {
4963                CmsGroup group = it.next();
4964                CmsRole givenRole = CmsRole.valueOf(group).forOrgUnit(null);
4965                if (givenRole.isOrganizationalUnitIndependent() || result.contains(givenRole)) {
4966                    // skip already added roles
4967                    continue;
4968                }
4969                result.add(givenRole);
4970            }
4971        }
4972
4973        result = Collections.unmodifiableList(result);
4974        m_monitor.cacheRoleList(key, result);
4975        return result;
4976    }
4977
4978    /**
4979     * Returns all roles the given user has independent of the resource.<p>
4980     *
4981     * @param dbc the current database context
4982     * @param user the user to check
4983     *
4984     * @return a list of {@link CmsRole} objects
4985     *
4986     * @throws CmsException if something goes wrong
4987     */
4988    public List<CmsRole> getRolesForUser(CmsDbContext dbc, CmsUser user) throws CmsException {
4989
4990        // guest user has no role
4991        if (user.isGuestUser()) {
4992            return Collections.emptyList();
4993        }
4994
4995        // try to read from cache
4996        List<CmsRole> result = m_monitor.getGroupListCache().getBareRoles(user.getId());
4997        if (result != null) {
4998            return result;
4999        }
5000        result = new ArrayList<CmsRole>();
5001
5002        // read all roles of the current user
5003        List<CmsGroup> groups = new ArrayList<CmsGroup>(
5004            getGroupsOfUser(dbc, user.getName(), "", true, true, false, dbc.getRequestContext().getRemoteAddress()));
5005
5006        // check the roles applying to the given resource
5007        Iterator<CmsGroup> it = groups.iterator();
5008        while (it.hasNext()) {
5009            CmsGroup group = it.next();
5010            CmsRole givenRole = CmsRole.valueOf(group);
5011            givenRole = givenRole.forOrgUnit(null);
5012            if (!result.contains(givenRole)) {
5013                result.add(givenRole);
5014            }
5015        }
5016        result = Collections.unmodifiableList(result);
5017        m_monitor.getGroupListCache().setBareRoles(user, result);
5018        return result;
5019    }
5020
5021    /**
5022     * Returns the security manager this driver manager belongs to.<p>
5023     *
5024     * @return the security manager this driver manager belongs to
5025     */
5026    public CmsSecurityManager getSecurityManager() {
5027
5028        return m_securityManager;
5029    }
5030
5031    /**
5032     * Returns an instance of the common sql manager.<p>
5033     *
5034     * @return an instance of the common sql manager
5035     */
5036    public CmsSqlManager getSqlManager() {
5037
5038        return m_sqlManager;
5039    }
5040
5041    /**
5042     * Returns the subscription driver of this driver manager.<p>
5043     *
5044     * @return a subscription driver
5045     */
5046    public I_CmsSubscriptionDriver getSubscriptionDriver() {
5047
5048        return m_subscriptionDriver;
5049    }
5050
5051    /**
5052     * Returns the user driver.<p>
5053     *
5054     * @return the user driver
5055     */
5056    public I_CmsUserDriver getUserDriver() {
5057
5058        return m_userDriver;
5059    }
5060
5061    /**
5062     * Returns the user driver for a given database context.<p>
5063     *
5064     * @param dbc the database context
5065     *
5066     * @return the user driver for the database context
5067     */
5068    public I_CmsUserDriver getUserDriver(CmsDbContext dbc) {
5069
5070        if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
5071            return m_userDriver;
5072        }
5073        I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId());
5074        return driver != null ? driver : m_userDriver;
5075
5076    }
5077
5078    /**
5079     * Returns either the user driver for the given DB context (if it has one) or a default value instead.<p>
5080     *
5081     * @param dbc the DB context
5082     * @param defaultDriver the driver that should be returned if no driver for the DB context was found
5083     *
5084     * @return either the user driver for the DB context, or <code>defaultDriver</code> if none were found
5085     */
5086    public I_CmsUserDriver getUserDriver(CmsDbContext dbc, I_CmsUserDriver defaultDriver) {
5087
5088        if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
5089            return defaultDriver;
5090        }
5091        I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId());
5092        return driver != null ? driver : defaultDriver;
5093    }
5094
5095    /**
5096     * Returns all direct users of the given organizational unit.<p>
5097     *
5098     * @param dbc the current db context
5099     * @param orgUnit the organizational unit to get all users for
5100     * @param recursive if all groups of sub-organizational units should be retrieved too
5101     *
5102     * @return all <code>{@link CmsUser}</code> objects in the organizational unit
5103     *
5104     * @throws CmsException if operation was not successful
5105     *
5106     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
5107     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
5108     */
5109    public List<CmsUser> getUsers(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, boolean recursive)
5110    throws CmsException {
5111
5112        return getUserDriver(dbc).getUsers(dbc, orgUnit, recursive);
5113    }
5114
5115    /**
5116     * Returns a list of users in a group.<p>
5117     *
5118     * @param dbc the current database context
5119     * @param groupname the name of the group to list users from
5120     * @param includeOtherOuUsers include users of other organizational units
5121     * @param directUsersOnly if set only the direct assigned users will be returned,
5122     *                        if not also indirect users, ie. members of parent roles,
5123     *                        this parameter only works with roles
5124     * @param readRoles if to read roles or groups
5125     *
5126     * @return all <code>{@link CmsUser}</code> objects in the group
5127     *
5128     * @throws CmsException if operation was not successful
5129     */
5130    public List<CmsUser> getUsersOfGroup(
5131        CmsDbContext dbc,
5132        String groupname,
5133        boolean includeOtherOuUsers,
5134        boolean directUsersOnly,
5135        boolean readRoles)
5136    throws CmsException {
5137
5138        return internalUsersOfGroup(
5139            dbc,
5140            CmsOrganizationalUnit.getParentFqn(groupname),
5141            groupname,
5142            includeOtherOuUsers,
5143            directUsersOnly,
5144            readRoles);
5145    }
5146
5147    /**
5148     * Returns the given user's publish list.<p>
5149     *
5150     * @param dbc the database context
5151     * @param userId the user's id
5152     *
5153     * @return the given user's publish list
5154     *
5155     * @throws CmsDataAccessException if something goes wrong
5156     */
5157    public List<CmsResource> getUsersPubList(CmsDbContext dbc, CmsUUID userId) throws CmsDataAccessException {
5158
5159        synchronized (m_publishListUpdateLock) {
5160            updateLog(dbc);
5161            return m_projectDriver.getUsersPubList(dbc, userId);
5162        }
5163    }
5164
5165    /**
5166     * Returns all direct users of the given organizational unit, without their additional info.<p>
5167     *
5168     * @param dbc the current db context
5169     * @param orgUnit the organizational unit to get all users for
5170     * @param recursive if all groups of sub-organizational units should be retrieved too
5171     *
5172     * @return all <code>{@link CmsUser}</code> objects in the organizational unit
5173     *
5174     * @throws CmsException if operation was not successful
5175     *
5176     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
5177     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
5178     */
5179    public List<CmsUser> getUsersWithoutAdditionalInfo(
5180        CmsDbContext dbc,
5181        CmsOrganizationalUnit orgUnit,
5182        boolean recursive)
5183    throws CmsException {
5184
5185        return getUserDriver(dbc).getUsersWithoutAdditionalInfo(dbc, orgUnit, recursive);
5186    }
5187
5188    /**
5189     * Returns the VFS driver.<p>
5190     *
5191     * @return the VFS driver
5192     */
5193    public I_CmsVfsDriver getVfsDriver() {
5194
5195        return m_vfsDriver;
5196    }
5197
5198    /**
5199     * Returns the VFS driver for the given database context.<p>
5200     *
5201     * @param dbc the database context
5202     *
5203     * @return a VFS driver
5204     */
5205    public I_CmsVfsDriver getVfsDriver(CmsDbContext dbc) {
5206
5207        if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) {
5208            return m_vfsDriver;
5209        }
5210        I_CmsVfsDriver driver = dbc.getVfsDriver(dbc.getProjectId());
5211        return driver != null ? driver : m_vfsDriver;
5212
5213    }
5214
5215    /**
5216     * Writes a vector of access control entries as new access control entries of a given resource.<p>
5217     *
5218     * Already existing access control entries of this resource are removed before.
5219     * Access is granted, if:<p>
5220     * <ul>
5221     * <li>the current user has control permission on the resource</li>
5222     * </ul>
5223     *
5224     * @param dbc the current database context
5225     * @param resource the resource
5226     * @param acEntries a list of <code>{@link CmsAccessControlEntry}</code> objects
5227     *
5228     * @throws CmsException if something goes wrong
5229     */
5230    public void importAccessControlEntries(
5231        CmsDbContext dbc,
5232        CmsResource resource,
5233        List<CmsAccessControlEntry> acEntries)
5234    throws CmsException {
5235
5236        I_CmsUserDriver userDriver = getUserDriver(dbc);
5237        userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId());
5238        List<CmsAccessControlEntry> fixedAces = new ArrayList<>();
5239        for (CmsAccessControlEntry entry : acEntries) {
5240            if (entry.getResource() == null) {
5241                entry = new CmsAccessControlEntry(
5242                    resource.getResourceId(),
5243                    entry.getPrincipal(),
5244                    entry.getPermissions(),
5245                    entry.getFlags());
5246            }
5247            fixedAces.add(entry);
5248        }
5249
5250        Iterator<CmsAccessControlEntry> i = fixedAces.iterator();
5251        while (i.hasNext()) {
5252            userDriver.writeAccessControlEntry(dbc, dbc.currentProject(), i.next());
5253        }
5254        m_monitor.clearAccessControlListCache();
5255    }
5256
5257    /**
5258     * Imports a rewrite alias.<p>
5259     *
5260     * @param dbc the database context
5261     * @param siteRoot the site root of the alias
5262     * @param source the source of the alias
5263     * @param target the target of the alias
5264     * @param mode the alias mode
5265     *
5266     * @return the import result
5267     *
5268     * @throws CmsException if something goes wrong
5269     */
5270    public CmsAliasImportResult importRewriteAlias(
5271        CmsDbContext dbc,
5272        String siteRoot,
5273        String source,
5274        String target,
5275        CmsAliasMode mode)
5276    throws CmsException {
5277
5278        I_CmsVfsDriver vfs = getVfsDriver(dbc);
5279        List<CmsRewriteAlias> existingAliases = vfs.readRewriteAliases(
5280            dbc,
5281            new CmsRewriteAliasFilter().setSiteRoot(siteRoot));
5282        CmsUUID idToDelete = null;
5283        for (CmsRewriteAlias alias : existingAliases) {
5284            if (alias.getPatternString().equals(source)) {
5285                idToDelete = alias.getId();
5286            }
5287        }
5288        if (idToDelete != null) {
5289            vfs.deleteRewriteAliases(dbc, new CmsRewriteAliasFilter().setId(idToDelete));
5290        }
5291        CmsRewriteAlias alias = new CmsRewriteAlias(new CmsUUID(), siteRoot, source, target, mode);
5292        List<CmsRewriteAlias> aliases = new ArrayList<CmsRewriteAlias>();
5293        aliases.add(alias);
5294        getVfsDriver(dbc).insertRewriteAliases(dbc, aliases);
5295        CmsAliasImportResult result = new CmsAliasImportResult(
5296            CmsAliasImportStatus.aliasNew,
5297            "OK",
5298            source,
5299            target,
5300            mode);
5301        return result;
5302    }
5303
5304    /**
5305     * Creates a new user by import.<p>
5306     *
5307     * @param dbc the current database context
5308     * @param id the id of the user
5309     * @param name the new name for the user
5310     * @param password the new password for the user (already encrypted)
5311     * @param firstname the firstname of the user
5312     * @param lastname the lastname of the user
5313     * @param email the email of the user
5314     * @param flags the flags for a user (for example <code>{@link I_CmsPrincipal#FLAG_ENABLED}</code>)
5315     * @param dateCreated the creation date
5316     * @param additionalInfos the additional user infos
5317     *
5318     * @return the imported user
5319     *
5320     * @throws CmsException if something goes wrong
5321     */
5322    public CmsUser importUser(
5323        CmsDbContext dbc,
5324        String id,
5325        String name,
5326        String password,
5327        String firstname,
5328        String lastname,
5329        String email,
5330        int flags,
5331        long dateCreated,
5332        Map<String, Object> additionalInfos)
5333    throws CmsException {
5334
5335        // no space before or after the name
5336        name = name.trim();
5337        // check the user name
5338        String userName = CmsOrganizationalUnit.getSimpleName(name);
5339        OpenCms.getValidationHandler().checkUserName(userName);
5340        if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) {
5341            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName));
5342        }
5343        // check the ou
5344        CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name));
5345
5346        // check webuser ou
5347        if (ou.hasFlagWebuser() && ((flags & I_CmsPrincipal.FLAG_USER_WEBUSER) == 0)) {
5348            flags += I_CmsPrincipal.FLAG_USER_WEBUSER;
5349        }
5350        CmsUser newUser = getUserDriver(dbc).createUser(
5351            dbc,
5352            new CmsUUID(id),
5353            name,
5354            password,
5355            firstname,
5356            lastname,
5357            email,
5358            0,
5359            flags,
5360            dateCreated,
5361            additionalInfos);
5362        return newUser;
5363    }
5364
5365    /**
5366     * Increments a counter and returns its value before incrementing.<p>
5367     *
5368     * @param dbc the current database context
5369     * @param name the name of the counter which should be incremented
5370     *
5371     * @return the value of the counter
5372     *
5373     * @throws CmsException if something goes wrong
5374     */
5375    public int incrementCounter(CmsDbContext dbc, String name) throws CmsException {
5376
5377        return getVfsDriver(dbc).incrementCounter(dbc, name);
5378    }
5379
5380    /**
5381     * Initializes the driver and sets up all required modules and connections.<p>
5382     *
5383     * @param configurationManager the configuration manager
5384     * @param dbContextFactory the db context factory
5385     *
5386     * @throws CmsException if something goes wrong
5387     * @throws Exception if something goes wrong
5388     */
5389    public void init(CmsConfigurationManager configurationManager, I_CmsDbContextFactory dbContextFactory)
5390    throws CmsException, Exception {
5391
5392        // initialize the access-module.
5393        if (CmsLog.INIT.isInfoEnabled()) {
5394            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE4_0));
5395        }
5396        // store local reference to the memory monitor to avoid multiple lookups through the OpenCms singelton
5397        m_monitor = OpenCms.getMemoryMonitor();
5398
5399        CmsSystemConfiguration systemConfiguation = (CmsSystemConfiguration)configurationManager.getConfiguration(
5400            CmsSystemConfiguration.class);
5401        CmsCacheSettings settings = systemConfiguation.getCacheSettings();
5402
5403        // initialize the key generator
5404        m_keyGenerator = (I_CmsCacheKey)Class.forName(settings.getCacheKeyGenerator()).newInstance();
5405
5406        // initialize the HTML link validator
5407        m_htmlLinkValidator = new CmsRelationSystemValidator(this);
5408
5409        // fills the defaults if needed
5410        CmsDbContext dbc1 = dbContextFactory.getDbContext();
5411        getUserDriver().fillDefaults(dbc1);
5412        getProjectDriver().fillDefaults(dbc1);
5413
5414        // set the driver manager in the publish engine
5415        m_publishEngine.setDriverManager(this);
5416        // create the root organizational unit if needed
5417        CmsDbContext dbc2 = dbContextFactory.getDbContext(
5418            new CmsRequestContext(
5419                readUser(dbc1, OpenCms.getDefaultUsers().getUserAdmin()),
5420                readProject(dbc1, CmsProject.ONLINE_PROJECT_ID),
5421                null,
5422                CmsSiteMatcher.DEFAULT_MATCHER,
5423                "",
5424                false,
5425                null,
5426                null,
5427                null,
5428                0,
5429                null,
5430                null,
5431                "",
5432                false));
5433        dbc1.clear();
5434        getUserDriver().createRootOrganizationalUnit(dbc2);
5435        dbc2.clear();
5436    }
5437
5438    /**
5439     * Initializes the organizational unit.<p>
5440     *
5441     * @param dbc the DB context
5442     * @param ou the organizational unit
5443     */
5444    public void initOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit ou) {
5445
5446        try {
5447            dbc.setAttribute(ATTR_INIT_OU, ou);
5448            m_userDriver.fillDefaults(dbc);
5449        } finally {
5450            dbc.removeAttribute(ATTR_INIT_OU);
5451        }
5452    }
5453
5454    /**
5455     * Checks if the specified resource is inside the current project.<p>
5456     *
5457     * The project "view" is determined by a set of path prefixes.
5458     * If the resource starts with any one of this prefixes, it is considered to
5459     * be "inside" the project.<p>
5460     *
5461     * @param dbc the current database context
5462     * @param resourcename the specified resource name (full path)
5463     *
5464     * @return <code>true</code>, if the specified resource is inside the current project
5465     */
5466    public boolean isInsideCurrentProject(CmsDbContext dbc, String resourcename) {
5467
5468        List<String> projectResources = null;
5469        try {
5470            projectResources = readProjectResources(dbc, dbc.currentProject());
5471        } catch (CmsException e) {
5472            if (LOG.isErrorEnabled()) {
5473                LOG.error(
5474                    Messages.get().getBundle().key(
5475                        Messages.LOG_CHECK_RESOURCE_INSIDE_CURRENT_PROJECT_2,
5476                        resourcename,
5477                        dbc.currentProject().getName()),
5478                    e);
5479            }
5480            return false;
5481        }
5482        return CmsProject.isInsideProject(projectResources, resourcename);
5483    }
5484
5485    /**
5486     * Checks whether the subscription driver is available.<p>
5487     *
5488     * @return true if the subscription driver is available
5489     */
5490    public boolean isSubscriptionDriverAvailable() {
5491
5492        return m_subscriptionDriver != null;
5493    }
5494
5495    /**
5496     * Checks if a project is the tempfile project.<p>
5497     * @param project the project to test
5498     * @return true if the project is the tempfile project
5499     */
5500    public boolean isTempfileProject(CmsProject project) {
5501
5502        return project.getName().equals("tempFileProject");
5503    }
5504
5505    /**
5506     * Checks if one of the resources (except the resource itself)
5507     * is a sibling in a "labeled" site folder.<p>
5508     *
5509     * This method is used when creating a new sibling
5510     * (use the <code>newResource</code> parameter & <code>action = 1</code>)
5511     * or deleting/importing a resource (call with <code>action = 2</code>).<p>
5512     *
5513     * @param dbc the current database context
5514     * @param resource the resource
5515     * @param newResource absolute path for a resource sibling which will be created
5516     * @param action the action which has to be performed (1: create VFS link, 2: all other actions)
5517     *
5518     * @return <code>true</code> if the flag should be set for the resource, otherwise <code>false</code>
5519     *
5520     * @throws CmsDataAccessException if something goes wrong
5521     */
5522    public boolean labelResource(CmsDbContext dbc, CmsResource resource, String newResource, int action)
5523    throws CmsDataAccessException {
5524
5525        // get the list of labeled site folders from the runtime property
5526        List<String> labeledSites = OpenCms.getWorkplaceManager().getLabelSiteFolders();
5527
5528        if (labeledSites.size() == 0) {
5529            // no labeled sites defined, just return false
5530            return false;
5531        }
5532
5533        if (action == 1) {
5534            // CASE 1: a new resource is created, check the sites
5535            if (!resource.isLabeled()) {
5536                // source isn't labeled yet, so check!
5537                boolean linkInside = false;
5538                boolean sourceInside = false;
5539                for (int i = 0; i < labeledSites.size(); i++) {
5540                    String curSite = labeledSites.get(i);
5541                    if (newResource.startsWith(curSite)) {
5542                        // the link lies in a labeled site
5543                        linkInside = true;
5544                    }
5545                    if (resource.getRootPath().startsWith(curSite)) {
5546                        // the source lies in a labeled site
5547                        sourceInside = true;
5548                    }
5549                    if (linkInside && sourceInside) {
5550                        break;
5551                    }
5552                }
5553                // return true when either source or link is in labeled site, otherwise false
5554                return (linkInside != sourceInside);
5555            }
5556            // resource is already labeled
5557            return false;
5558
5559        } else {
5560            // CASE 2: the resource will be deleted or created (import)
5561            // check if at least one of the other siblings resides inside a "labeled site"
5562            // and if at least one of the other siblings resides outside a "labeled site"
5563            boolean isInside = false;
5564            boolean isOutside = false;
5565            // check if one of the other vfs links lies in a labeled site folder
5566            List<CmsResource> siblings = getVfsDriver(
5567                dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, false);
5568            updateContextDates(dbc, siblings);
5569            Iterator<CmsResource> i = siblings.iterator();
5570            while (i.hasNext() && (!isInside || !isOutside)) {
5571                CmsResource currentResource = i.next();
5572                if (currentResource.equals(resource)) {
5573                    // dont't check the resource itself!
5574                    continue;
5575                }
5576                String curPath = currentResource.getRootPath();
5577                boolean curInside = false;
5578                for (int k = 0; k < labeledSites.size(); k++) {
5579                    if (curPath.startsWith(labeledSites.get(k))) {
5580                        // the link is in the labeled site
5581                        isInside = true;
5582                        curInside = true;
5583                        break;
5584                    }
5585                }
5586                if (!curInside) {
5587                    // the current link was not found in labeled site, so it is outside
5588                    isOutside = true;
5589                }
5590            }
5591            // now check the new resource name if present
5592            if (newResource != null) {
5593                boolean curInside = false;
5594                for (int k = 0; k < labeledSites.size(); k++) {
5595                    if (newResource.startsWith(labeledSites.get(k))) {
5596                        // the new resource is in the labeled site
5597                        isInside = true;
5598                        curInside = true;
5599                        break;
5600                    }
5601                }
5602                if (!curInside) {
5603                    // the new resource was not found in labeled site, so it is outside
5604                    isOutside = true;
5605                }
5606            }
5607            return (isInside && isOutside);
5608        }
5609    }
5610
5611    /**
5612     * Returns the user, who had locked the resource.<p>
5613     *
5614     * A user can lock a resource, so he is the only one who can write this
5615     * resource. This methods checks, if a resource was locked.
5616     *
5617     * @param dbc the current database context
5618     * @param resource the resource
5619     *
5620     * @return the user, who had locked the resource
5621     *
5622     * @throws CmsException will be thrown, if the user has not the rights for this resource
5623     */
5624    public CmsUser lockedBy(CmsDbContext dbc, CmsResource resource) throws CmsException {
5625
5626        return readUser(dbc, m_lockManager.getLock(dbc, resource).getEditionLock().getUserId());
5627    }
5628
5629    /**
5630     * Locks a resource.<p>
5631     *
5632     * The <code>type</code> parameter controls what kind of lock is used.<br>
5633     * Possible values for this parameter are: <br>
5634     * <ul>
5635     * <li><code>{@link org.opencms.lock.CmsLockType#EXCLUSIVE}</code></li>
5636     * <li><code>{@link org.opencms.lock.CmsLockType#TEMPORARY}</code></li>
5637     * <li><code>{@link org.opencms.lock.CmsLockType#PUBLISH}</code></li>
5638     * </ul><p>
5639     *
5640     * @param dbc the current database context
5641     * @param resource the resource to lock
5642     * @param type type of the lock
5643     *
5644     * @throws CmsException if something goes wrong
5645     *
5646     * @see CmsObject#lockResource(String)
5647     * @see CmsObject#lockResourceTemporary(String)
5648     * @see org.opencms.file.types.I_CmsResourceType#lockResource(CmsObject, CmsSecurityManager, CmsResource, CmsLockType)
5649     */
5650    public void lockResource(CmsDbContext dbc, CmsResource resource, CmsLockType type) throws CmsException {
5651
5652        // update the resource cache
5653        m_monitor.clearResourceCache();
5654
5655        CmsProject project = dbc.currentProject();
5656
5657        // add the resource to the lock dispatcher
5658        m_lockManager.addResource(dbc, resource, dbc.currentUser(), project, type);
5659        boolean changedProjectLastModified = false;
5660        if (!resource.getState().isUnchanged() && !resource.getState().isKeep()) {
5661            // update the project flag of a modified resource as "last modified inside the current project"
5662            getVfsDriver(dbc).writeLastModifiedProjectId(dbc, project, project.getUuid(), resource);
5663            changedProjectLastModified = true;
5664        }
5665
5666        // we must also clear the permission cache
5667        m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION);
5668
5669        // fire resource modification event
5670        Map<String, Object> data = new HashMap<String, Object>(2);
5671        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
5672        data.put(
5673            I_CmsEventListener.KEY_CHANGE,
5674            new Integer(changedProjectLastModified ? CHANGED_PROJECT : NOTHING_CHANGED));
5675        data.put(I_CmsEventListener.KEY_SKIPINDEX, Boolean.TRUE);
5676        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
5677    }
5678
5679    /**
5680     * Adds the given log entry to the current user's log.<p>
5681     *
5682     * This operation works only on memory, to get the log entries actually
5683     * written to DB you have to call the {@link #updateLog(CmsDbContext)} method.<p>
5684     *
5685     * @param dbc the current database context
5686     * @param logEntry the log entry to create
5687     * @param force forces the log entry to be counted,
5688     *              if not only the first log entry in a transaction will be taken into account
5689     */
5690    public void log(CmsDbContext dbc, CmsLogEntry logEntry, boolean force) {
5691
5692        if (dbc == null) {
5693            return;
5694        }
5695        // check log level
5696        if (!logEntry.getType().isActive()) {
5697            // do not log inactive entries
5698            return;
5699        }
5700        // if not forcing
5701        if (!force) {
5702            // operation already logged
5703            boolean abort = (dbc.getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null);
5704            // disabled logging from outside
5705            abort |= (dbc.getRequestContext().getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null);
5706            if (abort) {
5707                return;
5708            }
5709        }
5710        // prevent several entries for the same operation
5711        dbc.setAttribute(CmsLogEntry.ATTR_LOG_ENTRY, Boolean.TRUE);
5712        // keep it for later
5713        m_log.add(logEntry);
5714    }
5715
5716    /**
5717     * Attempts to authenticate a user into OpenCms with the given password.<p>
5718     *
5719     * @param dbc the current database context
5720     * @param userName the name of the user to be logged in
5721     * @param password the password of the user
5722     * @param remoteAddress the ip address of the request
5723     *
5724     * @return the logged in user
5725     *
5726     * @throws CmsAuthentificationException if the login was not successful
5727     * @throws CmsDataAccessException in case of errors accessing the database
5728     * @throws CmsPasswordEncryptionException in case of errors encrypting the users password
5729     */
5730    public CmsUser loginUser(CmsDbContext dbc, String userName, String password, String remoteAddress)
5731    throws CmsAuthentificationException, CmsDataAccessException, CmsPasswordEncryptionException {
5732
5733        if (CmsStringUtil.isEmptyOrWhitespaceOnly(password)) {
5734            throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, userName));
5735        }
5736        String originalUserName = userName;
5737        CmsUser newUser;
5738        try {
5739            // read the user from the driver to avoid the cache
5740            newUser = getUserDriver(dbc).readUser(dbc, userName, password, remoteAddress);
5741            userName = newUser.getName();
5742
5743        } catch (CmsDbEntryNotFoundException e) {
5744            // this indicates that the username / password combination does not exist
5745            // any other exception indicates database issues, these are not catched here
5746
5747            // check if a user with this name exists at all
5748            CmsUser user = null;
5749            try {
5750                user = readUser(dbc, userName);
5751                userName = user.getName();
5752            } catch (CmsDataAccessException e2) {
5753                // apparently this user does not exist in the database
5754            }
5755
5756            if (user != null) {
5757                if (dbc.currentUser().isGuestUser()) {
5758                    // add an invalid login attempt for this user to the storage
5759                    OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress);
5760                }
5761                OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress);
5762                throw new CmsAuthentificationException(
5763                    org.opencms.security.Messages.get().container(
5764                        org.opencms.security.Messages.ERR_LOGIN_FAILED_2,
5765                        userName,
5766                        remoteAddress),
5767                    e);
5768            } else {
5769                String userOu = CmsOrganizationalUnit.getParentFqn(userName);
5770                if (userOu != null) {
5771                    String parentOu = CmsOrganizationalUnit.getParentFqn(userOu);
5772                    if (parentOu != null) {
5773                        // try a higher level ou
5774                        String uName = CmsOrganizationalUnit.getSimpleName(userName);
5775                        return loginUser(dbc, parentOu + uName, password, remoteAddress);
5776                    }
5777                }
5778                throw new CmsAuthentificationException(
5779                    org.opencms.security.Messages.get().container(
5780                        org.opencms.security.Messages.ERR_LOGIN_FAILED_NO_USER_2,
5781                        userName,
5782                        remoteAddress),
5783                    e);
5784            }
5785        }
5786        // check if the "enabled" flag is set for the user
5787        if (!newUser.isEnabled()) {
5788            // user is disabled, throw a securiy exception
5789            throw new CmsAuthentificationException(
5790                org.opencms.security.Messages.get().container(
5791                    org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2,
5792                    userName,
5793                    remoteAddress));
5794        }
5795
5796        if (dbc.currentUser().isGuestUser()) {
5797            // check if this account is temporarily disabled because of too many invalid login attempts
5798            // this will throw an exception if the test fails
5799            OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress);
5800            // test successful, remove all previous invalid login attempts for this user from the storage
5801            OpenCms.getLoginManager().removeInvalidLogins(userName, remoteAddress);
5802        }
5803
5804        if (!m_securityManager.hasRole(
5805            dbc,
5806            newUser,
5807            CmsRole.ADMINISTRATOR.forOrgUnit(dbc.getRequestContext().getOuFqn()))) {
5808            // new user is not Administrator, check if login is currently allowed
5809            OpenCms.getLoginManager().checkLoginAllowed();
5810        }
5811        m_monitor.clearUserCache(newUser);
5812        // set the last login time to the current time
5813        newUser.setLastlogin(System.currentTimeMillis());
5814
5815        // write the changed user object back to the user driver
5816        Map<String, Object> additionalInfosForRepositories = OpenCms.getRepositoryManager().getAdditionalInfoForLogin(
5817            newUser.getName(),
5818            password);
5819        boolean requiresAddInfoUpdate = false;
5820
5821        // check for changes
5822        for (Entry<String, Object> entry : additionalInfosForRepositories.entrySet()) {
5823            Object value = entry.getValue();
5824            Object current = newUser.getAdditionalInfo(entry.getKey());
5825            if (((value == null) && (current != null)) || ((value != null) && !value.equals(current))) {
5826                requiresAddInfoUpdate = true;
5827                break;
5828            }
5829        }
5830        if (requiresAddInfoUpdate) {
5831            newUser.getAdditionalInfo().putAll(additionalInfosForRepositories);
5832        }
5833        String lastPasswordChange = (String)newUser.getAdditionalInfo(
5834            CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE);
5835        if (lastPasswordChange == null) {
5836            requiresAddInfoUpdate = true;
5837            newUser.getAdditionalInfo().put(
5838                CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE,
5839                "" + System.currentTimeMillis());
5840        }
5841        if (!requiresAddInfoUpdate) {
5842            dbc.setAttribute(ATTRIBUTE_LOGIN, newUser.getName());
5843        }
5844        getUserDriver(dbc).writeUser(dbc, newUser);
5845        int changes = CmsUser.FLAG_LAST_LOGIN;
5846
5847        // check if we need to update the password
5848        if (!OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), false)
5849            && OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), true)) {
5850            // the password does not check with the current hash algorithm but with the fall back, update the password
5851            getUserDriver(dbc).writePassword(dbc, userName, password, password);
5852            changes = changes | CmsUser.FLAG_CORE_DATA;
5853        }
5854
5855        // update cache
5856        m_monitor.cacheUser(newUser);
5857
5858        // invalidate all user dependent caches
5859        m_monitor.flushCache(
5860            CmsMemoryMonitor.CacheType.ACL,
5861            CmsMemoryMonitor.CacheType.GROUP,
5862            CmsMemoryMonitor.CacheType.ORG_UNIT,
5863            CmsMemoryMonitor.CacheType.USER_LIST,
5864            CmsMemoryMonitor.CacheType.PERMISSION,
5865            CmsMemoryMonitor.CacheType.RESOURCE_LIST);
5866
5867        // fire user modified event
5868        Map<String, Object> eventData = new HashMap<String, Object>();
5869        eventData.put(I_CmsEventListener.KEY_USER_ID, newUser.getId().toString());
5870        eventData.put(I_CmsEventListener.KEY_USER_NAME, newUser.getName());
5871        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER);
5872        eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(changes));
5873        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
5874
5875        // return the user object read from the driver
5876        return newUser.clone();
5877    }
5878
5879    /**
5880     * Lookup and read the user or group with the given UUID.<p>
5881     *
5882     * @param dbc the current database context
5883     * @param principalId the UUID of the principal to lookup
5884     *
5885     * @return the principal (group or user) if found, otherwise <code>null</code>
5886     */
5887    public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, CmsUUID principalId) {
5888
5889        try {
5890            CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalId);
5891            if (group != null) {
5892                return group;
5893            }
5894        } catch (Exception e) {
5895            // ignore this exception
5896        }
5897
5898        try {
5899            CmsUser user = readUser(dbc, principalId);
5900            if (user != null) {
5901                return user;
5902            }
5903        } catch (Exception e) {
5904            // ignore this exception
5905        }
5906
5907        return null;
5908    }
5909
5910    /**
5911     * Lookup and read the user or group with the given name.<p>
5912     *
5913     * @param dbc the current database context
5914     * @param principalName the name of the principal to lookup
5915     *
5916     * @return the principal (group or user) if found, otherwise <code>null</code>
5917     */
5918    public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, String principalName) {
5919
5920        try {
5921            CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalName);
5922            if (group != null) {
5923                return group;
5924            }
5925        } catch (Exception e) {
5926            // ignore this exception
5927        }
5928
5929        try {
5930            CmsUser user = readUser(dbc, principalName);
5931            if (user != null) {
5932                return user;
5933            }
5934        } catch (Exception e) {
5935            // ignore this exception
5936        }
5937
5938        return null;
5939    }
5940
5941    /**
5942     * Mark the given resource as visited by the user.<p>
5943     *
5944     * @param dbc the database context
5945     * @param poolName the name of the database pool to use
5946     * @param resource the resource to mark as visited
5947     * @param user the user that visited the resource
5948     *
5949     * @throws CmsException if something goes wrong
5950     */
5951    public void markResourceAsVisitedBy(CmsDbContext dbc, String poolName, CmsResource resource, CmsUser user)
5952    throws CmsException {
5953
5954        getSubscriptionDriver().markResourceAsVisitedBy(dbc, poolName, resource, user);
5955    }
5956
5957    /**
5958     * Moves a resource.<p>
5959     *
5960     * You must ensure that the parent of the destination path is an absolute, valid and
5961     * existing VFS path. Relative paths from the source are not supported.<p>
5962     *
5963     * The moved resource will always be locked to the current user
5964     * after the move operation.<p>
5965     *
5966     * In case the target resource already exists, it will be overwritten with the
5967     * source resource if possible.<p>
5968     *
5969     * @param dbc the current database context
5970     * @param source the resource to move
5971     * @param destination the name of the move destination with complete path
5972     * @param internal if set nothing more than the path is modified
5973     *
5974     * @throws CmsException if something goes wrong
5975     *
5976     * @see CmsSecurityManager#moveResource(CmsRequestContext, CmsResource, String)
5977     */
5978    public void moveResource(CmsDbContext dbc, CmsResource source, String destination, boolean internal)
5979    throws CmsException {
5980
5981        CmsFolder destinationFolder = readFolder(dbc, CmsResource.getParentFolder(destination), CmsResourceFilter.ALL);
5982        m_securityManager.checkPermissions(
5983            dbc,
5984            destinationFolder,
5985            CmsPermissionSet.ACCESS_WRITE,
5986            false,
5987            CmsResourceFilter.ALL);
5988
5989        if (source.isFolder()) {
5990            m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
5991        }
5992        getVfsDriver(dbc).moveResource(dbc, dbc.getRequestContext().getCurrentProject().getUuid(), source, destination);
5993
5994        if (!internal) {
5995            CmsResourceState newState = CmsResource.STATE_CHANGED;
5996            if (source.getState().isNew()) {
5997                newState = CmsResource.STATE_NEW;
5998            } else if (source.getState().isDeleted()) {
5999                newState = CmsResource.STATE_DELETED;
6000            }
6001            source.setState(newState);
6002            // safe since this operation always uses the ids instead of the resource path
6003            getVfsDriver(dbc).writeResourceState(
6004                dbc,
6005                dbc.currentProject(),
6006                source,
6007                CmsDriverManager.UPDATE_STRUCTURE_STATE,
6008                false);
6009            // log it
6010            log(
6011                dbc,
6012                new CmsLogEntry(
6013                    dbc,
6014                    source.getStructureId(),
6015                    CmsLogEntryType.RESOURCE_MOVED,
6016                    new String[] {source.getRootPath(), destination}),
6017                false);
6018        }
6019
6020        CmsResource destRes = readResource(dbc, destination, CmsResourceFilter.ALL);
6021        // move lock
6022        m_lockManager.moveResource(source.getRootPath(), destRes.getRootPath());
6023
6024        // flush all relevant caches
6025        m_monitor.clearAccessControlListCache();
6026        m_monitor.flushCache(
6027            CmsMemoryMonitor.CacheType.PROPERTY,
6028            CmsMemoryMonitor.CacheType.PROPERTY_LIST,
6029            CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
6030
6031        List<CmsResource> resources = new ArrayList<CmsResource>(4);
6032        // source
6033        resources.add(source);
6034        try {
6035            resources.add(readFolder(dbc, CmsResource.getParentFolder(source.getRootPath()), CmsResourceFilter.ALL));
6036        } catch (Exception e) {
6037            if (LOG.isDebugEnabled()) {
6038                LOG.debug(e.getLocalizedMessage(), e);
6039            }
6040        }
6041        // destination
6042        resources.add(destRes);
6043        resources.add(destinationFolder);
6044
6045        Map<String, Object> eventData = new HashMap<String, Object>();
6046        eventData.put(I_CmsEventListener.KEY_RESOURCES, resources);
6047        eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
6048
6049        // fire the events
6050        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MOVED, eventData));
6051    }
6052
6053    /**
6054     * Moves a resource to the "lost and found" folder.<p>
6055     *
6056     * The method can also be used to check get the name of a resource
6057     * in the "lost and found" folder only without actually moving the
6058     * the resource. To do this, the <code>returnNameOnly</code> flag
6059     * must be set to <code>true</code>.<p>
6060     *
6061     * @param dbc the current database context
6062     * @param resource the resource to apply this operation to
6063     * @param returnNameOnly if <code>true</code>, only the name of the resource in the "lost and found"
6064     *        folder is returned, the move operation is not really performed
6065     *
6066     * @return the name of the resource inside the "lost and found" folder
6067     *
6068     * @throws CmsException if something goes wrong
6069     * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0
6070     *
6071     * @see CmsObject#moveToLostAndFound(String)
6072     * @see CmsObject#getLostAndFoundName(String)
6073     */
6074    public String moveToLostAndFound(CmsDbContext dbc, CmsResource resource, boolean returnNameOnly)
6075    throws CmsException, CmsIllegalArgumentException {
6076
6077        String resourcename = dbc.removeSiteRoot(resource.getRootPath());
6078
6079        String siteRoot = dbc.getRequestContext().getSiteRoot();
6080        dbc.getRequestContext().setSiteRoot("");
6081        String destination = CmsDriverManager.LOST_AND_FOUND_FOLDER + resourcename;
6082        // create the required folders if necessary
6083        try {
6084            // collect all folders...
6085            String folderPath = CmsResource.getParentFolder(destination);
6086            folderPath = folderPath.substring(1, folderPath.length() - 1); // cut out leading and trailing '/'
6087            Iterator<String> folders = CmsStringUtil.splitAsList(folderPath, '/').iterator();
6088            // ...now create them....
6089            folderPath = "/";
6090            while (folders.hasNext()) {
6091                folderPath += folders.next().toString() + "/";
6092                try {
6093                    readFolder(dbc, folderPath, CmsResourceFilter.IGNORE_EXPIRATION);
6094                } catch (Exception e1) {
6095                    if (returnNameOnly) {
6096                        // we can use the original name without risk, and we do not need to recreate the parent folders
6097                        break;
6098                    }
6099                    // the folder is not existing, so create it
6100                    createResource(
6101                        dbc,
6102                        folderPath,
6103                        CmsResourceTypeFolder.RESOURCE_TYPE_ID,
6104                        null,
6105                        new ArrayList<CmsProperty>());
6106                }
6107            }
6108            // check if this resource name does already exist
6109            // if so add a postfix to the name
6110            String des = destination;
6111            int postfix = 1;
6112            boolean found = true;
6113            while (found) {
6114                try {
6115                    // try to read the file.....
6116                    found = true;
6117                    readResource(dbc, des, CmsResourceFilter.ALL);
6118                    // ....it's there, so add a postfix and try again
6119                    String path = destination.substring(0, destination.lastIndexOf('/') + 1);
6120                    String filename = destination.substring(destination.lastIndexOf('/') + 1, destination.length());
6121
6122                    des = path;
6123
6124                    if (filename.lastIndexOf('.') > 0) {
6125                        des += filename.substring(0, filename.lastIndexOf('.'));
6126                    } else {
6127                        des += filename;
6128                    }
6129                    des += "_" + postfix;
6130                    if (filename.lastIndexOf('.') > 0) {
6131                        des += filename.substring(filename.lastIndexOf('.'), filename.length());
6132                    }
6133                    postfix++;
6134                } catch (CmsException e3) {
6135                    // the file does not exist, so we can use this filename
6136                    found = false;
6137                }
6138            }
6139            destination = des;
6140
6141            if (!returnNameOnly) {
6142                // do not use the move semantic here! to prevent links pointing to the lost & found folder
6143                copyResource(dbc, resource, destination, CmsResource.COPY_AS_SIBLING);
6144                deleteResource(dbc, resource, CmsResource.DELETE_PRESERVE_SIBLINGS);
6145            }
6146        } catch (CmsException e2) {
6147            throw e2;
6148        } finally {
6149            // set the site root to the old value again
6150            dbc.getRequestContext().setSiteRoot(siteRoot);
6151        }
6152        return destination;
6153    }
6154
6155    /**
6156     * Gets a new driver instance.<p>
6157     *
6158     * @param dbc the database context
6159     * @param configurationManager the configuration manager
6160     * @param driverName the driver name
6161     * @param successiveDrivers the list of successive drivers
6162     *
6163     * @return the driver object
6164     * @throws CmsInitException if the selected driver could not be initialized
6165     */
6166    public Object newDriverInstance(
6167        CmsDbContext dbc,
6168        CmsConfigurationManager configurationManager,
6169        String driverName,
6170        List<String> successiveDrivers)
6171    throws CmsInitException {
6172
6173        Class<?> driverClass = null;
6174        I_CmsDriver driver = null;
6175
6176        try {
6177            // try to get the class
6178            driverClass = Class.forName(driverName);
6179            if (CmsLog.INIT.isInfoEnabled()) {
6180                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName));
6181            }
6182
6183            // try to create a instance
6184            driver = (I_CmsDriver)driverClass.newInstance();
6185            if (CmsLog.INIT.isInfoEnabled()) {
6186                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName));
6187            }
6188
6189            // invoke the init-method of this access class
6190            driver.init(dbc, configurationManager, successiveDrivers, this);
6191            if (CmsLog.INIT.isInfoEnabled()) {
6192                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_0));
6193            }
6194
6195        } catch (Throwable t) {
6196            CmsMessageContainer message = Messages.get().container(
6197                Messages.ERR_ERROR_INITIALIZING_DRIVER_1,
6198                driverName);
6199            if (LOG.isErrorEnabled()) {
6200                LOG.error(message.key(), t);
6201            }
6202            throw new CmsInitException(message, t);
6203        }
6204
6205        return driver;
6206    }
6207
6208    /**
6209     * Method to create a new instance of a driver.<p>
6210     *
6211     * @param configuration the configurations from the propertyfile
6212     * @param driverName the class name of the driver
6213     * @param driverPoolUrl the pool url for the driver
6214     * @return an initialized instance of the driver
6215     * @throws CmsException if something goes wrong
6216     */
6217    public Object newDriverInstance(CmsParameterConfiguration configuration, String driverName, String driverPoolUrl)
6218    throws CmsException {
6219
6220        Class<?>[] initParamClasses = {CmsParameterConfiguration.class, String.class, CmsDriverManager.class};
6221        Object[] initParams = {configuration, driverPoolUrl, this};
6222
6223        Class<?> driverClass = null;
6224        Object driver = null;
6225
6226        try {
6227            // try to get the class
6228            driverClass = Class.forName(driverName);
6229            if (CmsLog.INIT.isInfoEnabled()) {
6230                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName));
6231            }
6232
6233            // try to create a instance
6234            driver = driverClass.newInstance();
6235            if (CmsLog.INIT.isInfoEnabled()) {
6236                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName));
6237            }
6238
6239            // invoke the init-method of this access class
6240            driver.getClass().getMethod("init", initParamClasses).invoke(driver, initParams);
6241            if (CmsLog.INIT.isInfoEnabled()) {
6242                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_1, driverPoolUrl));
6243            }
6244
6245        } catch (Exception exc) {
6246
6247            CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_DRIVER_MANAGER_1);
6248            if (LOG.isFatalEnabled()) {
6249                LOG.fatal(message.key(), exc);
6250            }
6251            throw new CmsDbException(message, exc);
6252
6253        }
6254
6255        return driver;
6256    }
6257
6258    /**
6259     * Method to create a new instance of a pool.<p>
6260     *
6261     * @param configuration the configurations from the propertyfile
6262     * @param poolName the configuration name of the pool
6263     *
6264     * @throws CmsInitException if the pools could not be initialized
6265     */
6266    public void newPoolInstance(CmsParameterConfiguration configuration, String poolName) throws CmsInitException {
6267
6268        CmsDbPoolV11 pool;
6269
6270        try {
6271            pool = new CmsDbPoolV11(configuration, poolName);
6272        } catch (Exception e) {
6273
6274            CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_CONN_POOL_1, poolName);
6275            if (LOG.isErrorEnabled()) {
6276                LOG.error(message.key(), e);
6277            }
6278            throw new CmsInitException(message, e);
6279        }
6280        addPool(pool);
6281    }
6282
6283    /**
6284     * Publishes the given publish job.<p>
6285     *
6286     * @param cms the cms context
6287     * @param dbc the db context
6288     * @param publishList the list of resources to publish
6289     * @param report the report to write to
6290     *
6291     * @throws CmsException if something goes wrong
6292     */
6293    public void publishJob(CmsObject cms, CmsDbContext dbc, CmsPublishList publishList, I_CmsReport report)
6294    throws CmsException {
6295
6296        try {
6297            // check state and lock
6298            List<CmsResource> allResources = new ArrayList<CmsResource>(publishList.getFolderList());
6299            allResources.addAll(publishList.getDeletedFolderList());
6300            allResources.addAll(publishList.getFileList());
6301            Iterator<CmsResource> itResources = allResources.iterator();
6302            while (itResources.hasNext()) {
6303                CmsResource resource = itResources.next();
6304                try {
6305                    resource = readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL);
6306                } catch (CmsVfsResourceNotFoundException e) {
6307                    continue;
6308                }
6309                if (resource.getState().isUnchanged()) {
6310                    // remove files that were published by a concurrent job
6311                    if (LOG.isDebugEnabled()) {
6312                        LOG.debug(
6313                            Messages.get().getBundle().key(
6314                                Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
6315                                dbc.removeSiteRoot(resource.getRootPath())));
6316                    }
6317                    publishList.remove(resource);
6318                    unlockResource(dbc, resource, true, true);
6319                    continue;
6320                }
6321                CmsLock lock = m_lockManager.getLock(dbc, resource, false);
6322                if (!lock.getSystemLock().isPublish()) {
6323                    // remove files that are not locked for publishing
6324                    if (LOG.isDebugEnabled()) {
6325                        LOG.debug(
6326                            Messages.get().getBundle().key(
6327                                Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
6328                                dbc.removeSiteRoot(resource.getRootPath())));
6329                    }
6330                    publishList.remove(resource);
6331                    continue;
6332                }
6333            }
6334
6335            CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
6336
6337            // clear the cache
6338            m_monitor.clearCacheForPublishing();
6339
6340            int publishTag = getNextPublishTag(dbc);
6341            getProjectDriver(dbc).publishProject(dbc, report, onlineProject, publishList, publishTag);
6342
6343            // iterate the initialized module action instances
6344            Iterator<String> i = OpenCms.getModuleManager().getModuleNames().iterator();
6345            while (i.hasNext()) {
6346                CmsModule module = OpenCms.getModuleManager().getModule(i.next());
6347                if ((module != null) && (module.getActionInstance() != null)) {
6348                    module.getActionInstance().publishProject(cms, publishList, publishTag, report);
6349                }
6350            }
6351
6352            boolean temporaryProject = (cms.getRequestContext().getCurrentProject().getType() == CmsProject.PROJECT_TYPE_TEMPORARY);
6353            // the project was stored in the history tables for history
6354            // it will be deleted if the project_flag is PROJECT_TYPE_TEMPORARY
6355            if ((temporaryProject) && (!publishList.isDirectPublish())) {
6356                try {
6357                    getProjectDriver(dbc).deleteProject(dbc, dbc.currentProject());
6358                } catch (CmsException e) {
6359                    LOG.error(
6360                        Messages.get().getBundle().key(
6361                            Messages.LOG_DELETE_TEMP_PROJECT_FAILED_1,
6362                            cms.getRequestContext().getCurrentProject().getName()));
6363                }
6364                // if project was temporary set context to online project
6365                cms.getRequestContext().setCurrentProject(onlineProject);
6366            }
6367        } finally {
6368            // clear the cache again
6369            m_monitor.clearCacheForPublishing();
6370        }
6371    }
6372
6373    /**
6374     * Publishes the resources of a specified publish list.<p>
6375     *
6376     * @param cms the current request context
6377     * @param dbc the current database context
6378     * @param publishList a publish list
6379     * @param report an instance of <code>{@link I_CmsReport}</code> to print messages
6380     *
6381     * @throws CmsException if something goes wrong
6382     *
6383     * @see #fillPublishList(CmsDbContext, CmsPublishList)
6384     */
6385    public synchronized void publishProject(
6386        CmsObject cms,
6387        CmsDbContext dbc,
6388        CmsPublishList publishList,
6389        I_CmsReport report)
6390    throws CmsException {
6391
6392        // check the parent folders
6393        checkParentFolders(dbc, publishList);
6394        ensureSubResourcesOfMovedFoldersPublished(cms, dbc, publishList);
6395        OpenCms.getPublishManager().getPublishListVerifier().checkPublishList(publishList);
6396
6397        try {
6398            // fire an event that a project is to be published
6399            Map<String, Object> eventData = new HashMap<String, Object>();
6400            eventData.put(I_CmsEventListener.KEY_REPORT, report);
6401            eventData.put(I_CmsEventListener.KEY_PUBLISHLIST, publishList);
6402            eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
6403            eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
6404            CmsEvent beforePublishEvent = new CmsEvent(I_CmsEventListener.EVENT_BEFORE_PUBLISH_PROJECT, eventData);
6405            OpenCms.fireCmsEvent(beforePublishEvent);
6406        } catch (Throwable t) {
6407            if (report != null) {
6408                report.addError(t);
6409                report.println(t);
6410            }
6411            if (LOG.isErrorEnabled()) {
6412                LOG.error(t.getLocalizedMessage(), t);
6413            }
6414        }
6415
6416        // lock all resources with the special publish lock
6417        Iterator<CmsResource> itResources = new ArrayList<CmsResource>(publishList.getAllResources()).iterator();
6418        while (itResources.hasNext()) {
6419            CmsResource resource = itResources.next();
6420            CmsLock lock = m_lockManager.getLock(dbc, resource, false);
6421            if (lock.getSystemLock().isUnlocked() && lock.isLockableBy(dbc.currentUser())) {
6422                if (getLock(dbc, resource).getEditionLock().isNullLock()) {
6423                    lockResource(dbc, resource, CmsLockType.PUBLISH);
6424                } else {
6425                    changeLock(dbc, resource, CmsLockType.PUBLISH);
6426                }
6427            } else if (lock.getSystemLock().isPublish()) {
6428                if (LOG.isWarnEnabled()) {
6429                    LOG.warn(
6430                        Messages.get().getBundle().key(
6431                            Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
6432                            dbc.removeSiteRoot(resource.getRootPath())));
6433                }
6434                // remove files that are already waiting to be published
6435                publishList.remove(resource);
6436                continue;
6437            } else {
6438                // this is needed to fix TestPublishIsssues#testPublishScenarioE
6439                changeLock(dbc, resource, CmsLockType.PUBLISH);
6440            }
6441            // now re-check the lock state
6442            lock = m_lockManager.getLock(dbc, resource, false);
6443            if (!lock.getSystemLock().isPublish()) {
6444                if (report != null) {
6445                    report.println(
6446                        Messages.get().container(
6447                            Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
6448                            dbc.removeSiteRoot(resource.getRootPath())),
6449                        I_CmsReport.FORMAT_WARNING);
6450                }
6451                if (LOG.isWarnEnabled()) {
6452                    LOG.warn(
6453                        Messages.get().getBundle().key(
6454                            Messages.RPT_PUBLISH_REMOVED_RESOURCE_1,
6455                            dbc.removeSiteRoot(resource.getRootPath())));
6456                }
6457                // remove files that could not be locked
6458                publishList.remove(resource);
6459            }
6460        }
6461
6462        // enqueue the publish job
6463        CmsException enqueueException = null;
6464        try {
6465            m_publishEngine.enqueuePublishJob(cms, publishList, report);
6466        } catch (CmsException exc) {
6467            enqueueException = exc;
6468        }
6469
6470        // if an exception was raised, remove the publish locks
6471        // and throw the exception again
6472        if (enqueueException != null) {
6473            itResources = publishList.getAllResources().iterator();
6474            while (itResources.hasNext()) {
6475                CmsResource resource = itResources.next();
6476                CmsLock lock = m_lockManager.getLock(dbc, resource, false);
6477                if (lock.getSystemLock().isPublish()
6478                    && lock.getSystemLock().isOwnedInProjectBy(
6479                        cms.getRequestContext().getCurrentUser(),
6480                        cms.getRequestContext().getCurrentProject())) {
6481                    unlockResource(dbc, resource, true, true);
6482                }
6483            }
6484
6485            throw enqueueException;
6486        }
6487    }
6488
6489    /**
6490     * Transfers the new URL name mappings (if any) for a given resource to the online project.<p>
6491     *
6492     * @param dbc the current database context
6493     * @param res the resource whose new URL name mappings should be transferred to the online project
6494     *
6495     * @throws CmsDataAccessException if something goes wrong
6496     */
6497    public void publishUrlNameMapping(CmsDbContext dbc, CmsResource res) throws CmsDataAccessException {
6498
6499        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
6500
6501        if (res.getState().isDeleted()) {
6502            // remove both offline and online mappings
6503            CmsUrlNameMappingFilter idFilter = CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId());
6504            vfsDriver.deleteUrlNameMappingEntries(dbc, true, idFilter);
6505            vfsDriver.deleteUrlNameMappingEntries(dbc, false, idFilter);
6506        } else {
6507            // copy the new entries to the online table
6508            List<CmsUrlNameMappingEntry> entries = vfsDriver.readUrlNameMappingEntries(
6509                dbc,
6510                false,
6511                CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates(
6512                    CmsUrlNameMappingEntry.MAPPING_STATUS_NEW,
6513                    CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH));
6514
6515            boolean isReplaceOnPublish = false;
6516            for (CmsUrlNameMappingEntry entry : entries) {
6517                isReplaceOnPublish |= entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH;
6518            }
6519
6520            if (!entries.isEmpty()) {
6521
6522                long now = System.currentTimeMillis();
6523                if (isReplaceOnPublish) {
6524                    vfsDriver.deleteUrlNameMappingEntries(
6525                        dbc,
6526                        true,
6527                        CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()));
6528                    vfsDriver.deleteUrlNameMappingEntries(
6529                        dbc,
6530                        false,
6531                        CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()));
6532                }
6533
6534                for (CmsUrlNameMappingEntry entry : entries) {
6535                    CmsUrlNameMappingFilter nameFilter = CmsUrlNameMappingFilter.ALL.filterName(entry.getName());
6536                    if (!isReplaceOnPublish) { // we already handled the other case above
6537                        vfsDriver.deleteUrlNameMappingEntries(dbc, true, nameFilter);
6538                        vfsDriver.deleteUrlNameMappingEntries(dbc, false, nameFilter);
6539                    }
6540                }
6541                for (CmsUrlNameMappingEntry entry : entries) {
6542                    CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry(
6543                        entry.getName(),
6544                        entry.getStructureId(),
6545                        entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_NEW
6546                        ? CmsUrlNameMappingEntry.MAPPING_STATUS_PUBLISHED
6547                        : CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH_PUBLISHED,
6548                        now,
6549                        entry.getLocale());
6550                    vfsDriver.addUrlNameMappingEntry(dbc, true, newEntry);
6551                    vfsDriver.addUrlNameMappingEntry(dbc, false, newEntry);
6552                }
6553            }
6554        }
6555    }
6556
6557    /**
6558     * Reads an access control entry from the cms.<p>
6559     *
6560     * The access control entries of a resource are readable by everyone.
6561     *
6562     * @param dbc the current database context
6563     * @param resource the resource
6564     * @param principal the id of a group or a user any other entity
6565     * @return an access control entry that defines the permissions of the entity for the given resource
6566     * @throws CmsException if something goes wrong
6567     */
6568    public CmsAccessControlEntry readAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal)
6569    throws CmsException {
6570
6571        return getUserDriver(
6572            dbc).readAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal);
6573    }
6574
6575    /**
6576     * Finds the alias with a given path.<p>
6577     *
6578     * If no alias is found, null is returned.<p>
6579     *
6580     * @param dbc the current database context
6581     * @param project the current project
6582     * @param siteRoot the site root
6583     * @param path the path of the alias
6584     *
6585     * @return the alias with the given path
6586     *
6587     * @throws CmsException if something goes wrong
6588     */
6589
6590    public CmsAlias readAliasByPath(CmsDbContext dbc, CmsProject project, String siteRoot, String path)
6591    throws CmsException {
6592
6593        List<CmsAlias> aliases = getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(siteRoot, path, null));
6594        if (aliases.isEmpty()) {
6595            return null;
6596        } else {
6597            return aliases.get(0);
6598        }
6599    }
6600
6601    /**
6602     * Reads the aliases for a given site root.<p>
6603     *
6604     * @param dbc the current database context
6605     * @param currentProject the current project
6606     * @param siteRoot the site root
6607     *
6608     * @return the list of aliases for the given site root
6609     *
6610     * @throws CmsException if something goes wrong
6611     */
6612    public List<CmsAlias> readAliasesBySite(CmsDbContext dbc, CmsProject currentProject, String siteRoot)
6613    throws CmsException {
6614
6615        return getVfsDriver(dbc).readAliases(dbc, currentProject, new CmsAliasFilter(siteRoot, null, null));
6616    }
6617
6618    /**
6619     * Reads the aliases which point to a given structure id.<p>
6620     *
6621     * @param dbc the current database context
6622     * @param project the current project
6623     * @param structureId the structure id for which we want to read the aliases
6624     *
6625     * @return the list of aliases pointing to the structure id
6626     * @throws CmsException if something goes wrong
6627     */
6628    public List<CmsAlias> readAliasesByStructureId(CmsDbContext dbc, CmsProject project, CmsUUID structureId)
6629    throws CmsException {
6630
6631        return getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(null, null, structureId));
6632    }
6633
6634    /**
6635     * Reads all versions of the given resource.<br>
6636     *
6637     * This method returns a list with the history of the given resource, i.e.
6638     * the historical resource entries, independent of the project they were attached to.<br>
6639     *
6640     * The reading excludes the file content.<p>
6641     *
6642     * @param dbc the current database context
6643     * @param resource the resource to read the history for
6644     *
6645     * @return a list of file headers, as <code>{@link I_CmsHistoryResource}</code> objects
6646     *
6647     * @throws CmsException if something goes wrong
6648     */
6649    public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsResource resource)
6650    throws CmsException {
6651
6652        // read the historical resources
6653        List<I_CmsHistoryResource> versions = getHistoryDriver(dbc).readAllAvailableVersions(
6654            dbc,
6655            resource.getStructureId());
6656        if ((versions.size() > OpenCms.getSystemInfo().getHistoryVersions())
6657            && (OpenCms.getSystemInfo().getHistoryVersions() > -1)) {
6658            return versions.subList(0, OpenCms.getSystemInfo().getHistoryVersions());
6659        }
6660        return versions;
6661    }
6662
6663    /**
6664     * Reads all property definitions for the given mapping type.<p>
6665     *
6666     * @param dbc the current database context
6667     *
6668     * @return a list with the <code>{@link CmsPropertyDefinition}</code> objects (may be empty)
6669     *
6670     * @throws CmsException if something goes wrong
6671     */
6672    public List<CmsPropertyDefinition> readAllPropertyDefinitions(CmsDbContext dbc) throws CmsException {
6673
6674        List<CmsPropertyDefinition> result = getVfsDriver(dbc).readPropertyDefinitions(
6675            dbc,
6676            dbc.currentProject().getUuid());
6677        Collections.sort(result);
6678        return result;
6679    }
6680
6681    /**
6682     * Returns all resources subscribed by the given user or group.<p>
6683     *
6684     * @param dbc the database context
6685     * @param poolName the name of the database pool to use
6686     * @param principal the principal to read the subscribed resources
6687     *
6688     * @return all resources subscribed by the given user or group
6689     *
6690     * @throws CmsException if something goes wrong
6691     */
6692    public List<CmsResource> readAllSubscribedResources(CmsDbContext dbc, String poolName, CmsPrincipal principal)
6693    throws CmsException {
6694
6695        List<CmsResource> result = getSubscriptionDriver().readAllSubscribedResources(dbc, poolName, principal);
6696        result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT);
6697        return result;
6698    }
6699
6700    /**
6701     * Selects the best url name for a given resource and locale.<p>
6702     *
6703     * @param dbc the database context
6704     * @param id the resource's structure id
6705     * @param locale the requested locale
6706     * @param defaultLocales the default locales to use if the locale isn't available
6707     *
6708     * @return the URL name which was found
6709     *
6710     * @throws CmsDataAccessException if the database operation failed
6711     */
6712    public String readBestUrlName(CmsDbContext dbc, CmsUUID id, Locale locale, List<Locale> defaultLocales)
6713    throws CmsDataAccessException {
6714
6715        List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
6716            dbc,
6717            dbc.currentProject().isOnlineProject(),
6718            CmsUrlNameMappingFilter.ALL.filterStructureId(id));
6719        if (entries.isEmpty()) {
6720            return null;
6721        }
6722
6723        ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create();
6724        for (CmsUrlNameMappingEntry entry : entries) {
6725            entriesByLocale.put(entry.getLocale(), entry);
6726        }
6727        List<CmsUrlNameMappingEntry> lastEntries = new ArrayList<CmsUrlNameMappingEntry>();
6728        Comparator<CmsUrlNameMappingEntry> dateChangedComparator = new UrlNameMappingComparator();
6729        for (String localeKey : entriesByLocale.keySet()) {
6730            // for each locale select the latest mapping entry
6731            CmsUrlNameMappingEntry latestEntryForLocale = Collections.max(
6732                entriesByLocale.get(localeKey),
6733                dateChangedComparator);
6734            lastEntries.add(latestEntryForLocale);
6735        }
6736        CmsLocaleManager localeManager = OpenCms.getLocaleManager();
6737        List<Locale> availableLocales = new ArrayList<Locale>();
6738        for (CmsUrlNameMappingEntry entry : lastEntries) {
6739            availableLocales.add(CmsLocaleManager.getLocale(entry.getLocale()));
6740        }
6741        Locale bestLocale = localeManager.getBestMatchingLocale(locale, defaultLocales, availableLocales);
6742        String bestLocaleStr = bestLocale.toString();
6743        for (CmsUrlNameMappingEntry entry : lastEntries) {
6744            if (entry.getLocale().equals(bestLocaleStr)) {
6745                return entry.getName();
6746            }
6747        }
6748        return null;
6749    }
6750
6751    /**
6752     * Returns the child resources of a resource, that is the resources
6753     * contained in a folder.<p>
6754     *
6755     * With the parameters <code>getFolders</code> and <code>getFiles</code>
6756     * you can control what type of resources you want in the result list:
6757     * files, folders, or both.<p>
6758     *
6759     * This method is mainly used by the workplace explorer.<p>
6760     *
6761     * @param dbc the current database context
6762     * @param resource the resource to return the child resources for
6763     * @param filter the resource filter to use
6764     * @param getFolders if true the child folders are included in the result
6765     * @param getFiles if true the child files are included in the result
6766     * @param checkPermissions if the resources should be filtered with the current user permissions
6767     *
6768     * @return a list of all child resources
6769     *
6770     * @throws CmsException if something goes wrong
6771     */
6772    public List<CmsResource> readChildResources(
6773        CmsDbContext dbc,
6774        CmsResource resource,
6775        CmsResourceFilter filter,
6776        boolean getFolders,
6777        boolean getFiles,
6778        boolean checkPermissions)
6779    throws CmsException {
6780
6781        String cacheKey = null;
6782        List<CmsResource> resourceList = null;
6783        if (m_monitor.isEnabled(CmsMemoryMonitor.CacheType.RESOURCE_LIST)) { // check this here to skip the complex cache key generation
6784            String time = "";
6785            if (checkPermissions) {
6786                // ensure correct caching if site time offset is set
6787                if ((dbc.getRequestContext() != null)
6788                    && (OpenCms.getSiteManager().getSiteForSiteRoot(dbc.getRequestContext().getSiteRoot()) != null)) {
6789                    time += OpenCms.getSiteManager().getSiteForSiteRoot(
6790                        dbc.getRequestContext().getSiteRoot()).getSiteMatcher().getTimeOffset();
6791                }
6792            }
6793            // try to get the sub resources from the cache
6794            cacheKey = getCacheKey(
6795                new String[] {
6796                    dbc.currentUser().getName(),
6797                    getFolders
6798                    ? (getFiles ? CmsCacheKey.CACHE_KEY_SUBALL : CmsCacheKey.CACHE_KEY_SUBFOLDERS)
6799                    : CmsCacheKey.CACHE_KEY_SUBFILES,
6800                    checkPermissions ? "+" + time : "-",
6801                    filter.getCacheId(),
6802                    resource.getRootPath()},
6803                dbc);
6804
6805            resourceList = m_monitor.getCachedResourceList(cacheKey);
6806        }
6807        if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
6808            // read the result form the database
6809            resourceList = getVfsDriver(
6810                dbc).readChildResources(dbc, dbc.currentProject(), resource, getFolders, getFiles);
6811
6812            if (checkPermissions) {
6813                // apply the permission filter
6814                resourceList = filterPermissions(dbc, resourceList, filter);
6815            }
6816            // cache the sub resources
6817            if (dbc.getProjectId().isNullUUID()) {
6818                m_monitor.cacheResourceList(cacheKey, resourceList);
6819            }
6820        }
6821
6822        // we must always apply the result filter and update the context dates
6823        return updateContextDates(dbc, resourceList, filter);
6824    }
6825
6826    /**
6827     * Returns the default file for the given folder.<p>
6828     *
6829     * If the given resource is a file, then this file is returned.<p>
6830     *
6831     * Otherwise, in case of a folder:<br>
6832     * <ol>
6833     *   <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and
6834     *   <li>if still no file could be found, the configured default files in the
6835     *       <code>opencms-vfs.xml</code> configuration are iterated until a match is
6836     *       found, and
6837     *   <li>if still no file could be found, <code>null</code> is retuned
6838     * </ol>
6839     *
6840     * @param dbc the database context
6841     * @param resource the folder to get the default file for
6842     * @param resourceFilter the resource filter
6843     *
6844     * @return the default file for the given folder
6845     */
6846    public CmsResource readDefaultFile(CmsDbContext dbc, CmsResource resource, CmsResourceFilter resourceFilter) {
6847
6848        // resource exists, lets check if we have a file or a folder
6849        if (resource.isFolder()) {
6850            // the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder
6851            try {
6852                String defaultFileName = readPropertyObject(
6853                    dbc,
6854                    resource,
6855                    CmsPropertyDefinition.PROPERTY_DEFAULT_FILE,
6856                    false).getValue();
6857                // check if the default file property does not match the navigation level folder marker value
6858                if ((defaultFileName != null) && !CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER.equals(defaultFileName)) {
6859                    // property was set, so look up this file first
6860                    String folderName = CmsResource.getFolderPath(resource.getRootPath());
6861                    resource = readResource(dbc, folderName + defaultFileName, resourceFilter.addRequireFile());
6862                }
6863            } catch (CmsException e) {
6864                // ignore all other exceptions and continue the lookup process
6865                if (LOG.isDebugEnabled()) {
6866                    LOG.debug(e.getLocalizedMessage(), e);
6867                }
6868            }
6869            if (resource.isFolder()) {
6870                String folderName = CmsResource.getFolderPath(resource.getRootPath());
6871                // resource is (still) a folder, check default files specified in configuration
6872                Iterator<String> it = OpenCms.getDefaultFiles().iterator();
6873                while (it.hasNext()) {
6874                    String tmpResourceName = folderName + it.next();
6875                    try {
6876                        resource = readResource(dbc, tmpResourceName, resourceFilter.addRequireFile());
6877                        // no exception? So we have found the default file
6878                        // stop looking for default files
6879                        break;
6880                    } catch (CmsException e) {
6881                        // ignore all other exceptions and continue the lookup process
6882                        if (LOG.isDebugEnabled()) {
6883                            LOG.debug(e.getLocalizedMessage(), e);
6884                        }
6885                    }
6886                }
6887            }
6888        }
6889        if (resource.isFolder()) {
6890            // we only want files as a result for further processing
6891            resource = null;
6892        }
6893        return resource;
6894    }
6895
6896    /**
6897     * Reads all deleted (historical) resources below the given path,
6898     * including the full tree below the path, if required.<p>
6899     *
6900     * @param dbc the current db context
6901     * @param resource the parent resource to read the resources from
6902     * @param readTree <code>true</code> to read all subresources
6903     * @param isVfsManager <code>true</code> if the current user has the vfs manager role
6904     *
6905     * @return a list of <code>{@link I_CmsHistoryResource}</code> objects
6906     *
6907     * @throws CmsException if something goes wrong
6908     *
6909     * @see CmsObject#readResource(CmsUUID, int)
6910     * @see CmsObject#readResources(String, CmsResourceFilter, boolean)
6911     * @see CmsObject#readDeletedResources(String, boolean)
6912     */
6913    public List<I_CmsHistoryResource> readDeletedResources(
6914        CmsDbContext dbc,
6915        CmsResource resource,
6916        boolean readTree,
6917        boolean isVfsManager)
6918    throws CmsException {
6919
6920        Set<I_CmsHistoryResource> result = new HashSet<I_CmsHistoryResource>();
6921        List<I_CmsHistoryResource> deletedResources;
6922        dbc.getRequestContext().setAttribute("ATTR_RESOURCE_NAME", resource.getRootPath());
6923        try {
6924            deletedResources = getHistoryDriver(dbc).readDeletedResources(
6925                dbc,
6926                resource.getStructureId(),
6927                isVfsManager ? null : dbc.currentUser().getId());
6928        } finally {
6929            dbc.getRequestContext().removeAttribute("ATTR_RESOURCE_NAME");
6930        }
6931        result.addAll(deletedResources);
6932        Set<I_CmsHistoryResource> newResult = new HashSet<I_CmsHistoryResource>(result.size());
6933        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
6934        Iterator<I_CmsHistoryResource> it = result.iterator();
6935        while (it.hasNext()) {
6936            I_CmsHistoryResource histRes = it.next();
6937            // adjust the paths
6938            try {
6939                if (vfsDriver.validateStructureIdExists(
6940                    dbc,
6941                    dbc.currentProject().getUuid(),
6942                    histRes.getStructureId())) {
6943                    newResult.add(histRes);
6944                    continue;
6945                }
6946                // adjust the path in case of deleted files
6947                String resourcePath = histRes.getRootPath();
6948                String resName = CmsResource.getName(resourcePath);
6949                String path = CmsResource.getParentFolder(resourcePath);
6950
6951                CmsUUID parentId = histRes.getParentId();
6952                try {
6953                    // first look for the path through the parent id
6954                    path = readResource(dbc, parentId, CmsResourceFilter.IGNORE_EXPIRATION).getRootPath();
6955                } catch (CmsDataAccessException e) {
6956                    // if the resource with the parent id is not found, try to get a new parent id with the path
6957                    try {
6958                        parentId = readResource(dbc, path, CmsResourceFilter.IGNORE_EXPIRATION).getStructureId();
6959                    } catch (CmsDataAccessException e1) {
6960                        // ignore, the parent folder has been completely deleted
6961                    }
6962                }
6963                resourcePath = path + resName;
6964
6965                boolean isFolder = resourcePath.endsWith("/");
6966                if (isFolder) {
6967                    newResult.add(
6968                        new CmsHistoryFolder(
6969                            histRes.getPublishTag(),
6970                            histRes.getStructureId(),
6971                            histRes.getResourceId(),
6972                            resourcePath,
6973                            histRes.getTypeId(),
6974                            histRes.getFlags(),
6975                            histRes.getProjectLastModified(),
6976                            histRes.getState(),
6977                            histRes.getDateCreated(),
6978                            histRes.getUserCreated(),
6979                            histRes.getDateLastModified(),
6980                            histRes.getUserLastModified(),
6981                            histRes.getDateReleased(),
6982                            histRes.getDateExpired(),
6983                            histRes.getVersion(),
6984                            parentId,
6985                            histRes.getResourceVersion(),
6986                            histRes.getStructureVersion()));
6987                } else {
6988                    newResult.add(
6989                        new CmsHistoryFile(
6990                            histRes.getPublishTag(),
6991                            histRes.getStructureId(),
6992                            histRes.getResourceId(),
6993                            resourcePath,
6994                            histRes.getTypeId(),
6995                            histRes.getFlags(),
6996                            histRes.getProjectLastModified(),
6997                            histRes.getState(),
6998                            histRes.getDateCreated(),
6999                            histRes.getUserCreated(),
7000                            histRes.getDateLastModified(),
7001                            histRes.getUserLastModified(),
7002                            histRes.getDateReleased(),
7003                            histRes.getDateExpired(),
7004                            histRes.getLength(),
7005                            histRes.getDateContent(),
7006                            histRes.getVersion(),
7007                            parentId,
7008                            null,
7009                            histRes.getResourceVersion(),
7010                            histRes.getStructureVersion()));
7011                }
7012            } catch (CmsDataAccessException e) {
7013                // should never happen
7014                if (LOG.isErrorEnabled()) {
7015                    LOG.error(e.getLocalizedMessage(), e);
7016                }
7017            }
7018        }
7019        if (readTree) {
7020            Iterator<I_CmsHistoryResource> itDeleted = deletedResources.iterator();
7021            while (itDeleted.hasNext()) {
7022                I_CmsHistoryResource delResource = itDeleted.next();
7023                if (delResource.isFolder()) {
7024                    newResult.addAll(readDeletedResources(dbc, (CmsFolder)delResource, readTree, isVfsManager));
7025                }
7026            }
7027            try {
7028                readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL);
7029                // resource exists, so recurse
7030                Iterator<CmsResource> itResources = readResources(
7031                    dbc,
7032                    resource,
7033                    CmsResourceFilter.ALL.addRequireFolder(),
7034                    readTree).iterator();
7035                while (itResources.hasNext()) {
7036                    CmsResource subResource = itResources.next();
7037                    if (subResource.isFolder()) {
7038                        newResult.addAll(readDeletedResources(dbc, subResource, readTree, isVfsManager));
7039                    }
7040                }
7041            } catch (Exception e) {
7042                // resource does not exists
7043                if (LOG.isDebugEnabled()) {
7044                    LOG.debug(e.getLocalizedMessage(), e);
7045                }
7046            }
7047        }
7048        List<I_CmsHistoryResource> finalRes = new ArrayList<I_CmsHistoryResource>(newResult);
7049        Collections.sort(finalRes, I_CmsResource.COMPARE_ROOT_PATH);
7050        return finalRes;
7051    }
7052
7053    /**
7054     * Reads a file resource (including it's binary content) from the VFS,
7055     * using the specified resource filter.<p>
7056     *
7057     * In case you do not need the file content,
7058     * use <code>{@link #readResource(CmsDbContext, String, CmsResourceFilter)}</code> instead.<p>
7059     *
7060     * The specified filter controls what kind of resources should be "found"
7061     * during the read operation. This will depend on the application. For example,
7062     * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
7063     * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
7064     * will ignore the date release / date expired information of the resource.<p>
7065     *
7066     * @param dbc the current database context
7067     * @param resource the base file resource (without content)
7068     * @return the file read from the VFS
7069     * @throws CmsException if operation was not successful
7070     */
7071    public CmsFile readFile(CmsDbContext dbc, CmsResource resource) throws CmsException {
7072
7073        if (resource.isFolder()) {
7074            throw new CmsVfsResourceNotFoundException(
7075                Messages.get().container(
7076                    Messages.ERR_ACCESS_FOLDER_AS_FILE_1,
7077                    dbc.removeSiteRoot(resource.getRootPath())));
7078        }
7079
7080        CmsUUID projectId = dbc.currentProject().getUuid();
7081        CmsFile file = null;
7082        if (resource instanceof I_CmsHistoryResource) {
7083            file = new CmsHistoryFile((I_CmsHistoryResource)resource);
7084            file.setContents(
7085                getHistoryDriver(dbc).readContent(
7086                    dbc,
7087                    resource.getResourceId(),
7088                    ((I_CmsHistoryResource)resource).getPublishTag()));
7089        } else {
7090            file = new CmsFile(resource);
7091            file.setContents(getVfsDriver(dbc).readContent(dbc, projectId, resource.getResourceId()));
7092        }
7093        return file;
7094    }
7095
7096    /**
7097     * Reads a folder from the VFS,
7098     * using the specified resource filter.<p>
7099     *
7100     * @param dbc the current database context
7101     * @param resourcename the name of the folder to read (full path)
7102     * @param filter the resource filter to use while reading
7103     *
7104     * @return the folder that was read
7105     *
7106     * @throws CmsDataAccessException if something goes wrong
7107     *
7108     * @see #readResource(CmsDbContext, String, CmsResourceFilter)
7109     * @see CmsObject#readFolder(String)
7110     * @see CmsObject#readFolder(String, CmsResourceFilter)
7111     */
7112    public CmsFolder readFolder(CmsDbContext dbc, String resourcename, CmsResourceFilter filter)
7113    throws CmsDataAccessException {
7114
7115        CmsResource resource = readResource(dbc, resourcename, filter);
7116
7117        return convertResourceToFolder(resource);
7118    }
7119
7120    /**
7121     * Reads the group of a project.<p>
7122     *
7123     * @param dbc the current database context
7124     * @param project the project to read from
7125     *
7126     * @return the group of a resource
7127     */
7128    public CmsGroup readGroup(CmsDbContext dbc, CmsProject project) {
7129
7130        try {
7131            return readGroup(dbc, project.getGroupId());
7132        } catch (CmsException exc) {
7133            return new CmsGroup(
7134                CmsUUID.getNullUUID(),
7135                CmsUUID.getNullUUID(),
7136                project.getGroupId() + "",
7137                "deleted group",
7138                0);
7139        }
7140    }
7141
7142    /**
7143     * Reads a group based on its id.<p>
7144     *
7145     * @param dbc the current database context
7146     * @param groupId the id of the group that is to be read
7147     *
7148     * @return the requested group
7149     *
7150     * @throws CmsException if operation was not successful
7151     */
7152    public CmsGroup readGroup(CmsDbContext dbc, CmsUUID groupId) throws CmsException {
7153
7154        CmsGroup group = null;
7155        // try to read group from cache
7156        group = m_monitor.getCachedGroup(groupId.toString());
7157        if (group == null) {
7158            group = getUserDriver(dbc).readGroup(dbc, groupId);
7159            m_monitor.cacheGroup(group);
7160        }
7161        return group;
7162    }
7163
7164    /**
7165     * Reads a group based on its name.<p>
7166     *
7167     * @param dbc the current database context
7168     * @param groupname the name of the group that is to be read
7169     *
7170     * @return the requested group
7171     *
7172     * @throws CmsDataAccessException if operation was not successful
7173     */
7174    public CmsGroup readGroup(CmsDbContext dbc, String groupname) throws CmsDataAccessException {
7175
7176        CmsGroup group = null;
7177        // try to read group from cache
7178        group = m_monitor.getCachedGroup(groupname);
7179        if (group == null) {
7180            group = getUserDriver(dbc).readGroup(dbc, groupname);
7181            m_monitor.cacheGroup(group);
7182        }
7183        return group;
7184    }
7185
7186    /**
7187     * Reads a principal (an user or group) from the historical archive based on its ID.<p>
7188     *
7189     * @param dbc the current database context
7190     * @param principalId the id of the principal to read
7191     *
7192     * @return the historical principal entry with the given id
7193     *
7194     * @throws CmsException if something goes wrong, ie. {@link CmsDbEntryNotFoundException}
7195     *
7196     * @see CmsObject#readUser(CmsUUID)
7197     * @see CmsObject#readGroup(CmsUUID)
7198     * @see CmsObject#readHistoryPrincipal(CmsUUID)
7199     */
7200    public CmsHistoryPrincipal readHistoricalPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsException {
7201
7202        return getHistoryDriver(dbc).readPrincipal(dbc, principalId);
7203    }
7204
7205    /**
7206     * Returns the latest historical project entry with the given id.<p>
7207     *
7208     * @param dbc the current database context
7209     * @param projectId the project id
7210     *
7211     * @return the requested historical project entry
7212     *
7213     * @throws CmsException if something goes wrong
7214     */
7215    public CmsHistoryProject readHistoryProject(CmsDbContext dbc, CmsUUID projectId) throws CmsException {
7216
7217        return getHistoryDriver(dbc).readProject(dbc, projectId);
7218    }
7219
7220    /**
7221     * Returns a historical project entry.<p>
7222     *
7223     * @param dbc the current database context
7224     * @param publishTag the publish tag of the project
7225     *
7226     * @return the requested historical project entry
7227     *
7228     * @throws CmsException if something goes wrong
7229     */
7230    public CmsHistoryProject readHistoryProject(CmsDbContext dbc, int publishTag) throws CmsException {
7231
7232        return getHistoryDriver(dbc).readProject(dbc, publishTag);
7233    }
7234
7235    /**
7236     * Reads the list of all <code>{@link CmsProperty}</code> objects that belongs to the given historical resource.<p>
7237     *
7238     * @param dbc the current database context
7239     * @param historyResource the historical resource to read the properties for
7240     *
7241     * @return the list of <code>{@link CmsProperty}</code> objects
7242     *
7243     * @throws CmsException if something goes wrong
7244     */
7245    public List<CmsProperty> readHistoryPropertyObjects(CmsDbContext dbc, I_CmsHistoryResource historyResource)
7246    throws CmsException {
7247
7248        return getHistoryDriver(dbc).readProperties(dbc, historyResource);
7249    }
7250
7251    /**
7252     * Reads the structure id which is mapped to a given URL name.<p>
7253     *
7254     * @param dbc the current database context
7255     * @param name the name for which the mapped structure id should be looked up
7256     *
7257     * @return the structure id which is mapped to the given name, or null if there is no such id
7258     *
7259     * @throws CmsDataAccessException if something goes wrong
7260     */
7261    public CmsUUID readIdForUrlName(CmsDbContext dbc, String name) throws CmsDataAccessException {
7262
7263        List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
7264            dbc,
7265            dbc.currentProject().isOnlineProject(),
7266            CmsUrlNameMappingFilter.ALL.filterName(name));
7267        if (entries.isEmpty()) {
7268            return null;
7269        }
7270        return entries.get(0).getStructureId();
7271    }
7272
7273    /**
7274     * Reads the locks that were saved to the database in the previous run of OpenCms.<p>
7275     *
7276     * @param dbc the current database context
7277     *
7278     * @throws CmsException if something goes wrong
7279     */
7280    public void readLocks(CmsDbContext dbc) throws CmsException {
7281
7282        m_lockManager.readLocks(dbc);
7283    }
7284
7285    /**
7286     * Reads the manager group of a project.<p>
7287     *
7288     * @param dbc the current database context
7289     * @param project the project to read from
7290     *
7291     * @return the group of a resource
7292     */
7293    public CmsGroup readManagerGroup(CmsDbContext dbc, CmsProject project) {
7294
7295        try {
7296            return readGroup(dbc, project.getManagerGroupId());
7297        } catch (CmsException exc) {
7298            // the group does not exist any more - return a dummy-group
7299            return new CmsGroup(
7300                CmsUUID.getNullUUID(),
7301                CmsUUID.getNullUUID(),
7302                project.getManagerGroupId() + "",
7303                "deleted group",
7304                0);
7305        }
7306    }
7307
7308    /**
7309     * Reads the URL name which has been most recently mapped to the given structure id, or null
7310     * if no URL name is mapped to the id.<p>
7311     *
7312     * @param dbc the current database context
7313     * @param id a structure id
7314     * @return the name which has been most recently mapped to the given structure id
7315     *
7316     * @throws CmsDataAccessException if something goes wrong
7317     */
7318    public String readNewestUrlNameForId(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException {
7319
7320        List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
7321            dbc,
7322            dbc.currentProject().isOnlineProject(),
7323            CmsUrlNameMappingFilter.ALL.filterStructureId(id));
7324        if (entries.isEmpty()) {
7325            return null;
7326        }
7327
7328        Collections.sort(entries, new UrlNameMappingComparator());
7329        CmsUrlNameMappingEntry lastEntry = entries.get(entries.size() - 1);
7330        return lastEntry.getName();
7331    }
7332
7333    /**
7334     * Reads an organizational Unit based on its fully qualified name.<p>
7335     *
7336     * @param dbc the current db context
7337     * @param ouFqn the fully qualified name of the organizational Unit to be read
7338     *
7339     * @return the organizational Unit that with the provided fully qualified name
7340     *
7341     * @throws CmsException if something goes wrong
7342     */
7343    public CmsOrganizationalUnit readOrganizationalUnit(CmsDbContext dbc, String ouFqn) throws CmsException {
7344
7345        CmsOrganizationalUnit organizationalUnit = null;
7346        // try to read organizational unit from cache
7347        organizationalUnit = m_monitor.getCachedOrgUnit(ouFqn);
7348        if (organizationalUnit == null) {
7349            organizationalUnit = getUserDriver(dbc).readOrganizationalUnit(dbc, ouFqn);
7350            m_monitor.cacheOrgUnit(organizationalUnit);
7351        }
7352        return organizationalUnit;
7353    }
7354
7355    /**
7356     * Reads the owner of a project.<p>
7357     *
7358     * @param dbc the current database context
7359     * @param project the project to get the owner from
7360     *
7361     * @return the owner of a resource
7362     * @throws CmsException if something goes wrong
7363     */
7364    public CmsUser readOwner(CmsDbContext dbc, CmsProject project) throws CmsException {
7365
7366        return readUser(dbc, project.getOwnerId());
7367    }
7368
7369    /**
7370     * Reads the parent folder to a given structure id.<p>
7371     *
7372     * @param dbc the current database context
7373     * @param structureId the structure id of the child
7374     *
7375     * @return the parent folder resource
7376     *
7377     * @throws CmsDataAccessException if something goes wrong
7378     */
7379    public CmsResource readParentFolder(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException {
7380
7381        return getVfsDriver(dbc).readParentFolder(dbc, dbc.currentProject().getUuid(), structureId);
7382    }
7383
7384    /**
7385     * Builds a list of resources for a given path.<p>
7386     *
7387     * @param dbc the current database context
7388     * @param path the requested path
7389     * @param filter a filter object (only "includeDeleted" information is used!)
7390     *
7391     * @return list of <code>{@link CmsResource}</code>s
7392     *
7393     * @throws CmsException if something goes wrong
7394     */
7395    public List<CmsResource> readPath(CmsDbContext dbc, String path, CmsResourceFilter filter) throws CmsException {
7396
7397        // splits the path into folder and filename tokens
7398        List<String> tokens = CmsStringUtil.splitAsList(path, '/');
7399
7400        // the root folder is no token in the path but a resource which has to be added to the path
7401        int count = tokens.size() + 1;
7402        // holds the CmsResource instances in the path
7403        List<CmsResource> pathList = new ArrayList<CmsResource>(count);
7404
7405        // true if the path doesn't end with a folder
7406        boolean lastResourceIsFile = false;
7407        // number of folders in the path
7408        int folderCount = count;
7409        if (!path.endsWith("/")) {
7410            folderCount--;
7411            lastResourceIsFile = true;
7412        }
7413
7414        // read the root folder, because it's ID is required to read any sub-resources
7415        String currentResourceName = "/";
7416        StringBuffer currentPath = new StringBuffer(64);
7417        currentPath.append('/');
7418
7419        String cp = currentPath.toString();
7420        CmsUUID projectId = getProjectIdForContext(dbc);
7421
7422        // key to cache the resources
7423        String cacheKey = getCacheKey(null, false, projectId, cp);
7424        // the current resource
7425        CmsResource currentResource = m_monitor.getCachedResource(cacheKey);
7426        if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) {
7427            currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp);
7428            if (dbc.getProjectId().isNullUUID()) {
7429                m_monitor.cacheResource(cacheKey, currentResource);
7430            }
7431        }
7432
7433        pathList.add(0, currentResource);
7434
7435        if (count == 1) {
7436            // the root folder was requested- no further operations required
7437            return pathList;
7438        }
7439
7440        Iterator<String> it = tokens.iterator();
7441        currentResourceName = it.next();
7442
7443        // read the folder resources in the path /a/b/c/
7444        int i = 0;
7445        for (i = 1; i < folderCount; i++) {
7446            currentPath.append(currentResourceName);
7447            currentPath.append('/');
7448            // read the folder
7449            cp = currentPath.toString();
7450            cacheKey = getCacheKey(null, false, projectId, cp);
7451            currentResource = m_monitor.getCachedResource(cacheKey);
7452            if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) {
7453                currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp);
7454                if (dbc.getProjectId().isNullUUID()) {
7455                    m_monitor.cacheResource(cacheKey, currentResource);
7456                }
7457            }
7458
7459            pathList.add(i, currentResource);
7460
7461            if (i < (folderCount - 1)) {
7462                currentResourceName = it.next();
7463            }
7464        }
7465
7466        // read the (optional) last file resource in the path /x.html
7467        if (lastResourceIsFile) {
7468            if (it.hasNext()) {
7469                // this will only be false if a resource in the
7470                // top level root folder (e.g. "/index.html") was requested
7471                currentResourceName = it.next();
7472            }
7473            currentPath.append(currentResourceName);
7474
7475            // read the file
7476            cp = currentPath.toString();
7477            cacheKey = getCacheKey(null, false, projectId, cp);
7478            currentResource = m_monitor.getCachedResource(cacheKey);
7479            if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) {
7480                currentResource = getVfsDriver(dbc).readResource(dbc, projectId, cp, filter.includeDeleted());
7481                if (dbc.getProjectId().isNullUUID()) {
7482                    m_monitor.cacheResource(cacheKey, currentResource);
7483                }
7484            }
7485
7486            pathList.add(i, currentResource);
7487        }
7488
7489        return pathList;
7490    }
7491
7492    /**
7493     * Reads a project given the projects id.<p>
7494     *
7495     * @param dbc the current database context
7496     * @param id the id of the project
7497     *
7498     * @return the project read
7499     *
7500     * @throws CmsDataAccessException if something goes wrong
7501     */
7502    public CmsProject readProject(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException {
7503
7504        CmsProject project = null;
7505        project = m_monitor.getCachedProject(id.toString());
7506        if (project == null) {
7507            project = getProjectDriver(dbc).readProject(dbc, id);
7508            m_monitor.cacheProject(project);
7509        }
7510        return project;
7511    }
7512
7513    /**
7514     * Reads a project.<p>
7515     *
7516     * Important: Since a project name can be used multiple times, this is NOT the most efficient
7517     * way to read the project. This is only a convenience for front end developing.
7518     * Reading a project by name will return the first project with that name.
7519     * All core classes must use the id version {@link #readProject(CmsDbContext, CmsUUID)} to ensure the right project is read.<p>
7520     *
7521     * @param dbc the current database context
7522     * @param name the name of the project
7523     *
7524     * @return the project read
7525     *
7526     * @throws CmsException if something goes wrong
7527     */
7528    public CmsProject readProject(CmsDbContext dbc, String name) throws CmsException {
7529
7530        CmsProject project = null;
7531        project = m_monitor.getCachedProject(name);
7532        if (project == null) {
7533            project = getProjectDriver(dbc).readProject(dbc, name);
7534            m_monitor.cacheProject(project);
7535        }
7536        return project;
7537    }
7538
7539    /**
7540     * Returns the list of all resource names that define the "view" of the given project.<p>
7541     *
7542     * @param dbc the current database context
7543     * @param project the project to get the project resources for
7544     *
7545     * @return the list of all resources, as <code>{@link String}</code> objects
7546     *              that define the "view" of the given project.
7547     *
7548     * @throws CmsException if something goes wrong
7549     */
7550    public List<String> readProjectResources(CmsDbContext dbc, CmsProject project) throws CmsException {
7551
7552        return getProjectDriver(dbc).readProjectResources(dbc, project);
7553    }
7554
7555    /**
7556     * Reads all resources of a project that match a given state from the VFS.<p>
7557     *
7558     * Possible values for the <code>state</code> parameter are:<br>
7559     * <ul>
7560     * <li><code>{@link CmsResource#STATE_CHANGED}</code>: Read all "changed" resources in the project</li>
7561     * <li><code>{@link CmsResource#STATE_NEW}</code>: Read all "new" resources in the project</li>
7562     * <li><code>{@link CmsResource#STATE_DELETED}</code>: Read all "deleted" resources in the project</li>
7563     * <li><code>{@link CmsResource#STATE_KEEP}</code>: Read all resources either "changed", "new" or "deleted" in the project</li>
7564     * </ul><p>
7565     *
7566     * @param dbc the current database context
7567     * @param projectId the id of the project to read the file resources for
7568     * @param state the resource state to match
7569     *
7570     * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria
7571     *
7572     * @throws CmsException if something goes wrong
7573     *
7574     * @see CmsObject#readProjectView(CmsUUID, CmsResourceState)
7575     */
7576    public List<CmsResource> readProjectView(CmsDbContext dbc, CmsUUID projectId, CmsResourceState state)
7577    throws CmsException {
7578
7579        List<CmsResource> resources;
7580        if (state.isNew() || state.isChanged() || state.isDeleted()) {
7581            // get all resources form the database that match the selected state
7582            resources = getVfsDriver(dbc).readResources(dbc, projectId, state, CmsDriverManager.READMODE_MATCHSTATE);
7583        } else {
7584            // get all resources form the database that are somehow changed (i.e. not unchanged)
7585            resources = getVfsDriver(
7586                dbc).readResources(dbc, projectId, CmsResource.STATE_UNCHANGED, CmsDriverManager.READMODE_UNMATCHSTATE);
7587        }
7588
7589        // filter the permissions
7590        List<CmsResource> result = filterPermissions(dbc, resources, CmsResourceFilter.ALL);
7591        // sort the result
7592        Collections.sort(result);
7593        // set the full resource names
7594        return updateContextDates(dbc, result);
7595    }
7596
7597    /**
7598     * Reads a property definition.<p>
7599     *
7600     * If no property definition with the given name is found,
7601     * <code>null</code> is returned.<p>
7602     *
7603     * @param dbc the current database context
7604     * @param name the name of the property definition to read
7605     *
7606     * @return the property definition that was read
7607     *
7608     * @throws CmsException a CmsDbEntryNotFoundException is thrown if the property definition does not exist
7609     */
7610    public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name) throws CmsException {
7611
7612        return getVfsDriver(dbc).readPropertyDefinition(dbc, name, dbc.currentProject().getUuid());
7613    }
7614
7615    /**
7616     * Reads a property object from a resource specified by a property name.<p>
7617     *
7618     * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p>
7619     *
7620     * @param dbc the current database context
7621     * @param resource the resource where the property is read from
7622     * @param key the property key name
7623     * @param search if <code>true</code>, the property is searched on all parent folders of the resource.
7624     *      if it's not found attached directly to the resource.
7625     *
7626     * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found
7627     *
7628     * @throws CmsException if something goes wrong
7629     */
7630    public CmsProperty readPropertyObject(CmsDbContext dbc, CmsResource resource, String key, boolean search)
7631    throws CmsException {
7632
7633        // NOTE: Do not call readPropertyObject(dbc, resource, key, search, null) for performance reasons
7634
7635        // use the list reading method to obtain all properties for the resource
7636        List<CmsProperty> properties = readPropertyObjects(dbc, resource, search);
7637
7638        int i = properties.indexOf(new CmsProperty(key, null, null));
7639        if (i >= 0) {
7640            // property has been found in the map
7641            CmsProperty result = properties.get(i);
7642            // ensure the result value is not frozen
7643            return result.cloneAsProperty();
7644        }
7645        return CmsProperty.getNullProperty();
7646
7647    }
7648
7649    /**
7650     * Reads a property object from a resource specified by a property name.<p>
7651     *
7652     * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p>
7653     *
7654     * @param dbc the current database context
7655     * @param resource the resource where the property is read from
7656     * @param key the property key name
7657     * @param search if <code>true</code>, the property is searched on all parent folders of the resource.
7658     *      if it's not found attached directly to the resource.
7659     * @param locale the locale for which the property should be read.
7660     *
7661     * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found
7662     *
7663     * @throws CmsException if something goes wrong
7664     */
7665    public CmsProperty readPropertyObject(
7666        CmsDbContext dbc,
7667        CmsResource resource,
7668        String key,
7669        boolean search,
7670        Locale locale)
7671    throws CmsException {
7672
7673        // use the list reading method to obtain all properties for the resource
7674        List<CmsProperty> properties = readPropertyObjects(dbc, resource, search);
7675        // create a lookup property object and look this up in the result map
7676        CmsProperty result = null;
7677        // handle the case without locale separately to improve performance
7678        for (String localizedKey : CmsLocaleManager.getLocaleVariants(key, locale, true, false)) {
7679            int i = properties.indexOf(new CmsProperty(localizedKey, null, null));
7680            if (i >= 0) {
7681                // property has been found in the map
7682                result = properties.get(i);
7683                // ensure the result value is not frozen
7684                return result.cloneAsProperty();
7685            }
7686        }
7687        return CmsProperty.getNullProperty();
7688    }
7689
7690    /**
7691     * Reads all property objects mapped to a specified resource from the database.<p>
7692     *
7693     * All properties in the result List will be in frozen (read only) state, so you can't change the values.<p>
7694     *
7695     * Returns an empty list if no properties are found at all.<p>
7696     *
7697     * @param dbc the current database context
7698     * @param resource the resource where the properties are read from
7699     * @param search true, if the properties should be searched on all parent folders  if not found on the resource
7700     *
7701     * @return a list of CmsProperty objects containing the structure and/or resource value
7702     *
7703     * @throws CmsException if something goes wrong
7704     *
7705     * @see CmsObject#readPropertyObjects(String, boolean)
7706     */
7707    public List<CmsProperty> readPropertyObjects(CmsDbContext dbc, CmsResource resource, boolean search)
7708    throws CmsException {
7709
7710        // check if we have the result already cached
7711        CmsUUID projectId = getProjectIdForContext(dbc);
7712        String cacheKey = getCacheKey(CACHE_ALL_PROPERTIES, search, projectId, resource.getRootPath());
7713
7714        List<CmsProperty> properties = m_monitor.getCachedPropertyList(cacheKey);
7715
7716        if ((properties == null) || !dbc.getProjectId().isNullUUID()) {
7717            // result not cached, let's look it up in the DB
7718            if (search) {
7719                boolean cont;
7720                properties = new ArrayList<CmsProperty>();
7721                List<CmsProperty> parentProperties = null;
7722
7723                do {
7724                    try {
7725                        parentProperties = readPropertyObjects(dbc, resource, false);
7726
7727                        // make sure properties from lower folders "overwrite" properties from upper folders
7728                        parentProperties.removeAll(properties);
7729                        parentProperties.addAll(properties);
7730
7731                        properties.clear();
7732                        properties.addAll(parentProperties);
7733
7734                        cont = resource.getRootPath().length() > 1;
7735                    } catch (CmsSecurityException se) {
7736                        // a security exception (probably no read permission) we return the current result
7737                        cont = false;
7738                    }
7739                    if (cont) {
7740                        // no permission check on parent folder is required since we must have "read"
7741                        // permissions to read the child resource anyway
7742                        resource = readResource(
7743                            dbc,
7744                            CmsResource.getParentFolder(resource.getRootPath()),
7745                            CmsResourceFilter.ALL);
7746                    }
7747                } while (cont);
7748            } else {
7749                properties = getVfsDriver(dbc).readPropertyObjects(dbc, dbc.currentProject(), resource);
7750                //                for (CmsProperty prop : properties) {
7751                //                    prop.setOrigin(resource.getRootPath());
7752                //                }
7753            }
7754
7755            // set all properties in the result list as frozen
7756            CmsProperty.setFrozen(properties);
7757            if (dbc.getProjectId().isNullUUID()) {
7758                // store the result in the cache if needed
7759                m_monitor.cachePropertyList(cacheKey, properties);
7760            }
7761        }
7762
7763        return new ArrayList<CmsProperty>(properties);
7764    }
7765
7766    /**
7767     * Reads the resources that were published in a publish task for a given publish history ID.<p>
7768     *
7769     * @param dbc the current database context
7770     * @param publishHistoryId unique int ID to identify each publish task in the publish history
7771     *
7772     * @return a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects
7773     *
7774     * @throws CmsException if something goes wrong
7775     */
7776    public List<CmsPublishedResource> readPublishedResources(CmsDbContext dbc, CmsUUID publishHistoryId)
7777    throws CmsException {
7778
7779        String cacheKey = publishHistoryId.toString();
7780        List<CmsPublishedResource> resourceList = m_monitor.getCachedPublishedResources(cacheKey);
7781        if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
7782            resourceList = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId);
7783            // store the result in the cache
7784            if (dbc.getProjectId().isNullUUID()) {
7785                m_monitor.cachePublishedResources(cacheKey, resourceList);
7786            }
7787        }
7788        return resourceList;
7789    }
7790
7791    /**
7792     * Reads a single publish job identified by its publish history id.<p>
7793     *
7794     * @param dbc the current database context
7795     * @param publishHistoryId unique id to identify the publish job in the publish history
7796     * @return an object of type <code>{@link CmsPublishJobInfoBean}</code>
7797     *
7798     * @throws CmsException if something goes wrong
7799     */
7800    public CmsPublishJobInfoBean readPublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
7801
7802        return getProjectDriver(dbc).readPublishJob(dbc, publishHistoryId);
7803    }
7804
7805    /**
7806     * Reads all available publish jobs.<p>
7807     *
7808     * @param dbc the current database context
7809     * @param startTime the start of the time range for finish time
7810     * @param endTime the end of the time range for finish time
7811     * @return a list of objects of type <code>{@link CmsPublishJobInfoBean}</code>
7812     *
7813     * @throws CmsException if something goes wrong
7814     */
7815    public List<CmsPublishJobInfoBean> readPublishJobs(CmsDbContext dbc, long startTime, long endTime)
7816    throws CmsException {
7817
7818        return getProjectDriver(dbc).readPublishJobs(dbc, startTime, endTime);
7819    }
7820
7821    /**
7822     * Reads the publish list assigned to a publish job.<p>
7823     *
7824     * @param dbc the current database context
7825     * @param publishHistoryId the history id identifying the publish job
7826     * @return the assigned publish list
7827     * @throws CmsException if something goes wrong
7828     */
7829    public CmsPublishList readPublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
7830
7831        return getProjectDriver(dbc).readPublishList(dbc, publishHistoryId);
7832    }
7833
7834    /**
7835     * Reads the publish report assigned to a publish job.<p>
7836     *
7837     * @param dbc the current database context
7838     * @param publishHistoryId the history id identifying the publish job
7839     * @return the content of the assigned publish report
7840     * @throws CmsException if something goes wrong
7841     */
7842    public byte[] readPublishReportContents(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException {
7843
7844        return getProjectDriver(dbc).readPublishReportContents(dbc, publishHistoryId);
7845    }
7846
7847    /**
7848     * Reads an historical resource entry for the given resource and with the given version number.<p>
7849     *
7850     * @param dbc the current db context
7851     * @param resource the resource to be read
7852     * @param version the version number to retrieve
7853     *
7854     * @return the resource that was read
7855     *
7856     * @throws CmsException if the resource could not be read for any reason
7857     *
7858     * @see CmsObject#restoreResourceVersion(CmsUUID, int)
7859     * @see CmsObject#readResource(CmsUUID, int)
7860     */
7861    public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException {
7862
7863        Iterator<I_CmsHistoryResource> itVersions = getHistoryDriver(dbc).readAllAvailableVersions(
7864            dbc,
7865            resource.getStructureId()).iterator();
7866        while (itVersions.hasNext()) {
7867            I_CmsHistoryResource histRes = itVersions.next();
7868            if (histRes.getVersion() == version) {
7869                return histRes;
7870            }
7871        }
7872        throw new CmsVfsResourceNotFoundException(
7873            org.opencms.db.generic.Messages.get().container(
7874                org.opencms.db.generic.Messages.ERR_HISTORY_FILE_NOT_FOUND_1,
7875                resource.getStructureId()));
7876    }
7877
7878    /**
7879     * Reads a resource from the VFS, using the specified resource filter.<p>
7880     *
7881     * @param dbc the current database context
7882     * @param structureID the structure id of the resource to read
7883     * @param filter the resource filter to use while reading
7884     *
7885     * @return the resource that was read
7886     *
7887     * @throws CmsDataAccessException if something goes wrong
7888     *
7889     * @see CmsObject#readResource(CmsUUID, CmsResourceFilter)
7890     * @see CmsObject#readResource(CmsUUID)
7891     */
7892    public CmsResource readResource(CmsDbContext dbc, CmsUUID structureID, CmsResourceFilter filter)
7893    throws CmsDataAccessException {
7894
7895        CmsUUID projectId = getProjectIdForContext(dbc);
7896        // please note: the filter will be applied in the security manager later
7897        CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, structureID, filter.includeDeleted());
7898
7899        // context dates need to be updated
7900        updateContextDates(dbc, resource);
7901
7902        // return the resource
7903        return resource;
7904    }
7905
7906    /**
7907     * Reads a resource from the VFS, using the specified resource filter.<p>
7908     *
7909     * @param dbc the current database context
7910     * @param resourcePath the name of the resource to read (full path)
7911     * @param filter the resource filter to use while reading
7912     *
7913     * @return the resource that was read
7914     *
7915     * @throws CmsDataAccessException if something goes wrong
7916     *
7917     * @see CmsObject#readResource(String, CmsResourceFilter)
7918     * @see CmsObject#readResource(String)
7919     * @see CmsObject#readFile(CmsResource)
7920     */
7921    public CmsResource readResource(CmsDbContext dbc, String resourcePath, CmsResourceFilter filter)
7922    throws CmsDataAccessException {
7923
7924        CmsUUID projectId = getProjectIdForContext(dbc);
7925        // please note: the filter will be applied in the security manager later
7926        CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, resourcePath, filter.includeDeleted());
7927
7928        // context dates need to be updated
7929        updateContextDates(dbc, resource);
7930
7931        // return the resource
7932        return resource;
7933    }
7934
7935    /**
7936     * Reads all resources below the given path matching the filter criteria,
7937     * including the full tree below the path only in case the <code>readTree</code>
7938     * parameter is <code>true</code>.<p>
7939     *
7940     * @param dbc the current database context
7941     * @param parent the parent path to read the resources from
7942     * @param filter the filter
7943     * @param readTree <code>true</code> to read all subresources
7944     *
7945     * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria
7946     *
7947     * @throws CmsDataAccessException if the bare reading of the resources fails
7948     * @throws CmsException if security and permission checks for the resources read fail
7949     */
7950    public List<CmsResource> readResources(
7951        CmsDbContext dbc,
7952        CmsResource parent,
7953        CmsResourceFilter filter,
7954        boolean readTree)
7955    throws CmsException, CmsDataAccessException {
7956
7957        // try to get the sub resources from the cache
7958        String cacheKey = getCacheKey(
7959            new String[] {dbc.currentUser().getName(), filter.getCacheId(), readTree ? "+" : "-", parent.getRootPath()},
7960            dbc);
7961
7962        List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey);
7963        if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
7964            // read the result from the database
7965            resourceList = getVfsDriver(dbc).readResourceTree(
7966                dbc,
7967                dbc.currentProject().getUuid(),
7968                (readTree ? parent.getRootPath() : parent.getStructureId().toString()),
7969                filter.getType(),
7970                filter.getState(),
7971                filter.getModifiedAfter(),
7972                filter.getModifiedBefore(),
7973                filter.getReleaseAfter(),
7974                filter.getReleaseBefore(),
7975                filter.getExpireAfter(),
7976                filter.getExpireBefore(),
7977                (readTree ? CmsDriverManager.READMODE_INCLUDE_TREE : CmsDriverManager.READMODE_EXCLUDE_TREE)
7978                    | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0)
7979                    | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0)
7980                    | ((filter.getOnlyFolders() != null)
7981                    ? (filter.getOnlyFolders().booleanValue()
7982                    ? CmsDriverManager.READMODE_ONLY_FOLDERS
7983                    : CmsDriverManager.READMODE_ONLY_FILES)
7984                    : 0));
7985
7986            // HACK: do not take care of permissions if reading organizational units
7987            if (!parent.getRootPath().startsWith("/system/orgunits/")) {
7988                // apply permission filter
7989                resourceList = filterPermissions(dbc, resourceList, filter);
7990            }
7991            // store the result in the resourceList cache
7992            if (dbc.getProjectId().isNullUUID()) {
7993                m_monitor.cacheResourceList(cacheKey, resourceList);
7994            }
7995        }
7996        // we must always apply the result filter and update the context dates
7997        return updateContextDates(dbc, resourceList, filter);
7998    }
7999
8000    /**
8001     * Returns the resources that were visited by a user set in the filter.<p>
8002     *
8003     * @param dbc the database context
8004     * @param poolName the name of the database pool to use
8005     * @param filter the filter that is used to get the visited resources
8006     *
8007     * @return the resources that were visited by a user set in the filter
8008     *
8009     * @throws CmsException if something goes wrong
8010     */
8011    public List<CmsResource> readResourcesVisitedBy(CmsDbContext dbc, String poolName, CmsVisitedByFilter filter)
8012    throws CmsException {
8013
8014        List<CmsResource> result = getSubscriptionDriver().readResourcesVisitedBy(dbc, poolName, filter);
8015        result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT);
8016        return result;
8017    }
8018
8019    /**
8020     * Reads all resources that have a value (containing the given value string) set
8021     * for the specified property (definition) in the given path.<p>
8022     *
8023     * Both individual and shared properties of a resource are checked.<p>
8024     *
8025     * If the <code>value</code> parameter is <code>null</code>, all resources having the
8026     * given property set are returned.<p>
8027     *
8028     * @param dbc the current database context
8029     * @param folder the folder to get the resources with the property from
8030     * @param propertyDefinition the name of the property (definition) to check for
8031     * @param value the string to search in the value of the property
8032     * @param filter the resource filter to apply to the result set
8033     *
8034     * @return a list of all <code>{@link CmsResource}</code> objects
8035     *          that have a value set for the specified property.
8036     *
8037     * @throws CmsException if something goes wrong
8038     */
8039    public List<CmsResource> readResourcesWithProperty(
8040        CmsDbContext dbc,
8041        CmsResource folder,
8042        String propertyDefinition,
8043        String value,
8044        CmsResourceFilter filter)
8045    throws CmsException {
8046
8047        String cacheKey;
8048        if (value == null) {
8049            cacheKey = getCacheKey(
8050                new String[] {
8051                    dbc.currentUser().getName(),
8052                    folder.getRootPath(),
8053                    propertyDefinition,
8054                    filter.getCacheId()},
8055                dbc);
8056        } else {
8057            cacheKey = getCacheKey(
8058                new String[] {
8059                    dbc.currentUser().getName(),
8060                    folder.getRootPath(),
8061                    propertyDefinition,
8062                    value,
8063                    filter.getCacheId()},
8064                dbc);
8065        }
8066        List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey);
8067        if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) {
8068            // first read the property definition
8069            CmsPropertyDefinition propDef = readPropertyDefinition(dbc, propertyDefinition);
8070            // now read the list of resources that have a value set for the property definition
8071            resourceList = getVfsDriver(dbc).readResourcesWithProperty(
8072                dbc,
8073                dbc.currentProject().getUuid(),
8074                propDef.getId(),
8075                folder.getRootPath(),
8076                value);
8077            // apply permission filter
8078            resourceList = filterPermissions(dbc, resourceList, filter);
8079            // store the result in the resourceList cache
8080            if (dbc.getProjectId().isNullUUID()) {
8081                m_monitor.cacheResourceList(cacheKey, resourceList);
8082            }
8083        }
8084        // we must always apply the result filter and update the context dates
8085        return updateContextDates(dbc, resourceList, filter);
8086    }
8087
8088    /**
8089     * Returns the set of users that are responsible for a specific resource.<p>
8090     *
8091     * @param dbc the current database context
8092     * @param resource the resource to get the responsible users from
8093     *
8094     * @return the set of users that are responsible for a specific resource
8095     *
8096     * @throws CmsException if something goes wrong
8097     */
8098    public Set<I_CmsPrincipal> readResponsiblePrincipals(CmsDbContext dbc, CmsResource resource) throws CmsException {
8099
8100        Set<I_CmsPrincipal> result = new HashSet<I_CmsPrincipal>();
8101        Iterator<CmsAccessControlEntry> aces = getAccessControlEntries(dbc, resource, true).iterator();
8102        while (aces.hasNext()) {
8103            CmsAccessControlEntry ace = aces.next();
8104            if (ace.isResponsible()) {
8105                I_CmsPrincipal p = lookupPrincipal(dbc, ace.getPrincipal());
8106                if (p != null) {
8107                    result.add(p);
8108                }
8109            }
8110        }
8111        return result;
8112    }
8113
8114    /**
8115     * Returns the set of users that are responsible for a specific resource.<p>
8116     *
8117     * @param dbc the current database context
8118     * @param resource the resource to get the responsible users from
8119     *
8120     * @return the set of users that are responsible for a specific resource
8121     *
8122     * @throws CmsException if something goes wrong
8123     */
8124    public Set<CmsUser> readResponsibleUsers(CmsDbContext dbc, CmsResource resource) throws CmsException {
8125
8126        Set<CmsUser> result = new HashSet<CmsUser>();
8127        Iterator<I_CmsPrincipal> principals = readResponsiblePrincipals(dbc, resource).iterator();
8128        while (principals.hasNext()) {
8129            I_CmsPrincipal principal = principals.next();
8130            if (principal.isGroup()) {
8131                try {
8132                    result.addAll(getUsersOfGroup(dbc, principal.getName(), true, false, false));
8133                } catch (CmsException e) {
8134                    if (LOG.isInfoEnabled()) {
8135                        LOG.info(e.getLocalizedMessage(), e);
8136                    }
8137                }
8138            } else {
8139                result.add((CmsUser)principal);
8140            }
8141        }
8142        return result;
8143    }
8144
8145    /**
8146     * Returns a List of all siblings of the specified resource,
8147     * the specified resource being always part of the result set.<p>
8148     *
8149     * The result is a list of <code>{@link CmsResource}</code> objects.<p>
8150     *
8151     * @param dbc the current database context
8152     * @param resource the resource to read the siblings for
8153     * @param filter a filter object
8154     *
8155     * @return a list of <code>{@link CmsResource}</code> Objects that
8156     *          are siblings to the specified resource,
8157     *          including the specified resource itself
8158     *
8159     * @throws CmsException if something goes wrong
8160     */
8161    public List<CmsResource> readSiblings(CmsDbContext dbc, CmsResource resource, CmsResourceFilter filter)
8162    throws CmsException {
8163
8164        List<CmsResource> siblings = getVfsDriver(
8165            dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, filter.includeDeleted());
8166
8167        // important: there is no permission check done on the returned list of siblings
8168        // this is because of possible issues with the "publish all siblings" option,
8169        // moreover the user has read permission for the content through
8170        // the selected sibling anyway
8171        return updateContextDates(dbc, siblings, filter);
8172    }
8173
8174    /**
8175     * Returns the parameters of a resource in the table of all published template resources.<p>
8176     *
8177     * @param dbc the current database context
8178     * @param rfsName the rfs name of the resource
8179     *
8180     * @return the parameter string of the requested resource
8181     *
8182     * @throws CmsException if something goes wrong
8183     */
8184    public String readStaticExportPublishedResourceParameters(CmsDbContext dbc, String rfsName) throws CmsException {
8185
8186        return getProjectDriver(dbc).readStaticExportPublishedResourceParameters(dbc, rfsName);
8187    }
8188
8189    /**
8190     * Returns a list of all template resources which must be processed during a static export.<p>
8191     *
8192     * @param dbc the current database context
8193     * @param parameterResources flag for reading resources with parameters (1) or without (0)
8194     * @param timestamp for reading the data from the db
8195     *
8196     * @return a list of template resources as <code>{@link String}</code> objects
8197     *
8198     * @throws CmsException if something goes wrong
8199     */
8200    public List<String> readStaticExportResources(CmsDbContext dbc, int parameterResources, long timestamp)
8201    throws CmsException {
8202
8203        return getProjectDriver(dbc).readStaticExportResources(dbc, parameterResources, timestamp);
8204    }
8205
8206    /**
8207     * Returns the subscribed history resources that were deleted.<p>
8208     *
8209     * @param dbc the database context
8210     * @param poolName the name of the database pool to use
8211     * @param user the user that subscribed to the resource
8212     * @param groups the groups to check subscribed resources for
8213     * @param parent the parent resource (folder) of the deleted resources, if <code>null</code> all deleted resources will be returned
8214     * @param includeSubFolders indicates if the sub folders of the specified folder path should be considered, too
8215     * @param deletedFrom the time stamp from which the resources should have been deleted
8216     *
8217     * @return the subscribed history resources that were deleted
8218     *
8219     * @throws CmsException if something goes wrong
8220     */
8221    public List<I_CmsHistoryResource> readSubscribedDeletedResources(
8222        CmsDbContext dbc,
8223        String poolName,
8224        CmsUser user,
8225        List<CmsGroup> groups,
8226        CmsResource parent,
8227        boolean includeSubFolders,
8228        long deletedFrom)
8229    throws CmsException {
8230
8231        List<I_CmsHistoryResource> result = getSubscriptionDriver().readSubscribedDeletedResources(
8232            dbc,
8233            poolName,
8234            user,
8235            groups,
8236            parent,
8237            includeSubFolders,
8238            deletedFrom);
8239
8240        return result;
8241    }
8242
8243    /**
8244     * Returns the resources that were subscribed by a user or group set in the filter.<p>
8245     *
8246     * @param dbc the database context
8247     * @param poolName the name of the database pool to use
8248     * @param filter the filter that is used to get the subscribed resources
8249     *
8250     * @return the resources that were subscribed by a user or group set in the filter
8251     *
8252     * @throws CmsException if something goes wrong
8253     */
8254    public List<CmsResource> readSubscribedResources(CmsDbContext dbc, String poolName, CmsSubscriptionFilter filter)
8255    throws CmsException {
8256
8257        List<CmsResource> result = getSubscriptionDriver().readSubscribedResources(dbc, poolName, filter);
8258
8259        result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT);
8260        return result;
8261    }
8262
8263    /**
8264     * Reads URL name mapping entries which match the given filter.<p>
8265     *
8266     * @param dbc the database context
8267     * @param online if true, read online URL name mappings, else offline ones
8268     * @param filter the filter for matching the URL name entries
8269     *
8270     * @return the list of URL name mapping entries which match the given filter
8271     *
8272     * @throws CmsDataAccessException if something goes wrong
8273     */
8274    public List<CmsUrlNameMappingEntry> readUrlNameMappingEntries(
8275        CmsDbContext dbc,
8276        boolean online,
8277        CmsUrlNameMappingFilter filter)
8278    throws CmsDataAccessException {
8279
8280        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
8281        return vfsDriver.readUrlNameMappingEntries(dbc, online, filter);
8282    }
8283
8284    /**
8285     * Reads the URL name mappings matching the given filter.<p>
8286     *
8287     * @param dbc the DB context to use
8288     * @param filter the filter used to select the mapping entries
8289     * @return the entries matching the given filter
8290     *
8291     * @throws CmsDataAccessException if something goes wrong
8292     */
8293    public List<CmsUrlNameMappingEntry> readUrlNameMappings(CmsDbContext dbc, CmsUrlNameMappingFilter filter)
8294    throws CmsDataAccessException {
8295
8296        List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
8297            dbc,
8298            dbc.currentProject().isOnlineProject(),
8299            filter);
8300        return entries;
8301    }
8302
8303    /**
8304     * Reads the newest URL names of a resource for all locales.<p>
8305     *
8306     * @param dbc the database context
8307     * @param id the resource's structure id
8308     *
8309     * @return the url names for the locales
8310     *
8311     * @throws CmsDataAccessException if the database operation failed
8312     */
8313    public List<String> readUrlNamesForAllLocales(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException {
8314
8315        List<String> result = new ArrayList<String>();
8316        List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries(
8317            dbc,
8318            dbc.currentProject().isOnlineProject(),
8319            CmsUrlNameMappingFilter.ALL.filterStructureId(id));
8320        ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create();
8321        for (CmsUrlNameMappingEntry entry : entries) {
8322            String localeKey = entry.getLocale();
8323            entriesByLocale.put(localeKey, entry);
8324        }
8325
8326        for (String localeKey : entriesByLocale.keySet()) {
8327            List<CmsUrlNameMappingEntry> entrs = entriesByLocale.get(localeKey);
8328            CmsUrlNameMappingEntry maxEntryForLocale = Collections.max(entrs, new UrlNameMappingComparator());
8329            result.add(maxEntryForLocale.getName());
8330        }
8331        return result;
8332    }
8333
8334    /**
8335     * Returns a user object based on the id of a user.<p>
8336     *
8337     * @param dbc the current database context
8338     * @param id the id of the user to read
8339     *
8340     * @return the user read
8341     *
8342     * @throws CmsException if something goes wrong
8343     */
8344    public CmsUser readUser(CmsDbContext dbc, CmsUUID id) throws CmsException {
8345
8346        CmsUser user = m_monitor.getCachedUser(id.toString());
8347        if (user == null) {
8348            user = getUserDriver(dbc).readUser(dbc, id);
8349            m_monitor.cacheUser(user);
8350        }
8351        // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects
8352        return user.clone();
8353    }
8354
8355    /**
8356     * Returns a user object.<p>
8357     *
8358     * @param dbc the current database context
8359     * @param username the name of the user that is to be read
8360     *
8361     * @return user read
8362     *
8363     * @throws CmsDataAccessException if operation was not successful
8364     */
8365    public CmsUser readUser(CmsDbContext dbc, String username) throws CmsDataAccessException {
8366
8367        CmsUser user = m_monitor.getCachedUser(username);
8368        if (user == null) {
8369            user = getUserDriver(dbc).readUser(dbc, username);
8370            m_monitor.cacheUser(user);
8371        }
8372        // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects
8373        return user.clone();
8374    }
8375
8376    /**
8377     * Returns a user object if the password for the user is correct.<p>
8378     *
8379     * If the user/pwd pair is not valid a <code>{@link CmsException}</code> is thrown.<p>
8380     *
8381     * @param dbc the current database context
8382     * @param username the username of the user that is to be read
8383     * @param password the password of the user that is to be read
8384     *
8385     * @return user read
8386     *
8387     * @throws CmsException if operation was not successful
8388     */
8389    public CmsUser readUser(CmsDbContext dbc, String username, String password) throws CmsException {
8390
8391        // don't read user from cache here because password may have changed
8392        CmsUser user = getUserDriver(dbc).readUser(dbc, username, password, null);
8393        m_monitor.cacheUser(user);
8394        return user;
8395    }
8396
8397    /**
8398     * Removes an access control entry for a given resource and principal.<p>
8399     *
8400     * @param dbc the current database context
8401     * @param resource the resource
8402     * @param principal the id of the principal to remove the the access control entry for
8403     *
8404     * @throws CmsException if something goes wrong
8405     */
8406    public void removeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal)
8407    throws CmsException {
8408
8409        // remove the ace
8410        getUserDriver(dbc).removeAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal);
8411
8412        // log it
8413        log(
8414            dbc,
8415            new CmsLogEntry(
8416                dbc,
8417                resource.getStructureId(),
8418                CmsLogEntryType.RESOURCE_PERMISSIONS,
8419                new String[] {resource.getRootPath()}),
8420            false);
8421
8422        // update the "last modified" information
8423        setDateLastModified(dbc, resource, resource.getDateLastModified());
8424
8425        // clear the cache
8426        m_monitor.clearAccessControlListCache();
8427
8428        // fire a resource modification event
8429        Map<String, Object> data = new HashMap<String, Object>(2);
8430        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
8431        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_ACCESSCONTROL));
8432        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
8433    }
8434
8435    /**
8436     * Removes a resource from the given organizational unit.<p>
8437     *
8438     * @param dbc the current db context
8439     * @param orgUnit the organizational unit to remove the resource from
8440     * @param resource the resource that is to be removed from the organizational unit
8441     *
8442     * @throws CmsException if something goes wrong
8443     *
8444     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
8445     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
8446     */
8447    public void removeResourceFromOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource)
8448    throws CmsException {
8449
8450        m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
8451        getUserDriver(dbc).removeResourceFromOrganizationalUnit(dbc, orgUnit, resource);
8452    }
8453
8454    /**
8455     * Removes a resource from the current project of the user.<p>
8456     *
8457     * @param dbc the current database context
8458     * @param resource the resource to apply this operation to
8459     *
8460     * @throws CmsException if something goes wrong
8461     *
8462     * @see CmsObject#copyResourceToProject(String)
8463     * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource)
8464     */
8465    public void removeResourceFromProject(CmsDbContext dbc, CmsResource resource) throws CmsException {
8466
8467        // remove the resource to the project only if the resource is already in the project
8468        if (isInsideCurrentProject(dbc, resource.getRootPath())) {
8469            // check if there are already any subfolders of this resource
8470            I_CmsProjectDriver projectDriver = getProjectDriver(dbc);
8471            if (resource.isFolder()) {
8472                List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject());
8473                for (int i = 0; i < projectResources.size(); i++) {
8474                    String resname = projectResources.get(i);
8475                    if (resname.startsWith(resource.getRootPath())) {
8476                        // delete the existing project resource first
8477                        projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname);
8478                    }
8479                }
8480            }
8481            try {
8482                projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath());
8483            } catch (CmsException exc) {
8484                // if the subfolder exists already - all is ok
8485            } finally {
8486                m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES);
8487
8488                OpenCms.fireCmsEvent(
8489                    new CmsEvent(
8490                        I_CmsEventListener.EVENT_PROJECT_MODIFIED,
8491                        Collections.<String, Object> singletonMap("project", dbc.currentProject())));
8492            }
8493        }
8494    }
8495
8496    /**
8497     * Removes the given resource to the given user's publish list.<p>
8498     *
8499     * @param dbc the database context
8500     * @param userId the user's id
8501     * @param structureIds the collection of structure IDs to remove
8502     *
8503     * @throws CmsDataAccessException if something goes wrong
8504     */
8505    public void removeResourceFromUsersPubList(CmsDbContext dbc, CmsUUID userId, Collection<CmsUUID> structureIds)
8506    throws CmsDataAccessException {
8507
8508        for (CmsUUID structureId : structureIds) {
8509            CmsLogEntry entry = new CmsLogEntry(
8510                userId,
8511                System.currentTimeMillis(),
8512                structureId,
8513                CmsLogEntryType.RESOURCE_HIDDEN,
8514                new String[] {readResource(dbc, structureId, CmsResourceFilter.ALL).getRootPath()});
8515            log(dbc, entry, true);
8516        }
8517    }
8518
8519    /**
8520     * Removes a user from a group.<p>
8521     *
8522     * @param dbc the current database context
8523     * @param username the name of the user that is to be removed from the group
8524     * @param groupname the name of the group
8525     * @param readRoles if to read roles or groups
8526     *
8527     * @throws CmsException if operation was not successful
8528     * @throws CmsIllegalArgumentException if the given user was not member in the given group
8529     * @throws CmsDbEntryNotFoundException if the given group was not found
8530     * @throws CmsSecurityException if the given user was <b>read as 'null' from the database</b>
8531     *
8532     * @see #addUserToGroup(CmsDbContext, String, String, boolean)
8533     */
8534    public void removeUserFromGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles)
8535    throws CmsException, CmsIllegalArgumentException, CmsDbEntryNotFoundException, CmsSecurityException {
8536
8537        CmsGroup group = readGroup(dbc, groupname);
8538        //check if group exists
8539        if (group == null) {
8540            // the group does not exists
8541            throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
8542        }
8543        if (group.isVirtual() && !readRoles) {
8544            // if removing a user from a virtual role treat it as removing the user from the role
8545            removeUserFromGroup(dbc, username, CmsRole.valueOf(group).getGroupName(), true);
8546            return;
8547        }
8548        if (group.isVirtual()) {
8549            // this is an hack so to prevent a unlimited recursive calls
8550            readRoles = false;
8551        }
8552        if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) {
8553            // we want a role but we got a group, or the other way
8554            throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname));
8555        }
8556
8557        boolean skipRemove = false;
8558        // test if this user is existing in the group
8559        if (!userInGroup(dbc, username, groupname, readRoles)) {
8560            if (readRoles) {
8561                // Sometimes users can end up with the default groups corresponding to roles (Administrators, Users) without the actual roles.
8562                // When trying to remove the user from such a group, we end up here in a recursive call of this method with readRoles = true. We do not
8563                // want to throw an exception then, because it would prevent the code that actually removes the user from the group from running.
8564                LOG.warn(
8565                    "Trying to remove user from role that they are not a member of (user: "
8566                        + username
8567                        + ", group: "
8568                        + groupname
8569                        + ")");
8570                skipRemove = true;
8571            } else {
8572                // user is not in the group, throw exception
8573                throw new CmsIllegalArgumentException(
8574                    Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname));
8575            }
8576        }
8577
8578        CmsUser user = readUser(dbc, username);
8579        //check if the user exists
8580        if (user == null) {
8581            // the user does not exists
8582            throw new CmsIllegalArgumentException(
8583                Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname));
8584        }
8585
8586        if (readRoles) {
8587            CmsRole role = CmsRole.valueOf(group);
8588            // update virtual groups
8589            Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator();
8590            while (it.hasNext()) {
8591                CmsGroup virtualGroup = it.next();
8592                if (userInGroup(dbc, username, virtualGroup.getName(), false)) {
8593                    // here we say readroles = true, to prevent an unlimited recursive calls
8594                    removeUserFromGroup(dbc, username, virtualGroup.getName(), true);
8595                }
8596            }
8597        }
8598        if (!skipRemove) {
8599            getUserDriver(dbc).deleteUserInGroup(dbc, user.getId(), group.getId());
8600        }
8601
8602        // flush relevant caches
8603        if (readRoles) {
8604            m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST);
8605        }
8606        m_monitor.flushUserGroups(user.getId());
8607        m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST);
8608
8609        if (!dbc.getProjectId().isNullUUID()) {
8610            // user modified event is not needed
8611            return;
8612        }
8613        // fire user modified event
8614        Map<String, Object> eventData = new HashMap<String, Object>();
8615        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
8616        eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
8617        eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
8618        eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName());
8619        eventData.put(
8620            I_CmsEventListener.KEY_USER_ACTION,
8621            I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP);
8622        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
8623
8624    }
8625
8626    /**
8627     * Repairs broken categories.<p>
8628     *
8629     * @param dbc the database context
8630     * @param projectId the project id
8631     * @param resource the resource to repair the categories for
8632     *
8633     * @throws CmsException if something goes wrong
8634     */
8635    public void repairCategories(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsException {
8636
8637        CmsObject cms = OpenCms.initCmsObject(new CmsObject(getSecurityManager(), dbc.getRequestContext()));
8638        cms.getRequestContext().setSiteRoot("");
8639        cms.getRequestContext().setCurrentProject(readProject(dbc, projectId));
8640        CmsCategoryService.getInstance().repairRelations(cms, resource);
8641    }
8642
8643    /**
8644     * Replaces the content, type and properties of a resource.<p>
8645     *
8646     * @param dbc the current database context
8647     * @param resource the name of the resource to apply this operation to
8648     * @param type the new type of the resource
8649     * @param content the new content of the resource
8650     * @param properties the new properties of the resource
8651     *
8652     * @throws CmsException if something goes wrong
8653     *
8654     * @see CmsObject#replaceResource(String, int, byte[], List)
8655     * @see I_CmsResourceType#replaceResource(CmsObject, CmsSecurityManager, CmsResource, int, byte[], List)
8656     */
8657    @SuppressWarnings("javadoc")
8658    public void replaceResource(
8659        CmsDbContext dbc,
8660        CmsResource resource,
8661        int type,
8662        byte[] content,
8663        List<CmsProperty> properties)
8664    throws CmsException {
8665
8666        // replace the existing with the new file content
8667        getVfsDriver(dbc).replaceResource(dbc, resource, content, type);
8668
8669        if ((properties != null) && !properties.isEmpty()) {
8670            // write the properties
8671            getVfsDriver(dbc).writePropertyObjects(dbc, dbc.currentProject(), resource, properties);
8672            m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST);
8673        }
8674
8675        // update the resource state
8676        if (resource.getState().isUnchanged()) {
8677            resource.setState(CmsResource.STATE_CHANGED);
8678        }
8679        resource.setUserLastModified(dbc.currentUser().getId());
8680
8681        // log it
8682        log(
8683            dbc,
8684            new CmsLogEntry(
8685                dbc,
8686                resource.getStructureId(),
8687                CmsLogEntryType.RESOURCE_CONTENT_MODIFIED,
8688                new String[] {resource.getRootPath()}),
8689            false);
8690
8691        setDateLastModified(dbc, resource, System.currentTimeMillis());
8692
8693        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false);
8694
8695        deleteRelationsWithSiblings(dbc, resource);
8696
8697        // clear the cache
8698        m_monitor.clearResourceCache();
8699
8700        if ((properties != null) && !properties.isEmpty()) {
8701            // resource and properties were modified
8702            OpenCms.fireCmsEvent(
8703                new CmsEvent(
8704                    I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
8705                    Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource)));
8706        } else {
8707            // only the resource was modified
8708            Map<String, Object> data = new HashMap<String, Object>(2);
8709            data.put(I_CmsEventListener.KEY_RESOURCE, resource);
8710            data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE | CHANGED_CONTENT));
8711            OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
8712        }
8713    }
8714
8715    /**
8716     * Resets the password for a specified user.<p>
8717     *
8718     * @param dbc the current database context
8719     * @param username the name of the user
8720     * @param oldPassword the old password
8721     * @param newPassword the new password
8722     *
8723     * @throws CmsException if the user data could not be read from the database
8724     * @throws CmsSecurityException if the specified username and old password could not be verified
8725     */
8726    public void resetPassword(CmsDbContext dbc, String username, String oldPassword, String newPassword)
8727    throws CmsException, CmsSecurityException {
8728
8729        if ((oldPassword != null) && (newPassword != null)) {
8730
8731            CmsUser user = null;
8732
8733            if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) {
8734                validatePassword(newPassword);
8735            }
8736
8737            // read the user as a system user to verify that the specified old password is correct
8738            try {
8739                user = getUserDriver(dbc).readUser(dbc, username, oldPassword, null);
8740            } catch (CmsDbEntryNotFoundException e) {
8741                throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), e);
8742            }
8743
8744            if ((user == null) || user.isManaged()) {
8745                throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username));
8746            }
8747
8748            getUserDriver(dbc).writePassword(dbc, username, oldPassword, newPassword);
8749            user.getAdditionalInfo().put(
8750                CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE,
8751                "" + System.currentTimeMillis());
8752            user.deleteAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_PASSWORD_RESET);
8753            getUserDriver(dbc).writeUser(dbc, user);
8754
8755            if (!dbc.getProjectId().isNullUUID()) {
8756                // user modified event is not needed
8757                return;
8758            }
8759            // fire user modified event
8760            Map<String, Object> eventData = new HashMap<String, Object>();
8761            eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
8762            eventData.put(
8763                I_CmsEventListener.KEY_USER_ACTION,
8764                I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD);
8765            eventData.put(
8766                I_CmsEventListener.KEY_USER_CHANGES,
8767                Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA));
8768            OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
8769
8770        } else if (CmsStringUtil.isEmpty(oldPassword)) {
8771            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_OLD_MISSING_0));
8772        } else if (CmsStringUtil.isEmpty(newPassword)) {
8773            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_NEW_MISSING_0));
8774        }
8775    }
8776
8777    /**
8778     * Restores a deleted resource identified by its structure id from the historical archive.<p>
8779     *
8780     * @param dbc the current database context
8781     * @param structureId the structure id of the resource to restore
8782     *
8783     * @throws CmsException if something goes wrong
8784     *
8785     * @see CmsObject#restoreDeletedResource(CmsUUID)
8786     */
8787    public void restoreDeletedResource(CmsDbContext dbc, CmsUUID structureId) throws CmsException {
8788
8789        // get the last version, which should be the deleted one
8790        int version = getHistoryDriver(dbc).readLastVersion(dbc, structureId);
8791        // get that version
8792        I_CmsHistoryResource histRes = getHistoryDriver(dbc).readResource(dbc, structureId, version);
8793
8794        // check the parent path
8795        CmsResource parent;
8796        try {
8797            // try to read the parent resource by id
8798            parent = getVfsDriver(dbc).readResource(dbc, dbc.currentProject().getUuid(), histRes.getParentId(), true);
8799        } catch (CmsVfsResourceNotFoundException e) {
8800            // if not found try to read the parent resource by name
8801            try {
8802                // try to read the parent resource by id
8803                parent = getVfsDriver(dbc).readResource(
8804                    dbc,
8805                    dbc.currentProject().getUuid(),
8806                    CmsResource.getParentFolder(histRes.getRootPath()),
8807                    true);
8808            } catch (CmsVfsResourceNotFoundException e1) {
8809                // if not found try to restore the parent resource
8810                restoreDeletedResource(dbc, histRes.getParentId());
8811                parent = readResource(dbc, histRes.getParentId(), CmsResourceFilter.IGNORE_EXPIRATION);
8812            }
8813        }
8814        // check write permissions
8815        m_securityManager.checkPermissions(
8816            dbc,
8817            parent,
8818            CmsPermissionSet.ACCESS_WRITE,
8819            false,
8820            CmsResourceFilter.IGNORE_EXPIRATION);
8821
8822        // check the name
8823        String path = parent.getRootPath();
8824        String resName = CmsResource.getName(histRes.getRootPath()); // name
8825        String ext = "";
8826        if (resName.charAt(resName.length() - 1) == '/') {
8827            resName = resName.substring(0, resName.length() - 1);
8828        } else {
8829            ext = CmsFileUtil.getExtension(resName); // extension
8830        }
8831        String nameWOExt = resName.substring(0, resName.length() - ext.length()); // name without extension
8832        for (int i = 1; true; i++) {
8833            try {
8834                readResource(dbc, path + resName, CmsResourceFilter.ALL);
8835                resName = nameWOExt + "_" + i + ext;
8836                // try the next resource name with following schema: path/name_{i}.ext
8837            } catch (CmsVfsResourceNotFoundException e) {
8838                // ok, we found a not used resource name
8839                break;
8840            }
8841        }
8842
8843        // check structure id
8844        CmsUUID id = structureId;
8845        if (getVfsDriver(dbc).validateStructureIdExists(dbc, dbc.currentProject().getUuid(), structureId)) {
8846            // should never happen, but if already exists create a new one
8847            id = new CmsUUID();
8848        }
8849
8850        byte[] contents = null;
8851        boolean isFolder = true;
8852
8853        // do we need the contents?
8854        if (histRes instanceof CmsFile) {
8855            contents = ((CmsFile)histRes).getContents();
8856            if ((contents == null) || (contents.length == 0)) {
8857                contents = getHistoryDriver(dbc).readContent(dbc, histRes.getResourceId(), histRes.getPublishTag());
8858            }
8859            isFolder = false;
8860        }
8861
8862        // now read the historical properties
8863        List<CmsProperty> properties = getHistoryDriver(dbc).readProperties(dbc, histRes);
8864
8865        // create the object to create
8866        CmsResource newResource = new CmsResource(
8867            id,
8868            histRes.getResourceId(),
8869            path + resName,
8870            histRes.getTypeId(),
8871            isFolder,
8872            histRes.getFlags(),
8873            dbc.currentProject().getUuid(),
8874            CmsResource.STATE_NEW,
8875            histRes.getDateCreated(),
8876            histRes.getUserCreated(),
8877            histRes.getDateLastModified(),
8878            dbc.currentUser().getId(),
8879            histRes.getDateReleased(),
8880            histRes.getDateExpired(),
8881            histRes.getSiblingCount(),
8882            histRes.getLength(),
8883            histRes.getDateContent(),
8884            histRes.getVersion());
8885
8886        // log it
8887        log(
8888            dbc,
8889            new CmsLogEntry(
8890                dbc,
8891                newResource.getStructureId(),
8892                CmsLogEntryType.RESOURCE_RESTORE_DELETED,
8893                new String[] {newResource.getRootPath()}),
8894            false);
8895
8896        // prevent the date last modified is set to the current time
8897        newResource.setDateLastModified(newResource.getDateLastModified());
8898        // restore the resource!
8899        CmsResource resource = createResource(dbc, path + resName, newResource, contents, properties, true);
8900        // set resource state to changed
8901        newResource.setState(CmsResource.STATE_CHANGED);
8902        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), newResource, UPDATE_RESOURCE_STATE, false);
8903        newResource.setState(CmsResource.STATE_NEW);
8904        // fire the event
8905        Map<String, Object> data = new HashMap<String, Object>(2);
8906        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
8907        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE | CHANGED_CONTENT));
8908        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
8909    }
8910
8911    /**
8912     * Restores a resource in the current project with a version from the historical archive.<p>
8913     *
8914     * @param dbc the current database context
8915     * @param resource the resource to restore from the archive
8916     * @param version the version number to restore from the archive
8917     *
8918     * @throws CmsException if something goes wrong
8919     *
8920     * @see CmsObject#restoreResourceVersion(CmsUUID, int)
8921     * @see I_CmsResourceType#restoreResource(CmsObject, CmsSecurityManager, CmsResource, int)
8922     */
8923    public void restoreResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException {
8924
8925        I_CmsHistoryResource historyResource = readResource(dbc, resource, version);
8926        CmsResourceState state = CmsResource.STATE_CHANGED;
8927        if (resource.getState().isNew()) {
8928            state = CmsResource.STATE_NEW;
8929        }
8930        int newVersion = resource.getVersion();
8931        if (resource.getState().isUnchanged()) {
8932            newVersion++;
8933        }
8934        CmsResource newResource = null;
8935        // is the resource a file?
8936        if (historyResource instanceof CmsFile) {
8937            // get the historical up flags
8938            int flags = historyResource.getFlags();
8939            if (resource.isLabeled()) {
8940                // set the flag for labeled links on the restored file
8941                flags |= CmsResource.FLAG_LABELED;
8942            }
8943            CmsFile newFile = new CmsFile(
8944                resource.getStructureId(),
8945                resource.getResourceId(),
8946                resource.getRootPath(),
8947                historyResource.getTypeId(),
8948                flags,
8949                dbc.currentProject().getUuid(),
8950                state,
8951                resource.getDateCreated(),
8952                historyResource.getUserCreated(),
8953                resource.getDateLastModified(),
8954                dbc.currentUser().getId(),
8955                historyResource.getDateReleased(),
8956                historyResource.getDateExpired(),
8957                resource.getSiblingCount(),
8958                historyResource.getLength(),
8959                historyResource.getDateContent(),
8960                newVersion,
8961                readFile(dbc, (CmsHistoryFile)historyResource).getContents());
8962
8963            // log it
8964            log(
8965                dbc,
8966                new CmsLogEntry(
8967                    dbc,
8968                    newFile.getStructureId(),
8969                    CmsLogEntryType.RESOURCE_HISTORY,
8970                    new String[] {newFile.getRootPath()}),
8971                false);
8972
8973            newResource = writeFile(dbc, newFile);
8974        } else {
8975            // it is a folder!
8976            newResource = new CmsFolder(
8977                resource.getStructureId(),
8978                resource.getResourceId(),
8979                resource.getRootPath(),
8980                historyResource.getTypeId(),
8981                historyResource.getFlags(),
8982                dbc.currentProject().getUuid(),
8983                state,
8984                resource.getDateCreated(),
8985                historyResource.getUserCreated(),
8986                resource.getDateLastModified(),
8987                dbc.currentUser().getId(),
8988                historyResource.getDateReleased(),
8989                historyResource.getDateExpired(),
8990                newVersion);
8991
8992            // log it
8993            log(
8994                dbc,
8995                new CmsLogEntry(
8996                    dbc,
8997                    newResource.getStructureId(),
8998                    CmsLogEntryType.RESOURCE_HISTORY,
8999                    new String[] {newResource.getRootPath()}),
9000                false);
9001
9002            writeResource(dbc, newResource);
9003        }
9004        if (newResource != null) {
9005            // now read the historical properties
9006            List<CmsProperty> historyProperties = getHistoryDriver(dbc).readProperties(dbc, historyResource);
9007            // remove all properties
9008            deleteAllProperties(dbc, newResource.getRootPath());
9009            // write them to the restored resource
9010            writePropertyObjects(dbc, newResource, historyProperties, false);
9011
9012            m_monitor.clearResourceCache();
9013        }
9014
9015        Map<String, Object> data = new HashMap<String, Object>(2);
9016        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9017        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE | CHANGED_CONTENT));
9018        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9019    }
9020
9021    /**
9022     * Saves a list of aliases for the same structure id, replacing any aliases for the same structure id.<p>
9023     *
9024     * @param dbc the current database context
9025     * @param project the current project
9026     * @param structureId the structure id for which the aliases should be saved
9027     * @param aliases the list of aliases to save
9028     *
9029     * @throws CmsException if something goes wrong
9030     */
9031    public void saveAliases(CmsDbContext dbc, CmsProject project, CmsUUID structureId, List<CmsAlias> aliases)
9032    throws CmsException {
9033
9034        for (CmsAlias alias : aliases) {
9035            if (!structureId.equals(alias.getStructureId())) {
9036                throw new IllegalArgumentException("Aliases to replace must have the same structure id!");
9037            }
9038        }
9039        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
9040        vfsDriver.deleteAliases(dbc, project, new CmsAliasFilter(null, null, structureId));
9041        for (CmsAlias alias : aliases) {
9042            String aliasPath = alias.getAliasPath();
9043            if (CmsAlias.ALIAS_PATTERN.matcher(aliasPath).matches()) {
9044                vfsDriver.insertAlias(dbc, project, alias);
9045            } else {
9046                LOG.error("Invalid alias path: " + aliasPath);
9047            }
9048        }
9049    }
9050
9051    /**
9052     * Replaces the complete list of rewrite aliases for a given site root.<p>
9053     *
9054     * @param dbc the current database context
9055     * @param siteRoot the site root for which the rewrite aliases should be replaced
9056     * @param newAliases the new aliases for the given site root
9057     * @throws CmsException if something goes wrong
9058     */
9059    public void saveRewriteAliases(CmsDbContext dbc, String siteRoot, List<CmsRewriteAlias> newAliases)
9060    throws CmsException {
9061
9062        CmsRewriteAliasFilter filter = new CmsRewriteAliasFilter().setSiteRoot(siteRoot);
9063        getVfsDriver(dbc).deleteRewriteAliases(dbc, filter);
9064        getVfsDriver(dbc).insertRewriteAliases(dbc, newAliases);
9065    }
9066
9067    /**
9068     * Searches for users which fit the given criteria.<p>
9069     *
9070     * @param dbc the database context
9071     * @param searchParams the search criteria
9072     *
9073     * @return the users which fit the search criteria
9074     *
9075     * @throws CmsDataAccessException if something goes wrong
9076     */
9077    public List<CmsUser> searchUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams
9078
9079    ) throws CmsDataAccessException {
9080
9081        return getUserDriver(dbc).searchUsers(dbc, searchParams);
9082    }
9083
9084    /**
9085     * Changes the "expire" date of a resource.<p>
9086     *
9087     * @param dbc the current database context
9088     * @param resource the resource to touch
9089     * @param dateExpired the new expire date of the resource
9090     *
9091     * @throws CmsDataAccessException if something goes wrong
9092     *
9093     * @see CmsObject#setDateExpired(String, long, boolean)
9094     * @see I_CmsResourceType#setDateExpired(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
9095     */
9096    public void setDateExpired(CmsDbContext dbc, CmsResource resource, long dateExpired) throws CmsDataAccessException {
9097
9098        resource.setDateExpired(dateExpired);
9099        if (resource.getState().isUnchanged()) {
9100            resource.setState(CmsResource.STATE_CHANGED);
9101        }
9102        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false);
9103
9104        // modify the last modified project reference
9105        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false);
9106        // log
9107        log(
9108            dbc,
9109            new CmsLogEntry(
9110                dbc,
9111                resource.getStructureId(),
9112                CmsLogEntryType.RESOURCE_DATE_EXPIRED,
9113                new String[] {resource.getRootPath()}),
9114            false);
9115
9116        // clear the cache
9117        m_monitor.clearResourceCache();
9118
9119        // fire the event
9120        Map<String, Object> data = new HashMap<String, Object>(2);
9121        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9122        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_TIMEFRAME));
9123        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9124    }
9125
9126    /**
9127     * Changes the "last modified" timestamp of a resource.<p>
9128     *
9129     * @param dbc the current database context
9130     * @param resource the resource to touch
9131     * @param dateLastModified the new last modified date of the resource
9132     *
9133     * @throws CmsDataAccessException if something goes wrong
9134     *
9135     * @see CmsObject#setDateLastModified(String, long, boolean)
9136     * @see I_CmsResourceType#setDateLastModified(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
9137     */
9138    public void setDateLastModified(CmsDbContext dbc, CmsResource resource, long dateLastModified)
9139    throws CmsDataAccessException {
9140
9141        // modify the last modification date
9142        resource.setDateLastModified(dateLastModified);
9143        if (resource.getState().isUnchanged()) {
9144            resource.setState(CmsResource.STATE_CHANGED);
9145        } else if (resource.getState().isNew() && (resource.getSiblingCount() > 1)) {
9146            // in case of new resources with siblings make sure the state is correct
9147            resource.setState(CmsResource.STATE_CHANGED);
9148        }
9149        resource.setUserLastModified(dbc.currentUser().getId());
9150        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false);
9151
9152        log(
9153            dbc,
9154            new CmsLogEntry(
9155                dbc,
9156                resource.getStructureId(),
9157                CmsLogEntryType.RESOURCE_TOUCHED,
9158                new String[] {resource.getRootPath()}),
9159            false);
9160
9161        // clear the cache
9162        m_monitor.clearResourceCache();
9163
9164        // fire the event
9165        Map<String, Object> data = new HashMap<String, Object>(2);
9166        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9167        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_LASTMODIFIED));
9168        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9169    }
9170
9171    /**
9172     * Changes the "release" date of a resource.<p>
9173     *
9174     * @param dbc the current database context
9175     * @param resource the resource to touch
9176     * @param dateReleased the new release date of the resource
9177     *
9178     * @throws CmsDataAccessException if something goes wrong
9179     *
9180     * @see CmsObject#setDateReleased(String, long, boolean)
9181     * @see I_CmsResourceType#setDateReleased(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
9182     */
9183    public void setDateReleased(CmsDbContext dbc, CmsResource resource, long dateReleased)
9184    throws CmsDataAccessException {
9185
9186        // modify the last modification date
9187        resource.setDateReleased(dateReleased);
9188        if (resource.getState().isUnchanged()) {
9189            resource.setState(CmsResource.STATE_CHANGED);
9190        }
9191        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false);
9192
9193        // modify the last modified project reference
9194        getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false);
9195        // log it
9196        log(
9197            dbc,
9198            new CmsLogEntry(
9199                dbc,
9200                resource.getStructureId(),
9201                CmsLogEntryType.RESOURCE_DATE_RELEASED,
9202                new String[] {resource.getRootPath()}),
9203            false);
9204
9205        // clear the cache
9206        m_monitor.clearResourceCache();
9207
9208        // fire the event
9209        Map<String, Object> data = new HashMap<String, Object>(2);
9210        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9211        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_TIMEFRAME));
9212        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9213    }
9214
9215    /**
9216     * Sets a new parent group for an already existing group.<p>
9217     *
9218     * @param dbc the current database context
9219     * @param groupName the name of the group that should be written
9220     * @param parentGroupName the name of the parent group to set,
9221     *                      or <code>null</code> if the parent
9222     *                      group should be deleted.
9223     *
9224     * @throws CmsException if operation was not successful
9225     * @throws CmsDataAccessException if the group with <code>groupName</code> could not be read from VFS
9226     */
9227    public void setParentGroup(CmsDbContext dbc, String groupName, String parentGroupName)
9228    throws CmsException, CmsDataAccessException {
9229
9230        CmsGroup group = readGroup(dbc, groupName);
9231        CmsUUID parentGroupId = CmsUUID.getNullUUID();
9232
9233        // if the group exists, use its id, else set to unknown.
9234        if (parentGroupName != null) {
9235            parentGroupId = readGroup(dbc, parentGroupName).getId();
9236        }
9237
9238        group.setParentId(parentGroupId);
9239
9240        // write the changes to the cms
9241        writeGroup(dbc, group);
9242    }
9243
9244    /**
9245     * Sets the password for a user.<p>
9246     *
9247     * @param dbc the current database context
9248     * @param username the name of the user
9249     * @param newPassword the new password
9250     *
9251     * @throws CmsException if operation was not successful
9252     * @throws CmsIllegalArgumentException if the user with the <code>username</code> was not found
9253     */
9254    public void setPassword(CmsDbContext dbc, String username, String newPassword)
9255    throws CmsException, CmsIllegalArgumentException {
9256
9257        if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) {
9258            validatePassword(newPassword);
9259        }
9260
9261        // read the user as a system user to verify that the specified old password is correct
9262        CmsUser user = getUserDriver(dbc).readUser(dbc, username);
9263        // only continue if not found and read user from web might succeed
9264        getUserDriver(dbc).writePassword(dbc, username, null, newPassword);
9265        user.getAdditionalInfo().put(
9266            CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE,
9267            "" + System.currentTimeMillis());
9268        getUserDriver(dbc).writeUser(dbc, user);
9269
9270        // fire user modified event
9271        Map<String, Object> eventData = new HashMap<String, Object>();
9272        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
9273        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD);
9274        eventData.put(
9275            I_CmsEventListener.KEY_USER_CHANGES,
9276            Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA));
9277        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
9278    }
9279
9280    /**
9281     * Marks a subscribed resource as deleted.<p>
9282     *
9283     * @param dbc the database context
9284     * @param poolName the name of the database pool to use
9285     * @param resource the subscribed resource to mark as deleted
9286     *
9287     * @throws CmsException if something goes wrong
9288     */
9289    public void setSubscribedResourceAsDeleted(CmsDbContext dbc, String poolName, CmsResource resource)
9290    throws CmsException {
9291
9292        getSubscriptionDriver().setSubscribedResourceAsDeleted(dbc, poolName, resource);
9293    }
9294
9295    /**
9296     * Moves an user to the given organizational unit.<p>
9297     *
9298     * @param dbc the current db context
9299     * @param orgUnit the organizational unit to add the resource to
9300     * @param user the user that is to be moved to the organizational unit
9301     *
9302     * @throws CmsException if something goes wrong
9303     *
9304     * @see org.opencms.security.CmsOrgUnitManager#setUsersOrganizationalUnit(CmsObject, String, String)
9305     */
9306    public void setUsersOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsUser user)
9307    throws CmsException {
9308
9309        if (!getGroupsOfUser(dbc, user.getName(), false).isEmpty()) {
9310            throw new CmsDbConsistencyException(
9311                Messages.get().container(Messages.ERR_ORGUNIT_MOVE_USER_2, orgUnit.getName(), user.getName()));
9312        }
9313
9314        // move the principal
9315        getUserDriver(dbc).setUsersOrganizationalUnit(dbc, orgUnit, user);
9316        // remove the principal from cache
9317        m_monitor.clearUserCache(user);
9318
9319        if (!dbc.getProjectId().isNullUUID()) {
9320            // user modified event is not needed
9321            return;
9322        }
9323        // fire user modified event
9324        Map<String, Object> eventData = new HashMap<String, Object>();
9325        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
9326        eventData.put(I_CmsEventListener.KEY_OU_NAME, user.getOuFqn());
9327        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU);
9328        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
9329    }
9330
9331    /**
9332     * Subscribes the user or group to the resource.<p>
9333     *
9334     * @param dbc the database context
9335     * @param poolName the name of the database pool to use
9336     * @param principal the principal that subscribes to the resource
9337     * @param resource the resource to subscribe to
9338     *
9339     * @throws CmsException if something goes wrong
9340     */
9341    public void subscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource)
9342    throws CmsException {
9343
9344        getSubscriptionDriver().subscribeResourceFor(dbc, poolName, principal, resource);
9345    }
9346
9347    /**
9348     * Undelete the resource.<p>
9349     *
9350     * @param dbc the current database context
9351     * @param resource the name of the resource to apply this operation to
9352     *
9353     * @throws CmsException if something goes wrong
9354     *
9355     * @see CmsObject#undeleteResource(String, boolean)
9356     * @see I_CmsResourceType#undelete(CmsObject, CmsSecurityManager, CmsResource, boolean)
9357     */
9358    public void undelete(CmsDbContext dbc, CmsResource resource) throws CmsException {
9359
9360        if (!resource.getState().isDeleted()) {
9361            throw new CmsVfsException(
9362                Messages.get().container(
9363                    Messages.ERR_UNDELETE_FOR_RESOURCE_DELETED_1,
9364                    dbc.removeSiteRoot(resource.getRootPath())));
9365        }
9366
9367        // set the state to changed
9368        resource.setState(CmsResourceState.STATE_CHANGED);
9369        // perform the changes
9370        updateState(dbc, resource, false);
9371        // log it
9372        log(
9373            dbc,
9374            new CmsLogEntry(
9375                dbc,
9376                resource.getStructureId(),
9377                CmsLogEntryType.RESOURCE_UNDELETED,
9378                new String[] {resource.getRootPath()}),
9379            false);
9380        // clear the cache
9381        m_monitor.clearResourceCache();
9382
9383        // fire change event
9384        Map<String, Object> data = new HashMap<String, Object>(2);
9385        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9386        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_RESOURCE));
9387        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9388    }
9389
9390    /**
9391     * Undos all changes in the resource by restoring the version from the
9392     * online project to the current offline project.<p>
9393     *
9394     * @param dbc the current database context
9395     * @param resource the name of the resource to apply this operation to
9396     * @param mode the undo mode, one of the <code>{@link org.opencms.file.CmsResource.CmsResourceUndoMode}#UNDO_XXX</code> constants
9397     *      please note that the recursive flag is ignored at this level
9398     *
9399     * @throws CmsException if something goes wrong
9400     *
9401     * @see CmsObject#undoChanges(String, CmsResource.CmsResourceUndoMode)
9402     * @see I_CmsResourceType#undoChanges(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode)
9403     */
9404    public void undoChanges(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceUndoMode mode)
9405    throws CmsException {
9406
9407        if (resource.getState().isNew()) {
9408            // undo changes is impossible on a new resource
9409            throw new CmsVfsException(Messages.get().container(Messages.ERR_UNDO_CHANGES_FOR_RESOURCE_NEW_0));
9410        }
9411
9412        // we need this for later use
9413        CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID);
9414        // read the resource from the online project
9415        CmsResource onlineResource = getVfsDriver(
9416            dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getStructureId(), true);
9417
9418        CmsResource onlineResourceByPath = null;
9419        try {
9420            // this is needed to figure out if a moved resource overwrote a deleted one
9421            onlineResourceByPath = getVfsDriver(
9422                dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getRootPath(), true);
9423
9424            // force undo move operation if needed
9425            if (!mode.isUndoMove() && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) {
9426                mode = mode.includeMove();
9427            }
9428        } catch (Exception e) {
9429            // ok
9430        }
9431
9432        boolean moved = !onlineResource.getRootPath().equals(resource.getRootPath());
9433        // undo move operation if required
9434        if (moved && mode.isUndoMove()) {
9435            moveResource(dbc, resource, onlineResource.getRootPath(), true);
9436            if ((onlineResourceByPath != null)
9437                && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) {
9438                // was moved over deleted, so the deleted file has to be undone
9439                undoContentChanges(dbc, onlineProject, null, onlineResourceByPath, CmsResource.STATE_UNCHANGED, true);
9440            }
9441        }
9442        // undo content changes
9443        CmsResourceState newState = CmsResource.STATE_UNCHANGED;
9444        if (moved && !mode.isUndoMove()) {
9445            newState = CmsResource.STATE_CHANGED;
9446        }
9447        undoContentChanges(dbc, onlineProject, resource, onlineResource, newState, moved && mode.isUndoMove());
9448        // because undoContentChanges deletes the offline resource internally, we have
9449        // to write an entry to the log table to prevent the resource from appearing in the
9450        // user's publish list.
9451        log(
9452            dbc,
9453            new CmsLogEntry(
9454                dbc,
9455                resource.getStructureId(),
9456                CmsLogEntryType.RESOURCE_CHANGES_UNDONE,
9457                new String[] {resource.getRootPath()}),
9458            true);
9459
9460    }
9461
9462    /**
9463     * Unlocks all resources in the given project.<p>
9464     *
9465     * @param project the project to unlock the resources in
9466     */
9467    public void unlockProject(CmsProject project) {
9468
9469        // unlock all resources in the project
9470        m_lockManager.removeResourcesInProject(project.getUuid(), false);
9471        m_monitor.clearResourceCache();
9472        m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT, CmsMemoryMonitor.CacheType.PERMISSION);
9473    }
9474
9475    /**
9476     * Unlocks a resource.<p>
9477     *
9478     * @param dbc the current database context
9479     * @param resource the resource to unlock
9480     * @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
9481     * @param removeSystemLock <code>true</code>, if you also want to remove system locks
9482     *
9483     * @throws CmsException if something goes wrong
9484     *
9485     * @see CmsObject#unlockResource(String)
9486     * @see I_CmsResourceType#unlockResource(CmsObject, CmsSecurityManager, CmsResource)
9487     */
9488    public void unlockResource(CmsDbContext dbc, CmsResource resource, boolean force, boolean removeSystemLock)
9489    throws CmsException {
9490
9491        // update the resource cache
9492        m_monitor.clearResourceCache();
9493
9494        // now update lock status
9495        m_lockManager.removeResource(dbc, resource, force, removeSystemLock);
9496
9497        // we must also clear the permission cache
9498        m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION);
9499
9500        // fire resource modification event
9501        Map<String, Object> data = new HashMap<String, Object>(2);
9502        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9503        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(NOTHING_CHANGED));
9504        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9505    }
9506
9507    /**
9508     * Unsubscribes all deleted resources that were deleted before the specified time stamp.<p>
9509     *
9510     * @param dbc the database context
9511     * @param poolName the name of the database pool to use
9512     * @param deletedTo the time stamp to which the resources have been deleted
9513     *
9514     * @throws CmsException if something goes wrong
9515     */
9516    public void unsubscribeAllDeletedResources(CmsDbContext dbc, String poolName, long deletedTo) throws CmsException {
9517
9518        getSubscriptionDriver().unsubscribeAllDeletedResources(dbc, poolName, deletedTo);
9519    }
9520
9521    /**
9522     * Unsubscribes the principal from all resources.<p>
9523     *
9524     * @param dbc the database context
9525     * @param poolName the name of the database pool to use
9526     * @param principal the principal that unsubscribes from all resources
9527     *
9528     * @throws CmsException if something goes wrong
9529     */
9530    public void unsubscribeAllResourcesFor(CmsDbContext dbc, String poolName, CmsPrincipal principal)
9531    throws CmsException {
9532
9533        getSubscriptionDriver().unsubscribeAllResourcesFor(dbc, poolName, principal);
9534
9535    }
9536
9537    /**
9538     * Unsubscribes the principal from the resource.<p>
9539     *
9540     * @param dbc the database context
9541     * @param poolName the name of the database pool to use
9542     * @param principal the principal that unsubscribes from the resource
9543     * @param resource the resource to unsubscribe from
9544     *
9545     * @throws CmsException if something goes wrong
9546     */
9547    public void unsubscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource)
9548    throws CmsException {
9549
9550        getSubscriptionDriver().unsubscribeResourceFor(dbc, poolName, principal, resource);
9551    }
9552
9553    /**
9554     * Unsubscribes all groups and users from the resource.<p>
9555     *
9556     * @param dbc the database context
9557     * @param poolName the name of the database pool to use
9558     * @param resource the resource to unsubscribe all groups and users from
9559     *
9560     * @throws CmsException if something goes wrong
9561     */
9562    public void unsubscribeResourceForAll(CmsDbContext dbc, String poolName, CmsResource resource) throws CmsException {
9563
9564        getSubscriptionDriver().unsubscribeResourceForAll(dbc, poolName, resource);
9565    }
9566
9567    /**
9568     * Update the export points.<p>
9569     *
9570     * All files and folders "inside" an export point are written.<p>
9571     *
9572     * @param dbc the current database context
9573     */
9574    public void updateExportPoints(CmsDbContext dbc) {
9575
9576        try {
9577            // read the export points and return immediately if there are no export points at all
9578            Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>();
9579            exportPoints.addAll(OpenCms.getExportPoints());
9580            exportPoints.addAll(OpenCms.getModuleManager().getExportPoints());
9581            if (exportPoints.size() == 0) {
9582                if (LOG.isWarnEnabled()) {
9583                    LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0));
9584                }
9585                return;
9586            }
9587
9588            // create the driver to write the export points
9589            I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver(
9590                exportPoints);
9591
9592            // the export point hash table contains RFS export paths keyed by their internal VFS paths
9593            Iterator<String> i = exportPointDriver.getExportPointPaths().iterator();
9594            I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
9595            while (i.hasNext()) {
9596                String currentExportPoint = i.next();
9597
9598                // print some report messages
9599                if (LOG.isInfoEnabled()) {
9600                    LOG.info(Messages.get().getBundle().key(Messages.LOG_WRITE_EXPORT_POINT_1, currentExportPoint));
9601                }
9602
9603                try {
9604                    CmsResourceFilter filter = CmsResourceFilter.DEFAULT;
9605                    List<CmsResource> resources = vfsDriver.readResourceTree(
9606                        dbc,
9607                        CmsProject.ONLINE_PROJECT_ID,
9608                        currentExportPoint,
9609                        filter.getType(),
9610                        filter.getState(),
9611                        filter.getModifiedAfter(),
9612                        filter.getModifiedBefore(),
9613                        filter.getReleaseAfter(),
9614                        filter.getReleaseBefore(),
9615                        filter.getExpireAfter(),
9616                        filter.getExpireBefore(),
9617                        CmsDriverManager.READMODE_INCLUDE_TREE
9618                            | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0)
9619                            | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0));
9620
9621                    Iterator<CmsResource> j = resources.iterator();
9622                    while (j.hasNext()) {
9623                        CmsResource currentResource = j.next();
9624
9625                        if (currentResource.isFolder()) {
9626                            // export the folder
9627                            exportPointDriver.createFolder(currentResource.getRootPath(), currentExportPoint);
9628                        } else {
9629                            // try to create the exportpoint folder
9630                            exportPointDriver.createFolder(currentExportPoint, currentExportPoint);
9631                            byte[] onlineContent = vfsDriver.readContent(
9632                                dbc,
9633                                CmsProject.ONLINE_PROJECT_ID,
9634                                currentResource.getResourceId());
9635                            // export the file content online
9636                            exportPointDriver.writeFile(
9637                                currentResource.getRootPath(),
9638                                currentExportPoint,
9639                                onlineContent);
9640                        }
9641                    }
9642                } catch (CmsException e) {
9643                    // there might exist export points without corresponding resources in the VFS
9644                    // -> ignore exceptions which are not "resource not found" exception quiet here
9645                    if (e instanceof CmsVfsResourceNotFoundException) {
9646                        if (LOG.isErrorEnabled()) {
9647                            LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e);
9648                        }
9649                    }
9650                }
9651            }
9652        } catch (Exception e) {
9653            if (LOG.isErrorEnabled()) {
9654                LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e);
9655            }
9656        }
9657    }
9658
9659    /**
9660     * Updates the last login date on the given user to the current time.<p>
9661     *
9662     * @param dbc the current database context
9663     * @param user the user to be updated
9664     *
9665     * @throws CmsException if operation was not successful
9666     */
9667    public void updateLastLoginDate(CmsDbContext dbc, CmsUser user) throws CmsException {
9668
9669        m_monitor.clearUserCache(user);
9670        // set the last login time to the current time
9671        user.setLastlogin(System.currentTimeMillis());
9672        dbc.setAttribute(ATTRIBUTE_LOGIN, user.getName());
9673        getUserDriver(dbc).writeUser(dbc, user);
9674        // update cache
9675        m_monitor.cacheUser(user);
9676
9677        // invalidate all user dependent caches
9678        m_monitor.flushCache(
9679            CmsMemoryMonitor.CacheType.ACL,
9680            CmsMemoryMonitor.CacheType.GROUP,
9681            CmsMemoryMonitor.CacheType.ORG_UNIT,
9682            CmsMemoryMonitor.CacheType.USER_LIST,
9683            CmsMemoryMonitor.CacheType.PERMISSION,
9684            CmsMemoryMonitor.CacheType.RESOURCE_LIST);
9685
9686        // fire user modified event
9687        Map<String, Object> eventData = new HashMap<String, Object>();
9688        eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString());
9689        eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName());
9690        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER);
9691        eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(CmsUser.FLAG_LAST_LOGIN));
9692        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData));
9693    }
9694
9695    /**
9696     * Logs everything that has not been written to DB jet.<p>
9697     *
9698     * @param dbc the current db context
9699     *
9700     * @throws CmsDataAccessException if something goes wrong
9701     */
9702    public void updateLog(CmsDbContext dbc) throws CmsDataAccessException {
9703
9704        synchronized (m_publishListUpdateLock) {
9705
9706            if (m_log.isEmpty()) {
9707                return;
9708            }
9709
9710            List<CmsLogEntry> log = new ArrayList<CmsLogEntry>(m_log);
9711            m_log.clear();
9712            String logTableEnabledStr = (String)OpenCms.getRuntimeProperty(PARAM_LOG_TABLE_ENABLED);
9713            if (Boolean.parseBoolean(logTableEnabledStr)) { // defaults to 'false' if value not set
9714                m_projectDriver.log(dbc, log);
9715            }
9716            A_CmsLogPublishListConverter converter = null;
9717            switch (OpenCms.getPublishManager().getPublishListRemoveMode()) {
9718                case currentUser:
9719                    converter = new CmsLogPublishListConverterCurrentUser();
9720                    break;
9721                case allUsers:
9722                default:
9723                    converter = new CmsLogPublishListConverterAllUsers();
9724                    break;
9725            }
9726            for (CmsLogEntry entry : log) {
9727                converter.add(entry);
9728            }
9729            converter.writeChangesToDatabase(dbc, m_projectDriver);
9730        }
9731    }
9732
9733    /**
9734     * Updates/Creates the given relations for the given resource.<p>
9735     *
9736     * @param dbc the db context
9737     * @param resource the resource to update the relations for
9738     * @param links the links to consider for updating
9739     *
9740     * @throws CmsException if something goes wrong
9741     *
9742     * @see CmsSecurityManager#updateRelationsForResource(CmsRequestContext, CmsResource, List)
9743     */
9744    public void updateRelationsForResource(CmsDbContext dbc, CmsResource resource, List<CmsLink> links)
9745    throws CmsException {
9746
9747        deleteRelationsWithSiblings(dbc, resource);
9748
9749        // build the links again only if needed
9750        if ((links == null) || links.isEmpty()) {
9751            return;
9752        }
9753        // the set of written relations
9754        Set<CmsRelation> writtenRelations = new HashSet<CmsRelation>();
9755
9756        // create new relation information
9757        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
9758        Iterator<CmsLink> itLinks = links.iterator();
9759        while (itLinks.hasNext()) {
9760            CmsLink link = itLinks.next();
9761            if (link.isInternal()) { // only update internal links
9762                if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) {
9763                    // only an anchor
9764                    continue;
9765                }
9766                CmsUUID targetId = link.getStructureId();
9767                String destPath = link.getTarget();
9768
9769                if (targetId != null) {
9770                    // the link target may not be a VFS path even if the link id is a structure id,
9771                    // so if possible, we read the resource for the id and set the relation target to its
9772                    // real root path.
9773                    try {
9774                        CmsResource destRes = readResource(dbc, targetId, CmsResourceFilter.ALL);
9775                        destPath = destRes.getRootPath();
9776                    } catch (CmsVfsResourceNotFoundException e) {
9777                        // ignore
9778                    }
9779                }
9780
9781                CmsRelation originalRelation = new CmsRelation(
9782                    resource.getStructureId(),
9783                    resource.getRootPath(),
9784                    link.getStructureId(),
9785                    destPath,
9786                    link.getType());
9787
9788                // do not write twice the same relation
9789                if (writtenRelations.contains(originalRelation)) {
9790                    continue;
9791                }
9792                writtenRelations.add(originalRelation);
9793
9794                // TODO: it would be good to have the link locale to make the relation just to the right sibling
9795                // create the relations in content for all siblings
9796                Iterator<CmsResource> itSiblings = readSiblings(dbc, resource, CmsResourceFilter.ALL).iterator();
9797                while (itSiblings.hasNext()) {
9798                    CmsResource sibling = itSiblings.next();
9799                    CmsRelation relation = new CmsRelation(
9800                        sibling.getStructureId(),
9801                        sibling.getRootPath(),
9802                        originalRelation.getTargetId(),
9803                        originalRelation.getTargetPath(),
9804                        link.getType());
9805                    vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation);
9806                }
9807            }
9808        }
9809    }
9810
9811    /**
9812     * Returns <code>true</code> if a user is member of the given group.<p>
9813     *
9814     * @param dbc the current database context
9815     * @param username the name of the user to check
9816     * @param groupname the name of the group to check
9817     * @param readRoles if to read roles or groups
9818     *
9819     * @return <code>true</code>, if the user is in the group, <code>false</code> otherwise
9820     *
9821     * @throws CmsException if something goes wrong
9822     */
9823    public boolean userInGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles)
9824    throws CmsException {
9825
9826        List<CmsGroup> groups = getGroupsOfUser(dbc, username, readRoles);
9827        for (int i = 0; i < groups.size(); i++) {
9828            CmsGroup group = groups.get(i);
9829            if (groupname.equals(group.getName()) || groupname.substring(1).equals(group.getName())) {
9830                return true;
9831            }
9832        }
9833        return false;
9834    }
9835
9836    /**
9837     * This method checks if a new password follows the rules for
9838     * new passwords, which are defined by a Class implementing the
9839     * <code>{@link org.opencms.security.I_CmsPasswordHandler}</code>
9840     * interface and configured in the opencms.properties file.<p>
9841     *
9842     * If this method throws no exception the password is valid.<p>
9843     *
9844     * @param password the new password that has to be checked
9845     *
9846     * @throws CmsSecurityException if the password is not valid
9847     */
9848    public void validatePassword(String password) throws CmsSecurityException {
9849
9850        OpenCms.getPasswordHandler().validatePassword(password);
9851    }
9852
9853    /**
9854     * Validates the relations for the given resources.<p>
9855     *
9856     * @param dbc the database context
9857     * @param publishList the resources to validate during publishing
9858     * @param report a report to write the messages to
9859     *
9860     * @return a map with lists of invalid links
9861     *          (<code>{@link org.opencms.relations.CmsRelation}}</code> objects)
9862     *          keyed by root paths
9863     *
9864     * @throws Exception if something goes wrong
9865     */
9866    public Map<String, List<CmsRelation>> validateRelations(
9867        CmsDbContext dbc,
9868        CmsPublishList publishList,
9869        I_CmsReport report)
9870    throws Exception {
9871
9872        return m_htmlLinkValidator.validateResources(dbc, publishList, report);
9873    }
9874
9875    /**
9876     * Writes an access control entries to a given resource.<p>
9877     *
9878     * @param dbc the current database context
9879     * @param resource the resource
9880     * @param ace the entry to write
9881     *
9882     * @throws CmsException if something goes wrong
9883     */
9884    public void writeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsAccessControlEntry ace)
9885    throws CmsException {
9886
9887        // write the new ace
9888        getUserDriver(dbc).writeAccessControlEntry(dbc, dbc.currentProject(), ace);
9889
9890        // log it
9891        log(
9892            dbc,
9893            new CmsLogEntry(
9894                dbc,
9895                resource.getStructureId(),
9896                CmsLogEntryType.RESOURCE_PERMISSIONS,
9897                new String[] {resource.getRootPath()}),
9898            false);
9899
9900        // update the "last modified" information
9901        setDateLastModified(dbc, resource, resource.getDateLastModified());
9902
9903        // clear the cache
9904        m_monitor.clearAccessControlListCache();
9905
9906        // fire a resource modification event
9907        Map<String, Object> data = new HashMap<String, Object>(2);
9908        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
9909        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_ACCESSCONTROL));
9910        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
9911    }
9912
9913    /**
9914     * Writes all export points into the file system for the publish task
9915     * specified by trhe given publish history ID.<p>
9916     *
9917     * @param dbc the current database context
9918     * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
9919     * @param publishHistoryId ID to identify the publish task in the publish history
9920     */
9921    public void writeExportPoints(CmsDbContext dbc, I_CmsReport report, CmsUUID publishHistoryId) {
9922
9923        boolean printReportHeaders = false;
9924        List<CmsPublishedResource> publishedResources = null;
9925        try {
9926            // read the "published resources" for the specified publish history ID
9927            publishedResources = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId);
9928        } catch (CmsException e) {
9929            if (LOG.isErrorEnabled()) {
9930                LOG.error(
9931                    Messages.get().getBundle().key(Messages.ERR_READ_PUBLISHED_RESOURCES_FOR_ID_1, publishHistoryId),
9932                    e);
9933            }
9934        }
9935        if ((publishedResources == null) || publishedResources.isEmpty()) {
9936            if (LOG.isWarnEnabled()) {
9937                LOG.warn(Messages.get().getBundle().key(Messages.LOG_EMPTY_PUBLISH_HISTORY_1, publishHistoryId));
9938            }
9939            return;
9940        }
9941
9942        // read the export points and return immediately if there are no export points at all
9943        Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>();
9944        exportPoints.addAll(OpenCms.getExportPoints());
9945        exportPoints.addAll(OpenCms.getModuleManager().getExportPoints());
9946        if (exportPoints.size() == 0) {
9947            if (LOG.isWarnEnabled()) {
9948                LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0));
9949            }
9950            return;
9951        }
9952
9953        // create the driver to write the export points
9954        I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver(
9955            exportPoints);
9956
9957        // the report may be null if the export point write was started by an event
9958        if (report == null) {
9959            if (dbc.getRequestContext() != null) {
9960                report = new CmsLogReport(dbc.getRequestContext().getLocale(), getClass());
9961            } else {
9962                report = new CmsLogReport(CmsLocaleManager.getDefaultLocale(), getClass());
9963            }
9964        }
9965
9966        // iterate over all published resources to export them
9967        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
9968        Iterator<CmsPublishedResource> i = publishedResources.iterator();
9969        while (i.hasNext()) {
9970            CmsPublishedResource currentPublishedResource = i.next();
9971            String currentExportPoint = exportPointDriver.getExportPoint(currentPublishedResource.getRootPath());
9972
9973            if (currentExportPoint != null) {
9974                if (!printReportHeaders) {
9975                    report.println(
9976                        Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_BEGIN_0),
9977                        I_CmsReport.FORMAT_HEADLINE);
9978                    printReportHeaders = true;
9979                }
9980
9981                // print report message
9982                if (currentPublishedResource.getState().isDeleted()) {
9983                    report.print(
9984                        Messages.get().container(Messages.RPT_EXPORT_POINTS_DELETE_0),
9985                        I_CmsReport.FORMAT_NOTE);
9986                } else {
9987                    report.print(Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_0), I_CmsReport.FORMAT_NOTE);
9988                }
9989                report.print(
9990                    org.opencms.report.Messages.get().container(
9991                        org.opencms.report.Messages.RPT_ARGUMENT_1,
9992                        currentPublishedResource.getRootPath()));
9993                report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
9994
9995                if (currentPublishedResource.isFolder()) {
9996                    // export the folder
9997                    if (currentPublishedResource.getState().isDeleted()) {
9998                        exportPointDriver.deleteResource(currentPublishedResource.getRootPath(), currentExportPoint);
9999                    } else {
10000                        exportPointDriver.createFolder(currentPublishedResource.getRootPath(), currentExportPoint);
10001                    }
10002                    report.println(
10003                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
10004                        I_CmsReport.FORMAT_OK);
10005                } else {
10006                    // export the file
10007                    try {
10008                        if (currentPublishedResource.getState().isDeleted()) {
10009                            exportPointDriver.deleteResource(
10010                                currentPublishedResource.getRootPath(),
10011                                currentExportPoint);
10012                        } else {
10013                            // read the file content online
10014                            byte[] onlineContent = vfsDriver.readContent(
10015                                dbc,
10016                                CmsProject.ONLINE_PROJECT_ID,
10017                                currentPublishedResource.getResourceId());
10018                            exportPointDriver.writeFile(
10019                                currentPublishedResource.getRootPath(),
10020                                currentExportPoint,
10021                                onlineContent);
10022                        }
10023                        report.println(
10024                            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
10025                            I_CmsReport.FORMAT_OK);
10026                    } catch (CmsException e) {
10027                        if (LOG.isErrorEnabled()) {
10028                            LOG.error(
10029                                Messages.get().getBundle().key(
10030                                    Messages.LOG_WRITE_EXPORT_POINT_ERROR_1,
10031                                    currentPublishedResource.getRootPath()),
10032                                e);
10033                        }
10034                        report.println(
10035                            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0),
10036                            I_CmsReport.FORMAT_ERROR);
10037                    }
10038                }
10039            }
10040        }
10041        if (printReportHeaders) {
10042            report.println(
10043                Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_END_0),
10044                I_CmsReport.FORMAT_HEADLINE);
10045        }
10046    }
10047
10048    /**
10049     * Writes a resource to the OpenCms VFS, including it's content.<p>
10050     *
10051     * Applies only to resources of type <code>{@link CmsFile}</code>
10052     * i.e. resources that have a binary content attached.<p>
10053     *
10054     * Certain resource types might apply content validation or transformation rules
10055     * before the resource is actually written to the VFS. The returned result
10056     * might therefore be a modified version from the provided original.<p>
10057     *
10058     * @param dbc the current database context
10059     * @param resource the resource to apply this operation to
10060     *
10061     * @return the written resource (may have been modified)
10062     *
10063     * @throws CmsException if something goes wrong
10064     *
10065     * @see CmsObject#writeFile(CmsFile)
10066     * @see I_CmsResourceType#writeFile(CmsObject, CmsSecurityManager, CmsFile)
10067     */
10068    public CmsFile writeFile(CmsDbContext dbc, CmsFile resource) throws CmsException {
10069
10070        resource.setUserLastModified(dbc.currentUser().getId());
10071        resource.setContents(resource.getContents()); // to be sure the content date is updated
10072
10073        getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), resource, UPDATE_RESOURCE_STATE);
10074
10075        byte[] contents = resource.getContents();
10076        getVfsDriver(dbc).writeContent(dbc, resource.getResourceId(), contents);
10077        // log it
10078        log(
10079            dbc,
10080            new CmsLogEntry(
10081                dbc,
10082                resource.getStructureId(),
10083                CmsLogEntryType.RESOURCE_CONTENT_MODIFIED,
10084                new String[] {resource.getRootPath()}),
10085            false);
10086
10087        // read the file back from db
10088        resource = new CmsFile(readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL));
10089        resource.setContents(contents);
10090
10091        deleteRelationsWithSiblings(dbc, resource);
10092
10093        // update the cache
10094        m_monitor.clearResourceCache();
10095
10096        Map<String, Object> data = new HashMap<String, Object>(2);
10097        data.put(I_CmsEventListener.KEY_RESOURCE, resource);
10098        data.put(I_CmsEventListener.KEY_CHANGE, new Integer(CHANGED_CONTENT));
10099        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data));
10100
10101        return resource;
10102    }
10103
10104    /**
10105     * Writes an already existing group.<p>
10106     *
10107     * The group id has to be a valid OpenCms group id.<br>
10108     *
10109     * The group with the given id will be completely overridden
10110     * by the given data.<p>
10111     *
10112     * @param dbc the current database context
10113     * @param group the group that should be written
10114     *
10115     * @throws CmsException if operation was not successful
10116     */
10117    public void writeGroup(CmsDbContext dbc, CmsGroup group) throws CmsException {
10118
10119        CmsGroup oldGroup = readGroup(dbc, group.getName());
10120        m_monitor.uncacheGroup(oldGroup);
10121        getUserDriver(dbc).writeGroup(dbc, group);
10122        m_monitor.cacheGroup(group);
10123
10124        if (!dbc.getProjectId().isNullUUID()) {
10125            // group modified event is not needed
10126            return;
10127        }
10128        // fire group modified event
10129        Map<String, Object> eventData = new HashMap<String, Object>();
10130        eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString());
10131        eventData.put(I_CmsEventListener.KEY_GROUP_NAME, oldGroup.getName());
10132        eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_WRITE);
10133        OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData));
10134    }
10135
10136    /**
10137     * Creates an historical entry of the current project.<p>
10138     *
10139     * @param dbc the current database context
10140     * @param publishTag the version
10141     * @param publishDate the date of publishing
10142     *
10143     * @throws CmsDataAccessException if operation was not successful
10144     */
10145    public void writeHistoryProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException {
10146
10147        getHistoryDriver(dbc).writeProject(dbc, publishTag, publishDate);
10148    }
10149
10150    /**
10151     * Writes the locks that are currently stored in-memory to the database to allow restoring them
10152     * in future server startups.<p>
10153     *
10154     * This overwrites the locks previously stored in the underlying database table.<p>
10155     *
10156     * @param dbc the current database context
10157     *
10158     * @throws CmsException if something goes wrong
10159     */
10160    public void writeLocks(CmsDbContext dbc) throws CmsException {
10161
10162        m_lockManager.writeLocks(dbc);
10163    }
10164
10165    /**
10166     * Writes an already existing organizational unit.<p>
10167     *
10168     * The organizational unit id has to be a valid OpenCms organizational unit id.<br>
10169     *
10170     * The organizational unit with the given id will be completely overridden
10171     * by the given data.<p>
10172     *
10173     * @param dbc the current db context
10174     * @param organizationalUnit the organizational unit that should be written
10175     *
10176     * @throws CmsException if operation was not successful
10177     *
10178     * @see org.opencms.security.CmsOrgUnitManager#writeOrganizationalUnit(CmsObject, CmsOrganizationalUnit)
10179     */
10180    public void writeOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit)
10181    throws CmsException {
10182
10183        m_monitor.uncacheOrgUnit(organizationalUnit);
10184        getUserDriver(dbc).writeOrganizationalUnit(dbc, organizationalUnit);
10185
10186        // create a publish list for the 'virtual' publish event
10187        CmsResource ouRes = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT);
10188        CmsPublishList pl = new CmsPublishList(ouRes, false);
10189        pl.add(ouRes, false);
10190
10191        getProjectDriver(dbc).writePublishHistory(
10192            dbc,
10193            pl.getPublishHistoryId(),
10194            new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW));
10195
10196        // fire the 'virtual' publish event
10197        Map<String, Object> eventData = new HashMap<String, Object>();
10198        eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString());
10199        eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid());
10200        eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc);
10201        CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData);
10202        OpenCms.fireCmsEvent(afterPublishEvent);
10203
10204        m_monitor.cacheOrgUnit(organizationalUnit);
10205    }
10206
10207    /**
10208     * Writes an already existing project.<p>
10209     *
10210     * The project id has to be a valid OpenCms project id.<br>
10211     *
10212     * The project with the given id will be completely overridden
10213     * by the given data.<p>
10214     *
10215     * @param dbc the current database context
10216     * @param project the project that should be written
10217     *
10218     * @throws CmsException if operation was not successful
10219     */
10220    public void writeProject(CmsDbContext dbc, CmsProject project) throws CmsException {
10221
10222        m_monitor.uncacheProject(project);
10223        getProjectDriver(dbc).writeProject(dbc, project);
10224        m_monitor.cacheProject(project);
10225    }
10226
10227    /**
10228     * Writes a new project into the PROJECT_LASTMODIFIED field of a resource record.<p>
10229     *
10230     * @param dbc the current database context
10231     * @param resource the resource which should be modified
10232     * @param projectId the project id to write
10233     *
10234     * @throws CmsDataAccessException if the database access fails
10235     */
10236    public void writeProjectLastModified(CmsDbContext dbc, CmsResource resource, CmsUUID projectId)
10237    throws CmsDataAccessException {
10238
10239        I_CmsVfsDriver vfsDriver = getVfsDriver(dbc);
10240        vfsDriver.writeLastModifiedProjectId(dbc, dbc.currentProject(), projectId, resource);
10241    }
10242
10243    /**
10244     * Writes a property for a specified resource.<p>
10245     *
10246     * @param dbc the current database context
10247     * @param resource the resource to write the property for
10248     * @param property the property to write
10249     *
10250     * @throws CmsException if something goes wrong
10251     *
10252     * @see CmsObject#writePropertyObject(String, CmsProperty)
10253     * @see I_CmsResourceType#writePropertyObject(CmsObject, CmsSecurityManager, CmsResource, CmsProperty)
10254     */
10255    public void writePropertyObject(CmsDbContext dbc, CmsResource resource, CmsProperty property) throws CmsException {
10256
10257        try {
10258            if (property == CmsProperty.getNullProperty()) {
10259                // skip empty or null properties
10260                return;
10261            }
10262
10263            // test if and what state should be updated
10264            // 0: none, 1: structure, 2: resource
10265            int updateState = getUpdateState(dbc, resource, Collections.