001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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.contenteditor.CmsAccessRestrictionInfo;
031import org.opencms.ade.publish.CmsTooManyPublishResourcesException;
032import org.opencms.configuration.CmsConfigurationManager;
033import org.opencms.configuration.CmsSystemConfiguration;
034import org.opencms.db.generic.CmsPublishHistoryCleanupFilter;
035import org.opencms.db.log.CmsLogEntry;
036import org.opencms.db.log.CmsLogFilter;
037import org.opencms.db.storage.I_CmsStorageDelivery;
038import org.opencms.db.urlname.CmsUrlNameMappingEntry;
039import org.opencms.db.urlname.CmsUrlNameMappingFilter;
040import org.opencms.file.CmsDataAccessException;
041import org.opencms.file.CmsFile;
042import org.opencms.file.CmsFolder;
043import org.opencms.file.CmsGroup;
044import org.opencms.file.CmsObject;
045import org.opencms.file.CmsProject;
046import org.opencms.file.CmsProperty;
047import org.opencms.file.CmsPropertyDefinition;
048import org.opencms.file.CmsRequestContext;
049import org.opencms.file.CmsResource;
050import org.opencms.file.CmsResourceFilter;
051import org.opencms.file.CmsStoredContentInfo;
052import org.opencms.file.CmsUser;
053import org.opencms.file.CmsUserSearchParameters;
054import org.opencms.file.CmsVfsException;
055import org.opencms.file.CmsVfsResourceAlreadyExistsException;
056import org.opencms.file.CmsVfsResourceNotFoundException;
057import org.opencms.file.I_CmsFileContentStreamHandler;
058import org.opencms.file.history.CmsHistoryPrincipal;
059import org.opencms.file.history.CmsHistoryProject;
060import org.opencms.file.history.I_CmsHistoryResource;
061import org.opencms.file.quota.CmsFolderSizeEntry;
062import org.opencms.file.quota.CmsFolderSizeOptions;
063import org.opencms.file.types.CmsResourceTypeJsp;
064import org.opencms.gwt.shared.alias.CmsAliasImportResult;
065import org.opencms.gwt.shared.alias.CmsAliasMode;
066import org.opencms.i18n.CmsMessageContainer;
067import org.opencms.lock.CmsLock;
068import org.opencms.lock.CmsLockException;
069import org.opencms.lock.CmsLockFilter;
070import org.opencms.lock.CmsLockManager;
071import org.opencms.lock.CmsLockType;
072import org.opencms.main.CmsEvent;
073import org.opencms.main.CmsException;
074import org.opencms.main.CmsIllegalArgumentException;
075import org.opencms.main.CmsInitException;
076import org.opencms.main.CmsLog;
077import org.opencms.main.CmsMultiException;
078import org.opencms.main.I_CmsEventListener;
079import org.opencms.main.OpenCms;
080import org.opencms.publish.CmsPublishEngine;
081import org.opencms.relations.CmsLink;
082import org.opencms.relations.CmsRelation;
083import org.opencms.relations.CmsRelationFilter;
084import org.opencms.relations.CmsRelationType;
085import org.opencms.report.I_CmsReport;
086import org.opencms.security.CmsAccessControlEntry;
087import org.opencms.security.CmsAccessControlList;
088import org.opencms.security.CmsDefaultPermissionHandler;
089import org.opencms.security.CmsOrganizationalUnit;
090import org.opencms.security.CmsPermissionSet;
091import org.opencms.security.CmsPermissionSetCustom;
092import org.opencms.security.CmsPermissionViolationException;
093import org.opencms.security.CmsPrincipal;
094import org.opencms.security.CmsRole;
095import org.opencms.security.CmsRoleViolationException;
096import org.opencms.security.CmsSecurityException;
097import org.opencms.security.I_CmsPermissionHandler;
098import org.opencms.security.I_CmsPermissionHandler.LockCheck;
099import org.opencms.security.I_CmsPrincipal;
100import org.opencms.security.twofactor.CmsSecondFactorInfo;
101import org.opencms.util.CmsFileUtil;
102import org.opencms.util.CmsStringUtil;
103import org.opencms.util.CmsUUID;
104
105import java.io.OutputStream;
106import java.sql.Connection;
107import java.sql.SQLException;
108import java.util.ArrayList;
109import java.util.Collection;
110import java.util.Collections;
111import java.util.Date;
112import java.util.HashMap;
113import java.util.HashSet;
114import java.util.Iterator;
115import java.util.List;
116import java.util.Locale;
117import java.util.Map;
118import java.util.Set;
119import java.util.function.Predicate;
120import java.util.stream.Collectors;
121
122import org.apache.commons.logging.Log;
123
124/**
125 * The OpenCms security manager.<p>
126 *
127 * The security manager checks the permissions required for a user action invoke by the Cms object. If permissions
128 * are granted, the security manager invokes a method on the OpenCms driver manager to access the database.<p>
129 *
130 * @since 6.0.0
131 */
132public final class CmsSecurityManager {
133
134    /**
135     * Exception which indicates the user tried to call setRestricted while not being a member of the corresponding group.
136     */
137    private static class RestrictionGroupMembershipException extends Exception {
138
139        /** Serial version id. */
140        private static final long serialVersionUID = 1L;
141
142        /**
143         * Creates a new instance.
144         */
145        public RestrictionGroupMembershipException() {
146
147            super();
148
149        }
150
151    }
152
153    /**
154     * Exception which indicates the user tried to call setRestricted on a folder.
155     */
156    private static class RestrictionNotSupportedForFoldersException extends Exception {
157
158        /** Serial version id. */
159        private static final long serialVersionUID = 1L;
160
161        /**
162         * Creates a new instance.
163         */
164        public RestrictionNotSupportedForFoldersException() {
165
166            super();
167
168        }
169
170    }
171
172    /** The log object for this class. */
173    private static final Log LOG = CmsLog.getLog(CmsSecurityManager.class);
174
175    /** The factory to create runtime info objects. */
176    protected I_CmsDbContextFactory m_dbContextFactory;
177
178    /** The initialized OpenCms driver manager to access the database. */
179    protected CmsDriverManager m_driverManager;
180
181    /** The lock manager. */
182    private CmsLockManager m_lockManager;
183
184    /** Permission handler implementation. */
185    private I_CmsPermissionHandler m_permissionHandler;
186
187    /**
188     * Default constructor.<p>
189     */
190    private CmsSecurityManager() {
191
192        // intentionally left blank
193    }
194
195    /**
196     * Creates a new instance of the OpenCms security manager.<p>
197     *
198     * @param configurationManager the configuration manager
199     * @param runtimeInfoFactory the initialized OpenCms runtime info factory
200     * @param publishEngine the publish engine
201     *
202     * @return a new instance of the OpenCms security manager
203     *
204     * @throws CmsInitException if the security manager could not be initialized
205     */
206    public static CmsSecurityManager newInstance(
207        CmsConfigurationManager configurationManager,
208        I_CmsDbContextFactory runtimeInfoFactory,
209        CmsPublishEngine publishEngine)
210    throws CmsInitException {
211
212        if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) {
213            // OpenCms is already initialized
214            throw new CmsInitException(
215                org.opencms.main.Messages.get().container(org.opencms.main.Messages.ERR_ALREADY_INITIALIZED_0));
216        }
217
218        CmsSecurityManager securityManager = new CmsSecurityManager();
219        securityManager.init(configurationManager, runtimeInfoFactory, publishEngine);
220
221        return securityManager;
222    }
223
224    /**
225     * Adds an alias.<p>
226     *
227     * @param context the current request context
228     * @param alias the alias to add
229     * @throws CmsException if something goes wrong
230     */
231    public void addAlias(CmsRequestContext context, CmsAlias alias) throws CmsException {
232
233        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
234        try {
235            m_driverManager.addAlias(dbc, context.getCurrentProject(), alias);
236        } catch (Exception e) {
237            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
238        } finally {
239            dbc.clear();
240        }
241    }
242
243    /**
244     * Adds a new relation to a given resource.<p>
245     *
246     * @param context the request context
247     * @param resource the resource to add the relation to
248     * @param target the target of the relation
249     * @param type the type of the relation
250     * @param importCase if importing relations
251     *
252     * @throws CmsException if something goes wrong
253     *
254     * @see #deleteRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter)
255     * @see CmsObject#addRelationToResource(String, String, String)
256     */
257    public void addRelationToResource(
258        CmsRequestContext context,
259        CmsResource resource,
260        CmsResource target,
261        CmsRelationType type,
262        boolean importCase)
263    throws CmsException {
264
265        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
266
267            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
268            try {
269                checkOfflineProject(dbc);
270                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
271                m_driverManager.addRelationToResource(dbc, resource, target, type, importCase);
272                modContext.add(resource);
273            } catch (Exception e) {
274                dbc.report(
275                    null,
276                    Messages.get().container(
277                        Messages.ERR_ADD_RELATION_TO_RESOURCE_3,
278                        context.getSitePath(resource),
279                        context.getSitePath(target),
280                        type),
281                    e);
282
283            } finally {
284                dbc.clear();
285            }
286        }
287    }
288
289    /**
290     * Adds a resource to the given organizational unit.<p>
291     *
292     * @param context the current request context
293     * @param orgUnit the organizational unit to add the resource to
294     * @param resource the resource that is to be added to the organizational unit
295     *
296     * @throws CmsException if something goes wrong
297     *
298     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
299     * @see org.opencms.security.CmsOrgUnitManager#removeResourceFromOrgUnit(CmsObject, String, String)
300     */
301    public void addResourceToOrgUnit(CmsRequestContext context, CmsOrganizationalUnit orgUnit, CmsResource resource)
302    throws CmsException {
303
304        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
305        try {
306            checkOfflineProject(dbc);
307            checkRole(dbc, CmsRole.ADMINISTRATOR.forOrgUnit(orgUnit.getName()));
308            m_driverManager.addResourceToOrgUnit(dbc, orgUnit, resource);
309        } catch (Exception e) {
310            dbc.report(
311                null,
312                Messages.get().container(
313                    Messages.ERR_ADD_RESOURCE_TO_ORGUNIT_2,
314                    orgUnit.getName(),
315                    dbc.removeSiteRoot(resource.getRootPath())),
316                e);
317        } finally {
318            dbc.clear();
319        }
320    }
321
322    /**
323     * Adds a user to a group.<p>
324     *
325     * @param context the current request context
326     * @param username the name of the user that is to be added to the group
327     * @param groupname the name of the group
328     * @param readRoles if reading roles or groups
329     *
330     * @throws CmsException if operation was not successful
331     */
332    public void addUserToGroup(CmsRequestContext context, String username, String groupname, boolean readRoles)
333    throws CmsException {
334
335        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
336        try {
337            CmsRole role = CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(username));
338            checkRoleForUserModification(dbc, username, role);
339            m_driverManager.addUserToGroup(
340                dbc,
341                CmsOrganizationalUnit.removeLeadingSeparator(username),
342                CmsOrganizationalUnit.removeLeadingSeparator(groupname),
343                readRoles);
344        } catch (Exception e) {
345            dbc.report(null, Messages.get().container(Messages.ERR_ADD_USER_GROUP_FAILED_2, username, groupname), e);
346        } finally {
347            dbc.clear();
348        }
349    }
350
351    /**
352     * Changes the lock of a resource to the current user, that is "steals" the lock from another user.<p>
353     *
354     * @param context the current request context
355     * @param resource the resource to change the lock for
356     *
357     * @throws CmsException if something goes wrong
358     *
359     * @see org.opencms.file.types.I_CmsResourceType#changeLock(CmsObject, CmsSecurityManager, CmsResource)
360     */
361    public void changeLock(CmsRequestContext context, CmsResource resource) throws CmsException {
362
363        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
364        checkOfflineProject(dbc);
365        try {
366            m_driverManager.changeLock(dbc, resource, CmsLockType.EXCLUSIVE);
367        } catch (Exception e) {
368            dbc.report(
369                null,
370                Messages.get().container(
371                    Messages.ERR_CHANGE_LOCK_OF_RESOURCE_2,
372                    context.getSitePath(resource),
373                    " - " + e.getMessage()),
374                e);
375        } finally {
376            dbc.clear();
377        }
378    }
379
380    /**
381     * Returns a list with all sub resources of a given folder that have set the given property,
382     * matching the current property's value with the given old value and replacing it by a given new value.<p>
383     *
384     * @param context the current request context
385     * @param resource the resource on which property definition values are changed
386     * @param propertyDefinition the name of the property definition to change the value
387     * @param oldValue the old value of the property definition
388     * @param newValue the new value of the property definition
389     * @param recursive if true, change recursively all property values on sub-resources (only for folders)
390     *
391     * @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed
392     *
393     * @throws CmsVfsException for now only when the search for the old value fails
394     * @throws CmsException if operation was not successful
395     */
396    public synchronized List<CmsResource> changeResourcesInFolderWithProperty(
397        CmsRequestContext context,
398        CmsResource resource,
399        String propertyDefinition,
400        String oldValue,
401        String newValue,
402        boolean recursive)
403    throws CmsException, CmsVfsException {
404
405        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
406
407            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
408            List<CmsResource> result = null;
409            try {
410                result = m_driverManager.changeResourcesInFolderWithProperty(
411                    dbc,
412                    resource,
413                    propertyDefinition,
414                    oldValue,
415                    newValue,
416                    recursive);
417                modContext.add(resource);
418                for (CmsResource changedResource : result) {
419                    modContext.add(changedResource);
420                }
421            } catch (Exception e) {
422                dbc.report(
423                    null,
424                    Messages.get().container(
425                        Messages.ERR_CHANGE_RESOURCES_IN_FOLDER_WITH_PROP_4,
426                        new Object[] {propertyDefinition, oldValue, newValue, context.getSitePath(resource)}),
427                    e);
428            } finally {
429                dbc.clear();
430            }
431            return result;
432        }
433    }
434
435    /**
436     * Checks user name / password and other things which would prevent the user from logging in, but does not check the second factor for 2FA.
437     *
438     * <p>Throws an exception like the normal login method if these checks fail. If it succeeds, nothing actually happens.
439     *
440     * @param context the request context
441     * @param username the user name
442     * @param password the password
443     * @param remoteAddress the remote address
444     * @throws CmsException if the login check fails
445     */
446    public void checkLogin(CmsRequestContext context, String username, String password, String remoteAddress)
447    throws CmsException {
448
449        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
450        CmsUser result = null;
451        try {
452            result = m_driverManager.loginUser(
453                dbc,
454                CmsOrganizationalUnit.removeLeadingSeparator(username),
455                password,
456                null,
457                remoteAddress,
458                CmsDriverManager.LoginUserMode.checkOnly);
459        } finally {
460            dbc.clear();
461        }
462    }
463
464    /**
465     * Checks if the current user has management access to the given project.<p>
466     *
467     * @param dbc the current database context
468     * @param project the project to check
469     *
470     * @throws CmsRoleViolationException if the user does not have the required role permissions
471     */
472    public void checkManagerOfProjectRole(CmsDbContext dbc, CmsProject project) throws CmsRoleViolationException {
473
474        boolean hasRole = false;
475        try {
476            if (hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) {
477                return;
478            }
479            hasRole = m_driverManager.getAllManageableProjects(
480                dbc,
481                m_driverManager.readOrganizationalUnit(dbc, project.getOuFqn()),
482                false).contains(project);
483        } catch (CmsException e) {
484            // should never happen
485            if (LOG.isErrorEnabled()) {
486                LOG.error(e.getLocalizedMessage(), e);
487            }
488        }
489        if (!hasRole) {
490            throw new CmsRoleViolationException(
491                org.opencms.security.Messages.get().container(
492                    org.opencms.security.Messages.ERR_NOT_MANAGER_OF_PROJECT_2,
493                    dbc.currentUser().getName(),
494                    dbc.currentProject().getName()));
495        }
496    }
497
498    /**
499     * Checks if the project in the given database context is not the "Online" project,
500     * and throws an Exception if this is the case.<p>
501     *
502     * This is used to ensure a user is in an "Offline" project
503     * before write access to VFS resources is granted.<p>
504     *
505     * @param dbc the current OpenCms users database context
506     *
507     * @throws CmsVfsException if the project in the given database context is the "Online" project
508     */
509    public void checkOfflineProject(CmsDbContext dbc) throws CmsVfsException {
510
511        if (dbc.currentProject().isOnlineProject()) {
512            throw new CmsVfsException(
513                org.opencms.file.Messages.get().container(
514                    org.opencms.file.Messages.ERR_NOT_ALLOWED_IN_ONLINE_PROJECT_0));
515        }
516    }
517
518    /**
519     * Performs a blocking permission check on a resource.<p>
520     *
521     * If the required permissions are not satisfied by the permissions the user has on the resource,
522     * an exception is thrown.<p>
523     *
524     * @param context the current request context
525     * @param resource the resource on which permissions are required
526     * @param requiredPermissions the set of permissions required to access the resource
527     * @param checkLock if true, the lock status of the resource is also checked
528     * @param filter the filter for the resource
529     *
530     * @throws CmsException in case of any i/o error
531     * @throws CmsSecurityException if the required permissions are not satisfied
532     *
533     * @see #checkPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, I_CmsPermissionHandler.CmsPermissionCheckResult)
534     */
535    public void checkPermissions(
536        CmsRequestContext context,
537        CmsResource resource,
538        CmsPermissionSet requiredPermissions,
539        boolean checkLock,
540        CmsResourceFilter filter)
541    throws CmsException, CmsSecurityException {
542
543        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
544        try {
545            // check the access permissions
546            checkPermissions(dbc, resource, requiredPermissions, checkLock, filter);
547        } finally {
548            dbc.clear();
549        }
550    }
551
552    /**
553     * Checks if the current user has the permissions to publish the given publish list
554     * (which contains the information about the resources / project to publish).<p>
555     *
556     * @param dbc the current OpenCms users database context
557     * @param publishList the publish list to check (contains the information about the resources / project to publish)
558     *
559     * @throws CmsException if the user does not have the required permissions because of project lock state
560     * @throws CmsMultiException if issues occur like a direct publish is attempted on a resource
561     *         whose parent folder is new or deleted in the offline project,
562     *         or if the current user has no management access to the current project
563     */
564    public void checkPublishPermissions(CmsDbContext dbc, CmsPublishList publishList)
565    throws CmsException, CmsMultiException {
566
567        // is the current project an "offline" project?
568        checkOfflineProject(dbc);
569
570        // check if this is a "direct publish" attempt
571        if (!publishList.isDirectPublish()) {
572            // check if the user is a manager of the current project, in this case he has publish permissions
573            checkManagerOfProjectRole(dbc, dbc.getRequestContext().getCurrentProject());
574        } else {
575            // direct publish, create exception containers
576            CmsMultiException resourceIssues = new CmsMultiException();
577            CmsMultiException permissionIssues = new CmsMultiException();
578            // iterate all resources in the direct publish list
579            Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator();
580            List<String> parentFolders = new ArrayList<String>();
581            while (it.hasNext()) {
582                CmsResource res = it.next();
583                // the parent folder must not be new or deleted
584                String parentFolder = CmsResource.getParentFolder(res.getRootPath());
585                if ((parentFolder != null) && !parentFolders.contains(parentFolder)) {
586                    // check each parent folder only once
587                    CmsResource parent = readResource(dbc, parentFolder, CmsResourceFilter.ALL);
588                    if (parent.getState().isDeleted()) {
589                        if (!(publishList.isUserPublishList() && publishList.getDeletedFolderList().contains(parent))) {
590                            // parent folder is deleted - direct publish not allowed
591                            resourceIssues.addException(
592                                new CmsVfsException(
593                                    Messages.get().container(
594                                        Messages.ERR_DIRECT_PUBLISH_PARENT_DELETED_2,
595                                        dbc.getRequestContext().removeSiteRoot(res.getRootPath()),
596                                        parentFolder)));
597                        }
598                    }
599                    if (parent.getState().isNew()) {
600                        if (!(publishList.isUserPublishList() && publishList.getFolderList().contains(parent))) {
601                            // parent folder is new - direct publish not allowed
602                            resourceIssues.addException(
603                                new CmsVfsException(
604                                    Messages.get().container(
605                                        Messages.ERR_DIRECT_PUBLISH_PARENT_NEW_2,
606                                        dbc.removeSiteRoot(res.getRootPath()),
607                                        parentFolder)));
608                        }
609                    }
610                    // add checked parent folder to prevent duplicate checks
611                    parentFolders.add(parentFolder);
612                }
613                // check if the user has the explicit permission to direct publish the selected resource
614                if (I_CmsPermissionHandler.PERM_ALLOWED != hasPermissions(
615                    dbc.getRequestContext(),
616                    res,
617                    CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
618                    true,
619                    CmsResourceFilter.ALL)) {
620
621                    // the user has no "direct publish" permissions on the resource
622                    permissionIssues.addException(
623                        new CmsSecurityException(
624                            Messages.get().container(
625                                Messages.ERR_DIRECT_PUBLISH_NO_PERMISSIONS_1,
626                                dbc.removeSiteRoot(res.getRootPath()))));
627                }
628            }
629            if (resourceIssues.hasExceptions() || permissionIssues.hasExceptions()) {
630                // there are issues, permission check has failed
631                resourceIssues.addExceptions(permissionIssues.getExceptions());
632                throw resourceIssues;
633            }
634        }
635        // no issues have been found , permissions are granted
636    }
637
638    /**
639     * Checks if the user of the current database context has permissions to impersonate the given role
640     * in the given organizational unit.<p>
641     *
642     * If the organizational unit is <code>null</code>, this method will check if the
643     * given user has the given role for at least one organizational unit.<p>
644     *
645     * @param dbc the current OpenCms users database context
646     * @param role the role to check
647     *
648     * @throws CmsRoleViolationException if the user does not have the required role permissions
649     *
650     * @see org.opencms.security.CmsRoleManager#checkRole(CmsObject, CmsRole)
651     */
652    public void checkRole(CmsDbContext dbc, CmsRole role) throws CmsRoleViolationException {
653
654        if (!hasRole(dbc, dbc.currentUser(), role)) {
655            if (role.getOuFqn() != null) {
656                throw role.createRoleViolationExceptionForOrgUnit(dbc.getRequestContext(), role.getOuFqn());
657            } else {
658                throw role.createRoleViolationException(dbc.getRequestContext());
659            }
660        }
661    }
662
663    /**
664     * Checks if the user of the current context has permissions to impersonate the given role.<p>
665     *
666     * If the organizational unit is <code>null</code>, this method will check if the
667     * given user has the given role for at least one organizational unit.<p>
668     *
669     * @param context the current request context
670     * @param role the role to check
671     *
672     * @throws CmsRoleViolationException if the user does not have the required role permissions
673     */
674    public void checkRole(CmsRequestContext context, CmsRole role) throws CmsRoleViolationException {
675
676        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
677        try {
678            checkRole(dbc, role);
679        } finally {
680            dbc.clear();
681        }
682    }
683
684    /**
685     * Checks if the user of the current database context has permissions to impersonate the given role
686     * for the given resource.<p>
687     *
688     * @param dbc the current OpenCms users database context
689     * @param role the role to check
690     * @param resource the resource to check the role for
691     *
692     * @throws CmsRoleViolationException if the user does not have the required role permissions
693     *
694     * @see org.opencms.security.CmsRoleManager#checkRole(CmsObject, CmsRole)
695     */
696    public void checkRoleForResource(CmsDbContext dbc, CmsRole role, CmsResource resource)
697    throws CmsRoleViolationException {
698
699        if (!hasRoleForResource(dbc, dbc.currentUser(), role, resource)) {
700            throw role.createRoleViolationExceptionForResource(dbc.getRequestContext(), resource);
701        }
702    }
703
704    /**
705     * Checks if the user of the current context has permissions to impersonate the given role
706     * for the given resource.<p>
707     *
708     * @param context the current request context
709     * @param role the role to check
710     * @param resource the resource to check the role for
711     *
712     * @throws CmsRoleViolationException if the user does not have the required role permissions
713     */
714    public void checkRoleForResource(CmsRequestContext context, CmsRole role, CmsResource resource)
715    throws CmsRoleViolationException {
716
717        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
718        try {
719            checkRoleForResource(dbc, role, resource);
720        } finally {
721            dbc.clear();
722        }
723    }
724
725    /**
726     * Changes the resource flags of a resource.<p>
727     *
728     * The resource flags are used to indicate various "special" conditions
729     * for a resource. Most notably, the "internal only" setting which signals
730     * that a resource can not be directly requested with it's URL.<p>
731     *
732     * @param context the current request context
733     * @param resource the resource to change the flags for
734     * @param flags the new resource flags for this resource
735     *
736     * @throws CmsException if something goes wrong
737     * @throws CmsSecurityException if the user has insufficient permission for the given resource (({@link CmsPermissionSet#ACCESS_WRITE} required)
738     *
739     * @see org.opencms.file.types.I_CmsResourceType#chflags(CmsObject, CmsSecurityManager, CmsResource, int)
740     */
741
742    public void chflags(CmsRequestContext context, CmsResource resource, int flags)
743    throws CmsException, CmsSecurityException {
744
745        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
746
747            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
748            try {
749                checkOfflineProject(dbc);
750                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
751                m_driverManager.chflags(dbc, resource, flags);
752                modContext.add(resource);
753            } catch (Exception e) {
754                dbc.report(
755                    null,
756                    Messages.get().container(Messages.ERR_CHANGE_RESOURCE_FLAGS_1, context.getSitePath(resource)),
757                    e);
758            } finally {
759                dbc.clear();
760            }
761        }
762    }
763
764    /**
765     * Changes the resource type of a resource.<p>
766     *
767     * OpenCms handles resources according to the resource type,
768     * not the file suffix. This is e.g. why a JSP in OpenCms can have the
769     * suffix ".html" instead of ".jsp" only. Changing the resource type
770     * makes sense e.g. if you want to make a plain text file a JSP resource,
771     * or a binary file an image, etc.<p>
772     *
773     * @param context the current request context
774     * @param resource the resource to change the type for
775     * @param type the new resource type for this resource
776     *
777     * @throws CmsException if something goes wrong
778     * @throws CmsSecurityException if the user has insufficient permission for the given resource (({@link CmsPermissionSet#ACCESS_WRITE} required))
779     *
780     * @see org.opencms.file.types.I_CmsResourceType#chtype(CmsObject, CmsSecurityManager, CmsResource, int)
781     * @see CmsObject#chtype(String, int)
782     */
783    @SuppressWarnings("javadoc")
784    public void chtype(CmsRequestContext context, CmsResource resource, int type)
785    throws CmsException, CmsSecurityException {
786
787        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
788            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
789            try {
790                checkOfflineProject(dbc);
791                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
792                if (CmsResourceTypeJsp.isJspTypeId(type)) {
793                    // security check preventing the creation of a jsp file without permissions
794                    checkRoleForResource(dbc, CmsRole.VFS_MANAGER, resource);
795                }
796                m_driverManager.chtype(dbc, resource, type);
797                modContext.add(resource);
798            } catch (Exception e) {
799                dbc.report(
800                    null,
801                    Messages.get().container(Messages.ERR_CHANGE_RESOURCE_TYPE_1, context.getSitePath(resource)),
802                    e);
803            } finally {
804                dbc.clear();
805            }
806        }
807    }
808
809    /**
810     * Cleans up publish history entries according to the given filter object.
811     *
812     * @param context the request context
813     * @param filter the filter describing what to clean up
814     * @return the number of cleaned up rows
815     * @throws CmsException if something goes wrong
816     */
817    public int cleanupPublishHistory(CmsRequestContext context, CmsPublishHistoryCleanupFilter filter)
818    throws CmsException {
819
820        checkRole(context, CmsRole.VFS_MANAGER);
821        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
822        try {
823            return m_driverManager.cleanupPublishHistory(dbc, filter);
824        } catch (Exception e) {
825            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
826            return 0;
827        } finally {
828            dbc.clear();
829        }
830
831    }
832
833    /**
834     * Copies the access control entries of a given resource to a destination resource.<p>
835     *
836     * Already existing access control entries of the destination resource are removed.<p>
837     *
838     * @param context the current request context
839     * @param source the resource to copy the access control entries from
840     * @param destination the resource to which the access control entries are copied
841     *
842     * @throws CmsException if something goes wrong
843     * @throws CmsSecurityException if the user has insufficient permission for the given resource ({@link CmsPermissionSet#ACCESS_CONTROL} required)
844     */
845    public void copyAccessControlEntries(CmsRequestContext context, CmsResource source, CmsResource destination)
846    throws CmsException, CmsSecurityException {
847
848        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
849
850            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
851            try {
852                checkOfflineProject(dbc);
853                checkPermissions(dbc, source, CmsPermissionSet.ACCESS_READ, true, CmsResourceFilter.ALL);
854                checkPermissions(dbc, destination, CmsPermissionSet.ACCESS_CONTROL, true, CmsResourceFilter.ALL);
855                m_driverManager.copyAccessControlEntries(dbc, source, destination, true);
856                modContext.add(destination);
857            } catch (Exception e) {
858                CmsRequestContext rc = context;
859                dbc.report(
860                    null,
861                    Messages.get().container(
862                        Messages.ERR_COPY_ACE_2,
863                        rc.removeSiteRoot(source.getRootPath()),
864                        rc.removeSiteRoot(destination.getRootPath())),
865                    e);
866            } finally {
867                dbc.clear();
868            }
869        }
870    }
871
872    /**
873     * Copies a resource.<p>
874     *
875     * You must ensure that the destination path is an absolute, valid and
876     * existing VFS path. Relative paths from the source are currently not supported.<p>
877     *
878     * The copied resource will always be locked to the current user
879     * after the copy operation.<p>
880     *
881     * In case the target resource already exists, it is overwritten with the
882     * source resource.<p>
883     *
884     * The <code>siblingMode</code> parameter controls how to handle siblings
885     * during the copy operation.<br>
886     * Possible values for this parameter are: <br>
887     * <ul>
888     * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_NEW}</code></li>
889     * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_SIBLING}</code></li>
890     * <li><code>{@link org.opencms.file.CmsResource#COPY_PRESERVE_SIBLING}</code></li>
891     * </ul><p>
892     *
893     * @param context the current request context
894     * @param source the resource to copy
895     * @param destination the name of the copy destination with complete path
896     * @param siblingMode indicates how to handle siblings during copy
897     *
898     * @throws CmsException if something goes wrong
899     * @throws CmsSecurityException if resource could not be copied
900     *
901     * @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode)
902     * @see org.opencms.file.types.I_CmsResourceType#copyResource(CmsObject, CmsSecurityManager, CmsResource, String, CmsResource.CmsResourceCopyMode)
903     */
904    public void copyResource(
905        CmsRequestContext context,
906        CmsResource source,
907        String destination,
908        CmsResource.CmsResourceCopyMode siblingMode)
909    throws CmsException, CmsSecurityException {
910
911        if ((CmsModificationContext.isInOnlineFolder(destination) != CmsModificationContext.isInOnlineFolder(
912            source.getRootPath())) && (siblingMode != CmsResource.COPY_AS_NEW)) {
913            siblingMode = CmsResource.COPY_AS_NEW;
914            LOG.warn(
915                "Copying resources into or out of online folders - switching to copy mode COPY_AS_NEW: "
916                    + source.getRootPath()
917                    + " -> "
918                    + destination);
919        }
920        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
921
922            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
923            try {
924                checkOfflineProject(dbc);
925                checkPermissions(dbc, source, CmsPermissionSet.ACCESS_READ, true, CmsResourceFilter.ALL);
926                if (source.isFolder() && destination.startsWith(source.getRootPath())) {
927                    throw new CmsVfsException(
928                        Messages.get().container(
929                            Messages.ERR_RECURSIVE_INCLUSION_2,
930                            dbc.removeSiteRoot(source.getRootPath()),
931                            dbc.removeSiteRoot(destination)));
932                }
933                // target permissions will be checked later
934                CmsResource newResource = m_driverManager.copyResource(dbc, source, destination, siblingMode);
935                modContext.add(newResource);
936            } catch (Exception e) {
937                dbc.report(
938                    null,
939                    Messages.get().container(
940                        Messages.ERR_COPY_RESOURCE_2,
941                        dbc.removeSiteRoot(source.getRootPath()),
942                        dbc.removeSiteRoot(destination)),
943                    e);
944            } finally {
945                dbc.clear();
946            }
947        }
948    }
949
950    /**
951     * Copies a resource to the current project of the user.<p>
952     *
953     * @param context the current request context
954     * @param resource the resource to apply this operation to
955     *
956     * @throws CmsException if something goes wrong
957     * @throws CmsRoleViolationException if the current user does not have management access to the project
958     *
959     * @see org.opencms.file.types.I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource)
960     */
961    public void copyResourceToProject(CmsRequestContext context, CmsResource resource)
962    throws CmsException, CmsRoleViolationException {
963
964        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
965        try {
966            checkOfflineProject(dbc);
967            checkManagerOfProjectRole(dbc, context.getCurrentProject());
968
969            m_driverManager.copyResourceToProject(dbc, resource);
970        } catch (Exception e) {
971            dbc.report(
972                null,
973                Messages.get().container(
974                    Messages.ERR_COPY_RESOURCE_TO_PROJECT_2,
975                    context.getSitePath(resource),
976                    context.getCurrentProject().getName()),
977                e);
978        } finally {
979            dbc.clear();
980        }
981    }
982
983    /**
984     * Counts the locked resources in this project.<p>
985     *
986     * @param context the current request context
987     * @param id the id of the project
988     *
989     * @return the amount of locked resources in this project
990     *
991     * @throws CmsException if something goes wrong
992     * @throws CmsRoleViolationException if the current user does not have management access to the project
993     */
994    public int countLockedResources(CmsRequestContext context, CmsUUID id)
995    throws CmsException, CmsRoleViolationException {
996
997        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
998        CmsProject project = null;
999        int result = 0;
1000        try {
1001            project = m_driverManager.readProject(dbc, id);
1002            checkManagerOfProjectRole(dbc, project);
1003            result = m_driverManager.countLockedResources(project);
1004        } catch (Exception e) {
1005            dbc.report(
1006                null,
1007                Messages.get().container(
1008                    Messages.ERR_COUNT_LOCKED_RESOURCES_PROJECT_2,
1009                    (project == null) ? "<failed to read>" : project.getName(),
1010                    id),
1011                e);
1012        } finally {
1013            dbc.clear();
1014        }
1015        return result;
1016    }
1017
1018    /**
1019     * Counts the total number of users which match the given search criteria.<p>
1020     *
1021     * @param requestContext the request context
1022     * @param searchParams the search criteria object
1023     *
1024     * @return the number of users which match the search criteria
1025     * @throws CmsException if something goes wrong
1026     */
1027    public long countUsers(CmsRequestContext requestContext, CmsUserSearchParameters searchParams) throws CmsException {
1028
1029        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
1030        try {
1031            return m_driverManager.countUsers(dbc, searchParams);
1032        } catch (Exception e) {
1033            dbc.report(null, Messages.get().container(Messages.ERR_COUNT_USERS_0), e);
1034            return -1;
1035        } finally {
1036            dbc.clear();
1037        }
1038    }
1039
1040    /**
1041     * Creates a new user group.<p>
1042     *
1043     * @param context the current request context
1044     * @param name the name of the new group
1045     * @param description the description for the new group
1046     * @param flags the flags for the new group
1047     * @param parent the name of the parent group (or <code>null</code>)
1048     *
1049     * @return a <code>{@link CmsGroup}</code> object representing the newly created group
1050     *
1051     * @throws CmsException if operation was not successful.
1052     * @throws CmsRoleViolationException if the  role {@link CmsRole#ACCOUNT_MANAGER} is not owned by the current user
1053     */
1054    public CmsGroup createGroup(CmsRequestContext context, String name, String description, int flags, String parent)
1055    throws CmsException, CmsRoleViolationException {
1056
1057        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1058
1059        CmsGroup result = null;
1060        try {
1061            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(name)));
1062            result = m_driverManager.createGroup(dbc, new CmsUUID(), name, description, flags, parent);
1063        } catch (Exception e) {
1064            dbc.report(null, Messages.get().container(Messages.ERR_CREATE_GROUP_1, name), e);
1065        } finally {
1066            dbc.clear();
1067        }
1068        return result;
1069    }
1070
1071    /**
1072     * Creates a new organizational unit.<p>
1073     *
1074     * @param context the current request context
1075     * @param ouFqn the fully qualified name of the new organizational unit
1076     * @param description the description of the new organizational unit
1077     * @param flags the flags for the new organizational unit
1078     * @param resource the first associated resource
1079     *
1080     * @return a <code>{@link CmsOrganizationalUnit}</code> object representing
1081     *          the newly created organizational unit
1082     *
1083     * @throws CmsException if operation was not successful
1084     *
1085     * @see org.opencms.security.CmsOrgUnitManager#createOrganizationalUnit(CmsObject, String, String, int, String)
1086     */
1087    public CmsOrganizationalUnit createOrganizationalUnit(
1088        CmsRequestContext context,
1089        String ouFqn,
1090        String description,
1091        int flags,
1092        CmsResource resource)
1093    throws CmsException {
1094
1095        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1096        CmsOrganizationalUnit result = null;
1097        try {
1098            checkRole(dbc, CmsRole.ADMINISTRATOR.forOrgUnit(getParentOrganizationalUnit(ouFqn)));
1099            checkOfflineProject(dbc);
1100            result = m_driverManager.createOrganizationalUnit(
1101                dbc,
1102                CmsOrganizationalUnit.removeLeadingSeparator(ouFqn),
1103                description,
1104                flags,
1105                resource);
1106        } catch (Exception e) {
1107            dbc.report(null, Messages.get().container(Messages.ERR_CREATE_ORGUNIT_1, ouFqn), e);
1108        } finally {
1109            dbc.clear();
1110        }
1111        return result;
1112    }
1113
1114    /**
1115     * Creates a project.<p>
1116     *
1117     * @param context the current request context
1118     * @param name the name of the project to create
1119     * @param description the description of the project
1120     * @param groupname the project user group to be set
1121     * @param managergroupname the project manager group to be set
1122     * @param projecttype the type of the project
1123     *
1124     * @return the created project
1125     *
1126     * @throws CmsException if something goes wrong
1127     * @throws CmsRoleViolationException if the current user does not own the role {@link CmsRole#PROJECT_MANAGER}
1128     */
1129    public CmsProject createProject(
1130        CmsRequestContext context,
1131        String name,
1132        String description,
1133        String groupname,
1134        String managergroupname,
1135        CmsProject.CmsProjectType projecttype)
1136    throws CmsException, CmsRoleViolationException {
1137
1138        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1139        CmsProject result = null;
1140        try {
1141            checkRole(dbc, CmsRole.PROJECT_MANAGER.forOrgUnit(getParentOrganizationalUnit(name)));
1142            result = m_driverManager.createProject(
1143                dbc,
1144                CmsOrganizationalUnit.removeLeadingSeparator(name),
1145                description,
1146                CmsOrganizationalUnit.removeLeadingSeparator(groupname),
1147                CmsOrganizationalUnit.removeLeadingSeparator(managergroupname),
1148                projecttype);
1149        } catch (Exception e) {
1150            dbc.report(null, Messages.get().container(Messages.ERR_CREATE_PROJECT_1, name), e);
1151        } finally {
1152            dbc.clear();
1153        }
1154        return result;
1155    }
1156
1157    /**
1158     * Creates a property definition.<p>
1159     *
1160     * Property definitions are valid for all resource types.<p>
1161     *
1162     * @param context the current request context
1163     * @param name the name of the property definition to create
1164     *
1165     * @return the created property definition
1166     *
1167     * @throws CmsException if something goes wrong
1168     * @throws CmsSecurityException if the current project is online.
1169     * @throws CmsRoleViolationException if the current user does not own the role {@link CmsRole#WORKPLACE_MANAGER}
1170     */
1171    public CmsPropertyDefinition createPropertyDefinition(CmsRequestContext context, String name)
1172    throws CmsException, CmsSecurityException, CmsRoleViolationException {
1173
1174        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1175        CmsPropertyDefinition result = null;
1176
1177        try {
1178            checkOfflineProject(dbc);
1179            checkRole(dbc, CmsRole.WORKPLACE_MANAGER.forOrgUnit(null));
1180            result = m_driverManager.createPropertyDefinition(dbc, name);
1181        } catch (Exception e) {
1182            dbc.report(null, Messages.get().container(Messages.ERR_CREATE_PROPDEF_1, name), e);
1183        } finally {
1184            dbc.clear();
1185        }
1186        return result;
1187    }
1188
1189    /**
1190     * Creates a new resource with the provided content and properties.<p>
1191     * An exception is thrown if a resource with the given name already exists.<p>
1192     *
1193     * @param context the current request context
1194     * @param resourcePath the name of the resource to create (full path)
1195     * @param resource the new resource to create
1196     * @param content the content for the new resource
1197     * @param properties the properties for the new resource
1198    *
1199     * @return the created resource
1200     *
1201     * @throws CmsVfsResourceAlreadyExistsException if a resource with the given name already exists
1202     * @throws CmsVfsException if the project in the given database context is the "Online" project
1203     * @throws CmsException if something goes wrong
1204     */
1205    public CmsResource createResource(
1206        CmsRequestContext context,
1207        String resourcePath,
1208        CmsResource resource,
1209        byte[] content,
1210        List<CmsProperty> properties)
1211    throws CmsVfsResourceAlreadyExistsException, CmsVfsException, CmsException {
1212
1213        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
1214            if (existsResource(context, resourcePath, CmsResourceFilter.IGNORE_EXPIRATION)) {
1215                // check if the resource already exists by name
1216                throw new CmsVfsResourceAlreadyExistsException(
1217                    org.opencms.db.generic.Messages.get().container(
1218                        org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
1219                        resource.getRootPath()));
1220            }
1221            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1222            CmsResource newResource = null;
1223            try {
1224                checkOfflineProject(dbc);
1225                newResource = m_driverManager.createResource(dbc, resourcePath, resource, content, properties, false);
1226            } catch (Exception e) {
1227                dbc.report(
1228                    null,
1229                    Messages.get().container(
1230                        Messages.ERR_IMPORT_RESOURCE_2,
1231                        context.getSitePath(resource),
1232                        resourcePath),
1233                    e);
1234            } finally {
1235                dbc.clear();
1236            }
1237            modContext.add(newResource);
1238            return newResource;
1239        }
1240    }
1241
1242    /**
1243     * Creates a new resource of the given resource type with the provided content and properties.<p>
1244     *
1245     * If the provided content is null and the resource is not a folder, the content will be set to an empty byte array.<p>
1246     *
1247     * @param context the current request context
1248     * @param resourcename the name of the resource to create (full path)
1249     * @param type the type of the resource to create
1250     * @param content the content for the new resource
1251     * @param properties the properties for the new resource
1252     *
1253     * @return the created resource
1254     *
1255     * @throws CmsException if something goes wrong
1256     *
1257     * @see org.opencms.file.types.I_CmsResourceType#createResource(CmsObject, CmsSecurityManager, String, byte[], List)
1258     */
1259    public synchronized CmsResource createResource(
1260        CmsRequestContext context,
1261        String resourcename,
1262        int type,
1263        byte[] content,
1264        List<CmsProperty> properties)
1265    throws CmsException {
1266
1267        String checkExistsPath = "/".equals(resourcename) ? "/" : CmsFileUtil.removeTrailingSeparator(resourcename);
1268        // We use checkExistsPath instead of resourcename because when creating a folder /foo/bar/, we want to fail
1269        // if a file /foo/bar already exists.
1270
1271        if (existsResource(context, checkExistsPath, CmsResourceFilter.ALL)) {
1272            // check if the resource already exists by name
1273            throw new CmsVfsResourceAlreadyExistsException(
1274                org.opencms.db.generic.Messages.get().container(
1275                    org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
1276                    resourcename));
1277        }
1278        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
1279            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1280            CmsResource newResource = null;
1281            try {
1282                checkOfflineProject(dbc);
1283                newResource = m_driverManager.createResource(dbc, resourcename, type, content, properties);
1284            } catch (Exception e) {
1285                dbc.report(null, Messages.get().container(Messages.ERR_CREATE_RESOURCE_1, resourcename), e);
1286            } finally {
1287                dbc.clear();
1288            }
1289            modContext.add(newResource);
1290            return newResource;
1291        }
1292    }
1293
1294    /**
1295     * Creates a new sibling of the source resource.<p>
1296     *
1297     * @param context the current request context
1298     * @param source the resource to create a sibling for
1299     * @param destination the name of the sibling to create with complete path
1300     * @param properties the individual properties for the new sibling
1301     *
1302     * @return the new created sibling
1303     *
1304     * @throws CmsException if something goes wrong
1305     *
1306     * @see org.opencms.file.types.I_CmsResourceType#createSibling(CmsObject, CmsSecurityManager, CmsResource, String, List)
1307     */
1308    public CmsResource createSibling(
1309        CmsRequestContext context,
1310        CmsResource source,
1311        String destination,
1312        List<CmsProperty> properties)
1313    throws CmsException {
1314
1315        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1316        CmsResource sibling = null;
1317        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
1318
1319            try {
1320                checkOfflineProject(dbc);
1321                sibling = m_driverManager.createSibling(dbc, source, destination, properties);
1322            } catch (Exception e) {
1323                dbc.report(
1324                    null,
1325                    Messages.get().container(
1326                        Messages.ERR_CREATE_SIBLING_1,
1327                        context.removeSiteRoot(source.getRootPath())),
1328                    e);
1329            } finally {
1330                dbc.clear();
1331            }
1332            if (sibling != null) {
1333                modContext.add(sibling);
1334            }
1335            return sibling;
1336        }
1337    }
1338
1339    /**
1340     * Creates the project for the temporary workplace files.<p>
1341     *
1342     * @param context the current request context
1343     *
1344     * @return the created project for the temporary workplace files
1345     *
1346     * @throws CmsException if something goes wrong
1347     */
1348    public CmsProject createTempfileProject(CmsRequestContext context) throws CmsException {
1349
1350        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1351
1352        CmsProject result = null;
1353        try {
1354            checkRole(dbc, CmsRole.PROJECT_MANAGER.forOrgUnit(null));
1355            result = m_driverManager.createTempfileProject(dbc);
1356        } catch (Exception e) {
1357            dbc.report(null, Messages.get().container(Messages.ERR_CREATE_TEMPFILE_PROJECT_0), e);
1358        } finally {
1359            dbc.clear();
1360        }
1361        return result;
1362    }
1363
1364    /**
1365     * Creates a new user.<p>
1366     *
1367     * @param context the current request context
1368     * @param name the name for the new user
1369     * @param password the password for the new user
1370     * @param description the description for the new user
1371     * @param additionalInfos the additional infos for the user
1372     *
1373     * @return the created user
1374     *
1375     * @see CmsObject#createUser(String, String, String, Map)
1376     *
1377     * @throws CmsException if something goes wrong
1378     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
1379     */
1380    public CmsUser createUser(
1381        CmsRequestContext context,
1382        String name,
1383        String password,
1384        String description,
1385        Map<String, Object> additionalInfos)
1386    throws CmsException, CmsRoleViolationException {
1387
1388        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1389
1390        CmsUser result = null;
1391        try {
1392            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(name)));
1393            result = m_driverManager.createUser(
1394                dbc,
1395                CmsOrganizationalUnit.removeLeadingSeparator(name),
1396                password,
1397                description,
1398                additionalInfos);
1399        } catch (Exception e) {
1400            dbc.report(null, Messages.get().container(Messages.ERR_CREATE_USER_1, name), e);
1401        } finally {
1402            dbc.clear();
1403        }
1404        return result;
1405    }
1406
1407    /**
1408     * Deletes alias entries matching a filter.<p>
1409     *
1410     * @param context the request context
1411     * @param filter the alias filter
1412     *
1413     * @throws CmsException if something goes wrong
1414     */
1415    public void deleteAliases(CmsRequestContext context, CmsAliasFilter filter) throws CmsException {
1416
1417        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1418        try {
1419            m_driverManager.deleteAliases(dbc, context.getCurrentProject(), filter);
1420        } catch (Exception e) {
1421            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
1422        } finally {
1423            dbc.clear();
1424        }
1425
1426    }
1427
1428    /**
1429     * Deletes all entries in the published resource table.<p>
1430     *
1431     * @param context the current request context
1432     * @param linkType the type of resource deleted (0= non-parameter, 1=parameter)
1433     *
1434     * @throws CmsException if something goes wrong
1435     */
1436    public void deleteAllStaticExportPublishedResources(CmsRequestContext context, int linkType) throws CmsException {
1437
1438        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1439        try {
1440            m_driverManager.deleteAllStaticExportPublishedResources(dbc, linkType);
1441        } catch (Exception e) {
1442            dbc.report(null, Messages.get().container(Messages.ERR_DELETE_STATEXP_PUBLISHED_RESOURCES_0), e);
1443        } finally {
1444            dbc.clear();
1445        }
1446    }
1447
1448    /**
1449     * Deletes a group, where all permissions, users and children of the group
1450     * are transfered to a replacement group.<p>
1451     *
1452     * @param context the current request context
1453     * @param groupId the id of the group to be deleted
1454     * @param replacementId the id of the group to be transfered, can be <code>null</code>
1455     *
1456     * @throws CmsException if operation was not successful
1457     * @throws CmsSecurityException if the group is a default group.
1458     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
1459     */
1460    public void deleteGroup(CmsRequestContext context, CmsUUID groupId, CmsUUID replacementId)
1461    throws CmsException, CmsRoleViolationException, CmsSecurityException {
1462
1463        CmsGroup group = readGroup(context, groupId);
1464        if (group.isRole()) {
1465            throw new CmsSecurityException(Messages.get().container(Messages.ERR_DELETE_ROLE_GROUP_1, group.getName()));
1466        }
1467        CmsDbContext dbc = null;
1468        try {
1469            dbc = getDbContextForDeletePrincipal(context);
1470            // catch own exception as special cause for general "Error deleting group".
1471            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(group.getName())));
1472            m_driverManager.deleteGroup(dbc, group, replacementId);
1473        } catch (Exception e) {
1474            CmsDbContext dbcForException = m_dbContextFactory.getDbContext(context);
1475            dbcForException.report(null, Messages.get().container(Messages.ERR_DELETE_GROUP_1, group.getName()), e);
1476            dbcForException.clear();
1477        } finally {
1478            if (null != dbc) {
1479                dbc.clear();
1480            }
1481        }
1482    }
1483
1484    /**
1485     * Delete a user group.<p>
1486     *
1487     * Only groups that contain no subgroups can be deleted.<p>
1488     *
1489     * @param context the current request context
1490     * @param name the name of the group that is to be deleted
1491     *
1492     * @throws CmsException if operation was not successful
1493     * @throws CmsSecurityException if the group is a default group.
1494     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
1495     */
1496    public void deleteGroup(CmsRequestContext context, String name)
1497    throws CmsException, CmsRoleViolationException, CmsSecurityException {
1498
1499        CmsGroup group = readGroup(context, name);
1500        if (group.isRole()) {
1501            throw new CmsSecurityException(Messages.get().container(Messages.ERR_DELETE_ROLE_GROUP_1, name));
1502        }
1503        CmsDbContext dbc = null;
1504        try {
1505            dbc = getDbContextForDeletePrincipal(context);
1506            // catch own exception as special cause for general "Error deleting group".
1507            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(name)));
1508            m_driverManager.deleteGroup(dbc, group, null);
1509        } catch (Exception e) {
1510            CmsDbContext dbcForException = m_dbContextFactory.getDbContext(context);
1511            dbcForException.report(null, Messages.get().container(Messages.ERR_DELETE_GROUP_1, name), e);
1512            dbcForException.clear();
1513        } finally {
1514            if (null != dbc) {
1515                dbc.clear();
1516            }
1517        }
1518
1519    }
1520
1521    /**
1522     * Deletes the versions from the history tables, keeping the given number of versions per resource.<p>
1523     *
1524     * @param context the current request context
1525     * @param versionsToKeep number of versions to keep, is ignored if negative
1526     * @param versionsDeleted number of versions to keep for deleted resources, is ignored if negative
1527     * @param timeDeleted deleted resources older than this will also be deleted, is ignored if negative
1528     * @param clearDeletedFilter a filter to evaluate whether a the history entry for deleted resources should be cleared
1529     * @param report the report for output logging
1530     *
1531     * @throws CmsException if operation was not successful
1532     * @throws CmsRoleViolationException if the current user does not own the role {@link CmsRole#WORKPLACE_MANAGER}
1533     */
1534    public void deleteHistoricalVersions(
1535        CmsRequestContext context,
1536        int versionsToKeep,
1537        int versionsDeleted,
1538        long timeDeleted,
1539        Predicate<I_CmsHistoryResource> clearDeletedFilter,
1540        I_CmsReport report)
1541    throws CmsException, CmsRoleViolationException {
1542
1543        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1544        try {
1545            CmsFolder root = readFolder(dbc, "/", CmsResourceFilter.ALL);
1546            checkRole(dbc, CmsRole.WORKPLACE_MANAGER.forOrgUnit(null));
1547            checkPermissions(dbc, root, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL);
1548            m_driverManager.deleteHistoricalVersions(
1549                dbc,
1550                versionsToKeep,
1551                versionsDeleted,
1552                timeDeleted,
1553                clearDeletedFilter,
1554                report);
1555        } catch (Exception e) {
1556            dbc.report(
1557                null,
1558                Messages.get().container(
1559                    Messages.ERR_DELETE_HISTORY_4,
1560                    new Object[] {
1561                        "/",
1562                        Integer.valueOf(versionsToKeep),
1563                        Integer.valueOf(versionsDeleted),
1564                        new Date(timeDeleted)}),
1565                e);
1566        } finally {
1567            dbc.clear();
1568        }
1569    }
1570
1571    /**
1572     * Deletes all log entries matching the given filter.<p>
1573     *
1574     * @param context the current user context
1575     * @param filter the filter to use for deletion
1576     *
1577     * @throws CmsException if something goes wrong
1578     *
1579     * @see #getLogEntries(CmsRequestContext, CmsLogFilter)
1580     * @see CmsObject#deleteLogEntries(CmsLogFilter)
1581     */
1582    public void deleteLogEntries(CmsRequestContext context, CmsLogFilter filter) throws CmsException {
1583
1584        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1585        try {
1586            checkRole(dbc, CmsRole.WORKPLACE_MANAGER);
1587            m_driverManager.deleteLogEntries(dbc, filter);
1588        } catch (Exception e) {
1589            dbc.report(null, Messages.get().container(Messages.ERR_DELETE_LOG_0), e);
1590        } finally {
1591            dbc.clear();
1592        }
1593    }
1594
1595    /**
1596     * Deletes an organizational unit.<p>
1597     *
1598     * Only organizational units that contain no sub organizational unit can be deleted.<p>
1599     *
1600     * The organizational unit can not be delete if it is used in the request context,
1601     * or if the current user belongs to it.<p>
1602     *
1603     * All users and groups in the given organizational unit will be deleted.<p>
1604     *
1605     * @param context the current request context
1606     * @param organizationalUnit the organizational unit to delete
1607     *
1608     * @throws CmsException if operation was not successful
1609     *
1610     * @see org.opencms.security.CmsOrgUnitManager#deleteOrganizationalUnit(CmsObject, String)
1611     */
1612    public void deleteOrganizationalUnit(CmsRequestContext context, CmsOrganizationalUnit organizationalUnit)
1613    throws CmsException {
1614
1615        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1616        try {
1617            // check for root ou
1618            if (organizationalUnit.getParentFqn() == null) {
1619                throw new CmsDataAccessException(
1620                    org.opencms.security.Messages.get().container(
1621                        org.opencms.security.Messages.ERR_ORGUNIT_ROOT_EDITION_0));
1622            }
1623
1624            checkRole(dbc, CmsRole.ADMINISTRATOR.forOrgUnit(getParentOrganizationalUnit(organizationalUnit.getName())));
1625            checkOfflineProject(dbc);
1626            m_driverManager.deleteOrganizationalUnit(dbc, organizationalUnit);
1627        } catch (Exception e) {
1628            dbc.report(null, Messages.get().container(Messages.ERR_DELETE_ORGUNIT_1, organizationalUnit.getName()), e);
1629        } finally {
1630            dbc.clear();
1631        }
1632    }
1633
1634    /**
1635     * Deletes a project.<p>
1636     *
1637     * All modified resources currently inside this project will be reset to their online state.<p>
1638     *
1639     * @param context the current request context
1640     * @param projectId the ID of the project to be deleted
1641     *
1642     * @throws CmsException if something goes wrong
1643     * @throws CmsRoleViolationException if the current user does not own management access to the project
1644     */
1645    public void deleteProject(CmsRequestContext context, CmsUUID projectId)
1646    throws CmsException, CmsRoleViolationException {
1647
1648        if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
1649            // online project must not be deleted
1650            throw new CmsVfsException(
1651                org.opencms.file.Messages.get().container(
1652                    org.opencms.file.Messages.ERR_NOT_ALLOWED_IN_ONLINE_PROJECT_0));
1653        }
1654
1655        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1656        CmsProject deleteProject = null;
1657        try {
1658            // read the project that should be deleted
1659            deleteProject = m_driverManager.readProject(dbc, projectId);
1660            checkManagerOfProjectRole(dbc, deleteProject);
1661            m_driverManager.deleteProject(dbc, deleteProject);
1662        } catch (Exception e) {
1663            String projectName = (deleteProject == null ? String.valueOf(projectId) : deleteProject.getName());
1664            dbc.report(null, Messages.get().container(Messages.ERR_DELETE_PROJECT_1, projectName), e);
1665        } finally {
1666            dbc.clear();
1667        }
1668    }
1669
1670    /**
1671     * Deletes a property definition.<p>
1672     *
1673     * @param context the current request context
1674     * @param name the name of the property definition to delete
1675     *
1676     * @throws CmsException if something goes wrong
1677     * @throws CmsSecurityException if the project to delete is the "Online" project
1678     * @throws CmsRoleViolationException if the current user does not own the role {@link CmsRole#WORKPLACE_MANAGER}
1679     */
1680    public void deletePropertyDefinition(CmsRequestContext context, String name)
1681    throws CmsException, CmsSecurityException, CmsRoleViolationException {
1682
1683        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1684        try {
1685            checkOfflineProject(dbc);
1686            checkRole(dbc, CmsRole.WORKPLACE_MANAGER.forOrgUnit(null));
1687            m_driverManager.deletePropertyDefinition(dbc, name);
1688        } catch (Exception e) {
1689            dbc.report(null, Messages.get().container(Messages.ERR_DELETE_PROPERTY_1, name), e);
1690        } finally {
1691            dbc.clear();
1692        }
1693    }
1694
1695    /**
1696     * Deletes all relations for the given resource matching the given filter.<p>
1697     *
1698     * @param context the current user context
1699     * @param resource the resource to delete the relations for
1700     * @param filter the filter to use for deletion
1701     *
1702     * @throws CmsException if something goes wrong
1703     *
1704     * @see #addRelationToResource(CmsRequestContext, CmsResource, CmsResource, CmsRelationType, boolean)
1705     * @see CmsObject#deleteRelationsFromResource(String, CmsRelationFilter)
1706     */
1707    public void deleteRelationsForResource(CmsRequestContext context, CmsResource resource, CmsRelationFilter filter)
1708    throws CmsException {
1709
1710        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
1711            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1712
1713            try {
1714                checkOfflineProject(dbc);
1715                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
1716                m_driverManager.deleteRelationsForResource(dbc, resource, filter);
1717                modContext.add(resource);
1718            } catch (Exception e) {
1719                dbc.report(
1720                    null,
1721                    Messages.get().container(
1722                        Messages.ERR_DELETE_RELATIONS_1,
1723                        dbc.removeSiteRoot(resource.getRootPath())),
1724                    e);
1725            } finally {
1726                dbc.clear();
1727            }
1728        }
1729    }
1730
1731    /**
1732     * Deletes a resource given its name.<p>
1733     *
1734     * The <code>siblingMode</code> parameter controls how to handle siblings
1735     * during the delete operation.<br>
1736     * Possible values for this parameter are: <br>
1737     * <ul>
1738     * <li><code>{@link CmsResource#DELETE_REMOVE_SIBLINGS}</code></li>
1739     * <li><code>{@link CmsResource#DELETE_PRESERVE_SIBLINGS}</code></li>
1740     * </ul><p>
1741     *
1742     * @param context the current request context
1743     * @param resource the name of the resource to delete (full path)
1744     * @param siblingMode indicates how to handle siblings of the deleted resource
1745     *
1746     * @throws CmsException if something goes wrong
1747     * @throws CmsSecurityException if the user does not have {@link CmsPermissionSet#ACCESS_WRITE} on the given resource
1748     *
1749     * @see org.opencms.file.types.I_CmsResourceType#deleteResource(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode)
1750     */
1751    public void deleteResource(
1752        CmsRequestContext context,
1753        CmsResource resource,
1754        CmsResource.CmsResourceDeleteMode siblingMode)
1755    throws CmsException, CmsSecurityException {
1756
1757        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
1758            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1759            Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(context);
1760            final CmsUUID forbiddenFolderId = OpenCms.getPublishManager().getPublishListVerifier().addForbiddenParentFolder(
1761                resource.getRootPath(),
1762                Messages.get().getBundle(locale).key(Messages.ERR_FORBIDDEN_PARENT_CURRENTLY_DELETING_0));
1763            try {
1764                checkOfflineProject(dbc);
1765                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
1766                checkSystemLocks(dbc, resource);
1767
1768                // check write permissions for subresources in case of deleting a folder
1769                if (resource.isFolder()) {
1770                    dbc.getRequestContext().setAttribute(I_CmsVfsDriver.REQ_ATTR_CHECK_PERMISSIONS, Boolean.TRUE);
1771                    try {
1772                        m_driverManager.getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), resource);
1773                    } catch (CmsDataAccessException e) {
1774                        // unwrap the permission violation exception
1775                        if (e.getCause() instanceof CmsPermissionViolationException) {
1776                            throw (CmsPermissionViolationException)e.getCause();
1777                        } else {
1778                            throw e;
1779                        }
1780                    }
1781                    dbc.getRequestContext().removeAttribute(I_CmsVfsDriver.REQ_ATTR_CHECK_PERMISSIONS);
1782                }
1783
1784                deleteResource(dbc, resource, siblingMode);
1785            } catch (Exception e) {
1786                dbc.report(
1787                    null,
1788                    Messages.get().container(Messages.ERR_DELETE_RESOURCE_1, context.getSitePath(resource)),
1789                    e);
1790            } finally {
1791                OpenCms.getPublishManager().getPublishListVerifier().removeForbiddenParentFolder(forbiddenFolderId);
1792                dbc.clear();
1793            }
1794        }
1795    }
1796
1797    /**
1798     * Deletes an entry in the published resource table.<p>
1799     *
1800     * @param context the current request context
1801     * @param resourceName The name of the resource to be deleted in the static export
1802     * @param linkType the type of resource deleted (0= non-parameter, 1=parameter)
1803     * @param linkParameter the parameters of the resource
1804     *
1805     * @throws CmsException if something goes wrong
1806     */
1807    public void deleteStaticExportPublishedResource(
1808        CmsRequestContext context,
1809        String resourceName,
1810        int linkType,
1811        String linkParameter)
1812    throws CmsException {
1813
1814        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1815        try {
1816            m_driverManager.deleteStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter);
1817        } catch (Exception e) {
1818            dbc.report(
1819                null,
1820                Messages.get().container(Messages.ERR_DELETE_STATEXP_PUBLISHES_RESOURCE_1, resourceName),
1821                e);
1822        } finally {
1823            dbc.clear();
1824        }
1825    }
1826
1827    /**
1828     * Deletes a user.<p>
1829     *
1830     * @param context the current request context
1831     * @param userId the Id of the user to be deleted
1832     *
1833     * @throws CmsException if something goes wrong
1834     */
1835    public void deleteUser(CmsRequestContext context, CmsUUID userId) throws CmsException {
1836
1837        CmsUser user = readUser(context, userId);
1838        deleteUser(context, user, null);
1839    }
1840
1841    /**
1842     * Deletes a user, where all permissions and resources attributes of the user
1843     * were transfered to a replacement user.<p>
1844     *
1845     * @param context the current request context
1846     * @param userId the id of the user to be deleted
1847     * @param replacementId the id of the user to be transfered
1848     *
1849     * @throws CmsException if operation was not successful
1850     */
1851    public void deleteUser(CmsRequestContext context, CmsUUID userId, CmsUUID replacementId) throws CmsException {
1852
1853        CmsUser user = readUser(context, userId);
1854        CmsUser replacementUser = null;
1855        if ((replacementId != null) && !replacementId.isNullUUID()) {
1856            replacementUser = readUser(context, replacementId);
1857        }
1858        deleteUser(context, user, replacementUser);
1859    }
1860
1861    /**
1862     * Deletes a user.<p>
1863     *
1864     * @param context the current request context
1865     * @param username the name of the user to be deleted
1866     *
1867     * @throws CmsException if something goes wrong
1868     */
1869    public void deleteUser(CmsRequestContext context, String username) throws CmsException {
1870
1871        CmsUser user = readUser(context, username);
1872        deleteUser(context, user, null);
1873    }
1874
1875    /**
1876     * Destroys this security manager.<p>
1877     *
1878     * @throws Throwable if something goes wrong
1879     */
1880    public synchronized void destroy() throws Throwable {
1881
1882        try {
1883            if (m_driverManager != null) {
1884                if (m_driverManager.getLockManager() != null) {
1885                    try {
1886                        writeLocks();
1887                    } catch (Throwable t) {
1888                        if (LOG.isErrorEnabled()) {
1889                            LOG.error(
1890                                org.opencms.lock.Messages.get().getBundle().key(
1891                                    org.opencms.lock.Messages.ERR_WRITE_LOCKS_FINAL_0),
1892                                t);
1893                        }
1894                    }
1895                }
1896                m_driverManager.destroy();
1897            }
1898        } catch (Throwable t) {
1899            if (LOG.isErrorEnabled()) {
1900                LOG.error(Messages.get().getBundle().key(Messages.LOG_ERR_DRIVER_MANAGER_CLOSE_0), t);
1901            }
1902        }
1903
1904        m_driverManager = null;
1905        m_dbContextFactory = null;
1906
1907        if (CmsLog.INIT.isInfoEnabled()) {
1908            CmsLog.INIT.info(
1909                Messages.get().getBundle().key(Messages.INIT_SECURITY_MANAGER_SHUTDOWN_1, this.getClass().getName()));
1910        }
1911    }
1912
1913    /**
1914     * Checks the availability of a resource in the VFS,
1915     * using the <code>{@link CmsResourceFilter#DEFAULT}</code> filter.<p>
1916     *
1917     * A resource may be of type <code>{@link CmsFile}</code> or
1918     * <code>{@link CmsFolder}</code>.<p>
1919     *
1920     * The specified filter controls what kind of resources should be "found"
1921     * during the read operation. This will depend on the application. For example,
1922     * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
1923     * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
1924     * will ignore the date release / date expired information of the resource.<p>
1925     *
1926     * This method also takes into account the user permissions, so if
1927     * the given resource exists, but the current user has not the required
1928     * permissions, then this method will return <code>false</code>.<p>
1929     *
1930     * @param context the current request context
1931     * @param structureId the structure id of the resource to check
1932     * @param filter the resource filter to use while reading
1933     *
1934     * @return <code>true</code> if the resource is available
1935     *
1936     * @see CmsObject#existsResource(CmsUUID, CmsResourceFilter)
1937     * @see CmsObject#existsResource(CmsUUID)
1938     */
1939    public boolean existsResource(CmsRequestContext context, CmsUUID structureId, CmsResourceFilter filter) {
1940
1941        boolean result = false;
1942        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1943        try {
1944            readResource(dbc, structureId, filter);
1945            result = true;
1946        } catch (Exception e) {
1947            result = false;
1948        } finally {
1949            dbc.clear();
1950        }
1951        return result;
1952    }
1953
1954    /**
1955     * Checks the availability of a resource in the VFS,
1956     * using the <code>{@link CmsResourceFilter#DEFAULT}</code> filter.<p>
1957     *
1958     * A resource may be of type <code>{@link CmsFile}</code> or
1959     * <code>{@link CmsFolder}</code>.<p>
1960     *
1961     * The specified filter controls what kind of resources should be "found"
1962     * during the read operation. This will depend on the application. For example,
1963     * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
1964     * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
1965     * will ignore the date release / date expired information of the resource.<p>
1966     *
1967     * This method also takes into account the user permissions, so if
1968     * the given resource exists, but the current user has not the required
1969     * permissions, then this method will return <code>false</code>.<p>
1970     *
1971     * @param context the current request context
1972     * @param resourcePath the name of the resource to read (full path)
1973     * @param filter the resource filter to use while reading
1974     *
1975     * @return <code>true</code> if the resource is available
1976     *
1977     * @see CmsObject#existsResource(String, CmsResourceFilter)
1978     * @see CmsObject#existsResource(String)
1979     */
1980    public boolean existsResource(CmsRequestContext context, String resourcePath, CmsResourceFilter filter) {
1981
1982        boolean result = false;
1983        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
1984        try {
1985            readResource(dbc, resourcePath, filter);
1986            result = true;
1987        } catch (Exception e) {
1988            result = false;
1989        } finally {
1990            dbc.clear();
1991        }
1992        return result;
1993    }
1994
1995    /**
1996     * Fills the given publish list with the the VFS resources that actually get published.<p>
1997     *
1998     * Please refer to the source code of this method for the rules on how to decide whether a
1999     * new/changed/deleted <code>{@link CmsResource}</code> object can be published or not.<p>
2000     *
2001     * @param context the current request context
2002     * @param publishList must be initialized with basic publish information (Project or direct publish operation)
2003     *
2004     * @return the given publish list filled with all new/changed/deleted files from the current (offline) project
2005     *      that will be published actually
2006     *
2007     * @throws CmsException if something goes wrong
2008     *
2009     * @see org.opencms.db.CmsPublishList
2010     */
2011    public CmsPublishList fillPublishList(CmsRequestContext context, CmsPublishList publishList) throws CmsException {
2012
2013        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2014        try {
2015            m_driverManager.fillPublishList(dbc, publishList);
2016            checkPublishPermissions(dbc, publishList);
2017        } catch (CmsTooManyPublishResourcesException e) {
2018            throw e;
2019        } catch (Exception e) {
2020            if (publishList.isDirectPublish()) {
2021                dbc.report(
2022                    null,
2023                    Messages.get().container(
2024                        Messages.ERR_GET_PUBLISH_LIST_DIRECT_1,
2025                        CmsFileUtil.formatResourceNames(context, publishList.getDirectPublishResources())),
2026                    e);
2027            } else {
2028                dbc.report(
2029                    null,
2030                    Messages.get().container(
2031                        Messages.ERR_GET_PUBLISH_LIST_PROJECT_1,
2032                        context.getCurrentProject().getName()),
2033                    e);
2034            }
2035        } finally {
2036            dbc.clear();
2037        }
2038        return publishList;
2039    }
2040
2041    /**
2042     * Returns the list of access control entries of a resource given its name.<p>
2043     *
2044     * @param context the current request context
2045     * @param resource the resource to read the access control entries for
2046     * @param getInherited true if the result should include all access control entries inherited by parent folders
2047     *
2048     * @return a list of <code>{@link CmsAccessControlEntry}</code> objects defining all permissions for the given resource
2049     *
2050     * @throws CmsException if something goes wrong
2051     */
2052    public List<CmsAccessControlEntry> getAccessControlEntries(
2053        CmsRequestContext context,
2054        CmsResource resource,
2055        boolean getInherited)
2056    throws CmsException {
2057
2058        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2059        List<CmsAccessControlEntry> result = null;
2060        try {
2061            result = m_driverManager.getAccessControlEntries(dbc, resource, getInherited);
2062        } catch (Exception e) {
2063            dbc.report(
2064                null,
2065                Messages.get().container(Messages.ERR_GET_ACL_ENTRIES_1, context.getSitePath(resource)),
2066                e);
2067        } finally {
2068            dbc.clear();
2069        }
2070        return result;
2071    }
2072
2073    /**
2074     * Returns the access control list (summarized access control entries) of a given resource.<p>
2075     *
2076     * If <code>inheritedOnly</code> is set, only inherited access control entries are returned.<p>
2077     *
2078     * @param context the current request context
2079     * @param resource the resource
2080     * @param inheritedOnly skip non-inherited entries if set
2081     *
2082     * @return the access control list of the resource
2083     *
2084     * @throws CmsException if something goes wrong
2085     */
2086    public CmsAccessControlList getAccessControlList(
2087        CmsRequestContext context,
2088        CmsResource resource,
2089        boolean inheritedOnly)
2090    throws CmsException {
2091
2092        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2093        CmsAccessControlList result = null;
2094        try {
2095            result = m_driverManager.getAccessControlList(dbc, resource, inheritedOnly);
2096        } catch (Exception e) {
2097            dbc.report(
2098                null,
2099                Messages.get().container(Messages.ERR_GET_ACL_ENTRIES_1, context.getSitePath(resource)),
2100                e);
2101
2102        } finally {
2103            dbc.clear();
2104        }
2105        return result;
2106    }
2107
2108    /**
2109     * Gets the aliases for a given site.<p>
2110     *
2111     * @param requestContext the current request context
2112     * @param siteRoot the site root
2113     *
2114     * @return the list of aliases for the given site root
2115     *
2116     * @throws CmsException if something goes wrong
2117     */
2118    public List<CmsAlias> getAliasesForSite(CmsRequestContext requestContext, String siteRoot) throws CmsException {
2119
2120        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
2121        try {
2122            List<CmsAlias> aliases = m_driverManager.readAliasesBySite(
2123                dbc,
2124                requestContext.getCurrentProject(),
2125                siteRoot);
2126            return aliases;
2127        } catch (Exception e) {
2128            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
2129            return null; // will never be executed
2130        } finally {
2131            dbc.clear();
2132        }
2133    }
2134
2135    /**
2136     * Gets all access control entries.<p>
2137     *
2138     * @param context the current request context
2139     * @return the list of all access control entries
2140     *
2141     * @throws CmsException if something goes wrong
2142     */
2143    public List<CmsAccessControlEntry> getAllAccessControlEntries(CmsRequestContext context) throws CmsException {
2144
2145        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2146        List<CmsAccessControlEntry> result = null;
2147        try {
2148            result = m_driverManager.getAllAccessControlEntries(dbc);
2149        } catch (Exception e) {
2150            dbc.report(null, Messages.get().container(Messages.ERR_GET_ACL_ENTRIES_1, "<all resources>"), e);
2151        } finally {
2152            dbc.clear();
2153        }
2154        return result;
2155
2156    }
2157
2158    /**
2159     * Returns all projects which are owned by the current user or which are
2160     * accessible for the group of the user.<p>
2161     *
2162     * @param context the current request context
2163     * @param orgUnit the organizational unit to search project in
2164     * @param includeSubOus if to include sub organizational units
2165     *
2166     * @return a list of objects of type <code>{@link CmsProject}</code>
2167     *
2168     * @throws CmsException if something goes wrong
2169     */
2170    public List<CmsProject> getAllAccessibleProjects(
2171        CmsRequestContext context,
2172        CmsOrganizationalUnit orgUnit,
2173        boolean includeSubOus)
2174    throws CmsException {
2175
2176        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2177        List<CmsProject> result = null;
2178        try {
2179            result = m_driverManager.getAllAccessibleProjects(dbc, orgUnit, includeSubOus);
2180        } catch (Exception e) {
2181            dbc.report(
2182                null,
2183                Messages.get().container(Messages.ERR_GET_ALL_ACCESSIBLE_PROJECTS_1, dbc.currentUser().getName()),
2184                e);
2185        } finally {
2186            dbc.clear();
2187        }
2188        return result;
2189    }
2190
2191    /**
2192     * Returns a list with all projects from history.<p>
2193     *
2194     * @param context the current request context
2195     *
2196     * @return list of <code>{@link CmsHistoryProject}</code> objects
2197     *           with all projects from history.
2198     *
2199     * @throws CmsException if operation was not successful
2200     */
2201    public List<CmsHistoryProject> getAllHistoricalProjects(CmsRequestContext context) throws CmsException {
2202
2203        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2204        List<CmsHistoryProject> result = null;
2205        try {
2206            result = m_driverManager.getAllHistoricalProjects(dbc);
2207        } catch (Exception e) {
2208            dbc.report(
2209                null,
2210                Messages.get().container(Messages.ERR_GET_ALL_ACCESSIBLE_PROJECTS_1, dbc.currentUser().getName()),
2211                e);
2212        } finally {
2213            dbc.clear();
2214        }
2215        return result;
2216    }
2217
2218    /**
2219     * Returns all projects which are owned by the current user or which are manageable
2220     * for the group of the user.<p>
2221     *
2222     * @param context the current request context
2223     * @param orgUnit the organizational unit to search project in
2224     * @param includeSubOus if to include sub organizational units
2225     *
2226     * @return a list of objects of type <code>{@link CmsProject}</code>
2227     *
2228     * @throws CmsException if operation was not successful
2229     */
2230    public List<CmsProject> getAllManageableProjects(
2231        CmsRequestContext context,
2232        CmsOrganizationalUnit orgUnit,
2233        boolean includeSubOus)
2234    throws CmsException {
2235
2236        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2237        List<CmsProject> result = null;
2238        try {
2239            result = m_driverManager.getAllManageableProjects(dbc, orgUnit, includeSubOus);
2240        } catch (Exception e) {
2241            dbc.report(
2242                null,
2243                Messages.get().container(Messages.ERR_GET_ALL_MANAGEABLE_PROJECTS_1, dbc.currentUser().getName()),
2244                e);
2245        } finally {
2246            dbc.clear();
2247        }
2248        return result;
2249    }
2250
2251    /**
2252     * Returns all child groups of a group.<p>
2253     *
2254     * This method also returns all sub-child groups of the current group.<p>
2255     *
2256     * @param context the current request context
2257     * @param groupname the name of the group
2258     * @param includeSubChildren if set also returns all sub-child groups of the given group
2259     *
2260     * @return a list of all child <code>{@link CmsGroup}</code> objects or <code>null</code>
2261     *
2262     * @throws CmsException if operation was not successful
2263     */
2264    public List<CmsGroup> getChildren(CmsRequestContext context, String groupname, boolean includeSubChildren)
2265    throws CmsException {
2266
2267        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2268        List<CmsGroup> result = null;
2269        try {
2270            result = m_driverManager.getChildren(
2271                dbc,
2272                m_driverManager.readGroup(dbc, CmsOrganizationalUnit.removeLeadingSeparator(groupname)),
2273                includeSubChildren);
2274        } catch (Exception e) {
2275            dbc.report(null, Messages.get().container(Messages.ERR_GET_CHILD_GROUPS_TRANSITIVE_1, groupname), e);
2276        } finally {
2277            dbc.clear();
2278        }
2279        return result;
2280    }
2281
2282    /**
2283     * Gets a connection from a connection pool.<p>
2284     * @param poolUrl the connection pool url
2285     * @return a new connection from the pool
2286     *
2287     * @throws SQLException if getting the connection fails
2288     */
2289    public Connection getConnection(String poolUrl) throws SQLException {
2290
2291        CmsDbPoolV11 pool = CmsDriverManager.m_pools.get(poolUrl);
2292        return pool.getConnection();
2293
2294    }
2295
2296    /**
2297     * Returns the date when the resource was last visited by the user.<p>
2298     *
2299     * @param context the request context
2300     * @param poolName the name of the database pool to use
2301     * @param user the user to check the date
2302     * @param resource the resource to check the date
2303     *
2304     * @return the date when the resource was last visited by the user
2305     *
2306     * @throws CmsException if something goes wrong
2307     */
2308    public long getDateLastVisitedBy(CmsRequestContext context, String poolName, CmsUser user, CmsResource resource)
2309    throws CmsException {
2310
2311        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2312        long result = 0;
2313        try {
2314            result = m_driverManager.getDateLastVisitedBy(dbc, poolName, user, resource);
2315        } catch (Exception e) {
2316            dbc.report(
2317                null,
2318                Messages.get().container(
2319                    Messages.ERR_GET_DATE_LASTVISITED_2,
2320                    user.getName(),
2321                    context.getSitePath(resource)),
2322                e);
2323        } finally {
2324            dbc.clear();
2325        }
2326        return result;
2327    }
2328
2329    /**
2330     * Returns all groups of the given organizational unit.<p>
2331     *
2332     * @param context the current request context
2333     * @param orgUnit the organizational unit to get the groups for
2334     * @param includeSubOus if all groups of sub-organizational units should be retrieved too
2335     * @param readRoles if to read roles or groups
2336     *
2337     * @return all <code>{@link CmsGroup}</code> objects in the organizational unit
2338     *
2339     * @throws CmsException if operation was not successful
2340     *
2341     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
2342     * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
2343     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
2344     */
2345    public List<CmsGroup> getGroups(
2346        CmsRequestContext context,
2347        CmsOrganizationalUnit orgUnit,
2348        boolean includeSubOus,
2349        boolean readRoles)
2350    throws CmsException {
2351
2352        List<CmsGroup> result = null;
2353        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2354        try {
2355            result = m_driverManager.getGroups(dbc, orgUnit, includeSubOus, readRoles);
2356        } catch (Exception e) {
2357            dbc.report(null, Messages.get().container(Messages.ERR_READ_ORGUNIT_GROUPS_1, orgUnit.getName()), e);
2358        } finally {
2359            dbc.clear();
2360        }
2361        return result;
2362    }
2363
2364    /**
2365     * Returns the list of groups to which the user directly belongs to.<p>
2366     *
2367     * @param context the current request context
2368     * @param username The name of the user
2369     * @param ouFqn the fully qualified name of the organizational unit to restrict the result set for
2370     * @param includeChildOus include groups of child organizational units
2371     * @param readRoles if to read roles or groups
2372     * @param directGroupsOnly if set only the direct assigned groups will be returned, if not also indirect roles
2373     * @param remoteAddress the IP address to filter the groups in the result list
2374     *
2375     * @return a list of <code>{@link CmsGroup}</code> objects filtered by the given IP address
2376     *
2377     * @throws CmsException if operation was not successful
2378     */
2379    public List<CmsGroup> getGroupsOfUser(
2380        CmsRequestContext context,
2381        String username,
2382        String ouFqn,
2383        boolean includeChildOus,
2384        boolean readRoles,
2385        boolean directGroupsOnly,
2386        String remoteAddress)
2387    throws CmsException {
2388
2389        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2390        List<CmsGroup> result = null;
2391        try {
2392            result = m_driverManager.getGroupsOfUser(
2393                dbc,
2394                CmsOrganizationalUnit.removeLeadingSeparator(username),
2395                CmsOrganizationalUnit.removeLeadingSeparator(ouFqn),
2396                includeChildOus,
2397                readRoles,
2398                directGroupsOnly,
2399                remoteAddress);
2400        } catch (Exception e) {
2401            dbc.report(null, Messages.get().container(Messages.ERR_GET_GROUPS_OF_USER_2, username, remoteAddress), e);
2402        } finally {
2403            dbc.clear();
2404        }
2405        return result;
2406    }
2407
2408    /**
2409     * Returns the lock state of a resource.<p>
2410     *
2411     * @param context the current request context
2412     * @param resource the resource to return the lock state for
2413     *
2414     * @return the lock state of the resource
2415     *
2416     * @throws CmsException if something goes wrong
2417     */
2418    public CmsLock getLock(CmsRequestContext context, CmsResource resource) throws CmsException {
2419
2420        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2421        CmsLock result = null;
2422        try {
2423            result = m_driverManager.getLock(dbc, resource);
2424        } catch (Exception e) {
2425            dbc.report(null, Messages.get().container(Messages.ERR_GET_LOCK_1, context.getSitePath(resource)), e);
2426        } finally {
2427            dbc.clear();
2428        }
2429        return result;
2430    }
2431
2432    /**
2433     * Returns all locked resources in a given folder.<p>
2434     *
2435     * @param context the current request context
2436     * @param resource the folder to search in
2437     * @param filter the lock filter
2438     *
2439     * @return a list of locked resource paths (relative to current site)
2440     *
2441     * @throws CmsException if something goes wrong
2442     */
2443    public List<String> getLockedResources(CmsRequestContext context, CmsResource resource, CmsLockFilter filter)
2444    throws CmsException {
2445
2446        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2447        List<String> result = null;
2448        try {
2449            checkOfflineProject(dbc);
2450            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, false, CmsResourceFilter.ALL);
2451            result = m_driverManager.getLockedResources(dbc, resource, filter);
2452        } catch (Exception e) {
2453            dbc.report(
2454                null,
2455                Messages.get().container(Messages.ERR_COUNT_LOCKED_RESOURCES_FOLDER_1, context.getSitePath(resource)),
2456                e);
2457        } finally {
2458            dbc.clear();
2459        }
2460        return result;
2461    }
2462
2463    /**
2464     * Returns all locked resources in a given folder.<p>
2465     *
2466     * @param context the current request context
2467     * @param resource the folder to search in
2468     * @param filter the lock filter
2469     *
2470     * @return a list of locked resource paths (relative to current site)
2471     *
2472     * @throws CmsException if something goes wrong
2473     */
2474    public List<CmsResource> getLockedResourcesObjects(
2475        CmsRequestContext context,
2476        CmsResource resource,
2477        CmsLockFilter filter)
2478    throws CmsException {
2479
2480        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2481        List<CmsResource> result = null;
2482        try {
2483            checkOfflineProject(dbc);
2484            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, false, CmsResourceFilter.ALL);
2485            result = m_driverManager.getLockedResourcesObjects(dbc, resource, filter);
2486        } catch (Exception e) {
2487            dbc.report(
2488                null,
2489                Messages.get().container(Messages.ERR_COUNT_LOCKED_RESOURCES_FOLDER_1, context.getSitePath(resource)),
2490                e);
2491        } finally {
2492            dbc.clear();
2493        }
2494        return result;
2495    }
2496
2497    /**
2498     * Returns all locked resources in a given folder, but uses a cache for resource lookups.<p>
2499     *
2500     * @param context the current request context
2501     * @param resource the folder to search in
2502     * @param filter the lock filter
2503     * @param cache the cache to use
2504     *
2505     * @return a list of locked resource paths (relative to current site)
2506     *
2507     * @throws CmsException if something goes wrong
2508     */
2509    public List<CmsResource> getLockedResourcesObjectsWithCache(
2510        CmsRequestContext context,
2511        CmsResource resource,
2512        CmsLockFilter filter,
2513        Map<String, CmsResource> cache)
2514    throws CmsException {
2515
2516        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2517        List<CmsResource> result = null;
2518        try {
2519            checkOfflineProject(dbc);
2520            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, false, CmsResourceFilter.ALL);
2521            result = m_driverManager.getLockedResourcesObjectsWithCache(dbc, resource, filter, cache);
2522        } catch (Exception e) {
2523            dbc.report(
2524                null,
2525                Messages.get().container(Messages.ERR_COUNT_LOCKED_RESOURCES_FOLDER_1, context.getSitePath(resource)),
2526                e);
2527        } finally {
2528            dbc.clear();
2529        }
2530        return result;
2531    }
2532
2533    /**
2534     * Returns the lock manger.<p>
2535     *
2536     * @return the lock manager
2537     */
2538    public CmsLockManager getLockManager() {
2539
2540        return m_lockManager;
2541    }
2542
2543    /**
2544     * Returns all log entries matching the given filter.<p>
2545     *
2546     * @param context the current user context
2547     * @param filter the filter to match the log entries
2548     *
2549     * @return all log entries matching the given filter
2550     *
2551     * @throws CmsException if something goes wrong
2552     *
2553     * @see CmsObject#getLogEntries(CmsLogFilter)
2554     */
2555    public List<CmsLogEntry> getLogEntries(CmsRequestContext context, CmsLogFilter filter) throws CmsException {
2556
2557        List<CmsLogEntry> result = null;
2558        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2559        try {
2560            result = m_driverManager.getLogEntries(dbc, filter);
2561        } catch (Exception e) {
2562            dbc.report(null, Messages.get().container(Messages.ERR_READ_LOG_ENTRIES_0), e);
2563        } finally {
2564            dbc.clear();
2565        }
2566        return result;
2567    }
2568
2569    /**
2570     * Returns all resources of organizational units for which the current user has
2571     * the given role role.<p>
2572     *
2573     * @param context the current request context
2574     * @param role the role to check
2575     *
2576     * @return a list of {@link org.opencms.file.CmsResource} objects
2577     *
2578     * @throws CmsException if something goes wrong
2579     */
2580    public List<CmsResource> getManageableResources(CmsRequestContext context, CmsRole role) throws CmsException {
2581
2582        List<CmsResource> resources;
2583        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2584        try {
2585            resources = getManageableResources(dbc, role);
2586        } finally {
2587            dbc.clear();
2588        }
2589        return resources;
2590    }
2591
2592    /**
2593     * Returns all child organizational units of the given parent organizational unit including
2594     * hierarchical deeper organization units if needed.<p>
2595     *
2596     * @param context the current request context
2597     * @param parent the parent organizational unit
2598     * @param includeChildren if hierarchical deeper organization units should also be returned
2599     *
2600     * @return a list of <code>{@link CmsOrganizationalUnit}</code> objects
2601     *
2602     * @throws CmsException if operation was not successful
2603     *
2604     * @see org.opencms.security.CmsOrgUnitManager#getOrganizationalUnits(CmsObject, String, boolean)
2605     */
2606    public List<CmsOrganizationalUnit> getOrganizationalUnits(
2607        CmsRequestContext context,
2608        CmsOrganizationalUnit parent,
2609        boolean includeChildren)
2610    throws CmsException {
2611
2612        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2613        List<CmsOrganizationalUnit> result = null;
2614        try {
2615            result = m_driverManager.getOrganizationalUnits(dbc, parent, includeChildren);
2616        } catch (Exception e) {
2617            dbc.report(null, Messages.get().container(Messages.ERR_GET_ORGUNITS_1, parent.getName()), e);
2618        } finally {
2619            dbc.clear();
2620        }
2621        return result;
2622    }
2623
2624    /**
2625     * Returns all the organizational units for which the current user has the given role.<p>
2626     *
2627     * @param requestContext the current request context
2628     * @param role the role to check
2629     * @param includeSubOus if sub organizational units should be included in the search
2630     *
2631     * @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects
2632     *
2633     * @throws CmsException if something goes wrong
2634     */
2635    public List<CmsOrganizationalUnit> getOrgUnitsForRole(
2636        CmsRequestContext requestContext,
2637        CmsRole role,
2638        boolean includeSubOus)
2639    throws CmsException {
2640
2641        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
2642        List<CmsOrganizationalUnit> result = null;
2643        try {
2644            result = m_driverManager.getOrgUnitsForRole(dbc, role, includeSubOus);
2645        } catch (Exception e) {
2646            dbc.report(
2647                null,
2648                Messages.get().container(Messages.ERR_GET_ORGUNITS_ROLE_1, role.getName(requestContext.getLocale())),
2649                e);
2650        } finally {
2651            dbc.clear();
2652        }
2653        return result;
2654    }
2655
2656    /**
2657     * Returns the parent group of a group.<p>
2658     *
2659     * @param context the current request context
2660     * @param groupname the name of the group
2661     *
2662     * @return group the parent group or <code>null</code>
2663     *
2664     * @throws CmsException if operation was not successful
2665     */
2666    public CmsGroup getParent(CmsRequestContext context, String groupname) throws CmsException {
2667
2668        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2669        CmsGroup result = null;
2670        try {
2671            result = m_driverManager.getParent(dbc, CmsOrganizationalUnit.removeLeadingSeparator(groupname));
2672        } catch (Exception e) {
2673            dbc.report(null, Messages.get().container(Messages.ERR_GET_PARENT_GROUP_1, groupname), e);
2674        } finally {
2675            dbc.clear();
2676        }
2677        return result;
2678    }
2679
2680    /**
2681     * Returns the set of permissions of the current user for a given resource.<p>
2682     *
2683     * @param context the current request context
2684     * @param resource the resource
2685     * @param user the user
2686     *
2687     * @return bit set with allowed permissions
2688     *
2689     * @throws CmsException if something goes wrong
2690     */
2691    public CmsPermissionSetCustom getPermissions(CmsRequestContext context, CmsResource resource, CmsUser user)
2692    throws CmsException {
2693
2694        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2695        CmsPermissionSetCustom result = null;
2696        try {
2697            result = m_driverManager.getPermissions(dbc, resource, user);
2698        } catch (Exception e) {
2699            dbc.report(
2700                null,
2701                Messages.get().container(Messages.ERR_GET_PERMISSIONS_2, user.getName(), context.getSitePath(resource)),
2702                e);
2703        } finally {
2704            dbc.clear();
2705        }
2706        return result;
2707    }
2708
2709    /**
2710     * Returns the uuid id for the given id,
2711     * remove this method as soon as possible.<p>
2712     *
2713     * @param context the current cms context
2714     * @param id the old project id
2715     *
2716     * @return the new uuid for the given id
2717     *
2718     * @throws CmsException if something goes wrong
2719     */
2720    public CmsUUID getProjectId(CmsRequestContext context, int id) throws CmsException {
2721
2722        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2723        CmsUUID result = null;
2724        try {
2725            result = m_driverManager.getProjectId(dbc, id);
2726        } catch (CmsException e) {
2727            dbc.report(null, e.getMessageContainer(), e);
2728        } finally {
2729            dbc.clear();
2730        }
2731        return result;
2732    }
2733
2734    /**
2735     * Returns a new publish list that contains the unpublished resources related
2736     * to all resources in the given publish list, the related resources exclude
2737     * all resources in the given publish list and also locked (by other users) resources.<p>
2738     *
2739     * @param context the current cms context
2740     * @param publishList the publish list to exclude from result
2741     * @param filter the relation filter to use to get the related resources
2742     *
2743     * @return a new publish list that contains the related resources
2744     *
2745     * @throws CmsException if something goes wrong
2746     *
2747     * @see org.opencms.publish.CmsPublishManager#getRelatedResourcesToPublish(CmsObject, CmsPublishList)
2748     */
2749    public CmsPublishList getRelatedResourcesToPublish(
2750        CmsRequestContext context,
2751        CmsPublishList publishList,
2752        CmsRelationFilter filter)
2753    throws CmsException {
2754
2755        if (!publishList.isDirectPublish()) {
2756            throw new CmsIllegalArgumentException(
2757                Messages.get().container(Messages.ERR_GET_RELATED_RESOURCES_PUBLISH_PROJECT_0));
2758        }
2759
2760        CmsPublishList ret = null;
2761        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2762        try {
2763            ret = m_driverManager.getRelatedResourcesToPublish(dbc, publishList, filter);
2764            checkPublishPermissions(dbc, ret);
2765        } catch (Exception e) {
2766            dbc.report(
2767                null,
2768                Messages.get().container(
2769                    Messages.ERR_GET_RELATED_RESOURCES_PUBLISH_DIRECT_1,
2770                    CmsFileUtil.formatResourceNames(context, publishList.getDirectPublishResources())),
2771                e);
2772        } finally {
2773            dbc.clear();
2774        }
2775        return ret;
2776    }
2777
2778    /**
2779     * Returns all relations for the given resource matching the given filter.<p>
2780     *
2781     * @param context the current user context
2782     * @param resource the resource to retrieve the relations for
2783     * @param filter the filter to match the relation
2784     *
2785     * @return all {@link org.opencms.relations.CmsRelation} objects for the given resource matching the given filter
2786     *
2787     * @throws CmsException if something goes wrong
2788     *
2789     * @see CmsObject#getRelationsForResource(String, CmsRelationFilter)
2790     */
2791    public List<CmsRelation> getRelationsForResource(
2792        CmsRequestContext context,
2793        CmsResource resource,
2794        CmsRelationFilter filter)
2795    throws CmsException {
2796
2797        List<CmsRelation> result = null;
2798        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2799        try {
2800            // check the access permissions
2801            if (resource != null) {
2802                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_VIEW, false, CmsResourceFilter.ALL);
2803            }
2804            result = m_driverManager.getRelationsForResource(dbc, resource, filter);
2805        } catch (Exception e) {
2806            dbc.report(
2807                null,
2808                Messages.get().container(
2809                    Messages.ERR_READ_RELATIONS_1,
2810                    (resource != null) ? context.removeSiteRoot(resource.getRootPath()) : "null"),
2811                e);
2812        } finally {
2813            dbc.clear();
2814        }
2815        return result;
2816    }
2817
2818    /**
2819     * Returns all resources of the given organizational unit.<p>
2820     *
2821     * @param context the current request context
2822     * @param orgUnit the organizational unit to get all resources for
2823     *
2824     * @return all <code>{@link CmsResource}</code> objects in the organizational unit
2825     *
2826     * @throws CmsException if operation was not successful
2827     *
2828     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
2829     * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
2830     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
2831     */
2832    public List<CmsResource> getResourcesForOrganizationalUnit(CmsRequestContext context, CmsOrganizationalUnit orgUnit)
2833    throws CmsException {
2834
2835        List<CmsResource> result = null;
2836        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2837        try {
2838            result = m_driverManager.getResourcesForOrganizationalUnit(dbc, orgUnit);
2839        } catch (Exception e) {
2840            dbc.report(null, Messages.get().container(Messages.ERR_READ_ORGUNIT_RESOURCES_1, orgUnit.getName()), e);
2841        } finally {
2842            dbc.clear();
2843        }
2844        return result;
2845    }
2846
2847    /**
2848     * Returns all resources associated to a given principal via an ACE with the given permissions.<p>
2849     *
2850     * If the <code>includeAttr</code> flag is set it returns also all resources associated to
2851     * a given principal through some of following attributes.<p>
2852     *
2853     * <ul>
2854     *    <li>User Created</li>
2855     *    <li>User Last Modified</li>
2856     * </ul><p>
2857     *
2858     * @param context the current request context
2859     * @param principalId the id of the principal
2860     * @param permissions a set of permissions to match, can be <code>null</code> for all ACEs
2861     * @param includeAttr a flag to include resources associated by attributes
2862     *
2863     * @return a set of <code>{@link CmsResource}</code> objects
2864     *
2865     * @throws CmsException if something goes wrong
2866     */
2867    public Set<CmsResource> getResourcesForPrincipal(
2868        CmsRequestContext context,
2869        CmsUUID principalId,
2870        CmsPermissionSet permissions,
2871        boolean includeAttr)
2872    throws CmsException {
2873
2874        Set<CmsResource> dependencies;
2875        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2876        try {
2877            dependencies = m_driverManager.getResourcesForPrincipal(
2878                dbc,
2879                dbc.currentProject(),
2880                principalId,
2881                permissions,
2882                includeAttr);
2883        } catch (Exception e) {
2884            dbc.report(null, Messages.get().container(Messages.ERR_READ_RESOURCES_FOR_PRINCIPAL_LOG_1, principalId), e);
2885            dependencies = new HashSet<CmsResource>();
2886        } finally {
2887            dbc.clear();
2888        }
2889        return dependencies;
2890    }
2891
2892    /**
2893     * Gets the rewrite aliases matching a given filter.<p>
2894     *
2895     * @param requestContext the current request context
2896     * @param filter the filter used for selecting the rewrite aliases
2897     * @return the rewrite aliases matching the given filter
2898     *
2899     * @throws CmsException if something goes wrong
2900     */
2901    public List<CmsRewriteAlias> getRewriteAliases(CmsRequestContext requestContext, CmsRewriteAliasFilter filter)
2902    throws CmsException {
2903
2904        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
2905        try {
2906            return m_driverManager.getRewriteAliases(dbc, filter);
2907        } catch (Exception e) {
2908            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
2909            return null; // will never be executed
2910        } finally {
2911            dbc.clear();
2912        }
2913
2914    }
2915
2916    /**
2917     * Gets the groups which constitute a given role.<p>
2918     *
2919     * @param context the request context
2920     * @param role the role
2921     * @param directUsersOnly if true, only direct users of the role group will be returned
2922     *
2923     * @return the role's groups
2924     *
2925     * @throws CmsException if something goes wrong
2926     */
2927    public Set<CmsGroup> getRoleGroups(CmsRequestContext context, CmsRole role, boolean directUsersOnly)
2928    throws CmsException {
2929
2930        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2931        try {
2932            return m_driverManager.getRoleGroups(dbc, role.getGroupName(), directUsersOnly);
2933        } catch (Exception e) {
2934            dbc.report(null, Messages.get().container(Messages.ERR_GET_ROLE_GROUPS_1, role.toString()), e);
2935            return null; // will never be executed
2936        } finally {
2937            dbc.clear();
2938        }
2939    }
2940
2941    /**
2942     * Returns all roles the given user has for the given resource.<p>
2943     *
2944     * @param context the current request context
2945     * @param user the user to check
2946     * @param resource the resource to check the roles for
2947     *
2948     * @return a list of {@link CmsRole} objects
2949     *
2950     * @throws CmsException is something goes wrong
2951     */
2952    public List<CmsRole> getRolesForResource(CmsRequestContext context, CmsUser user, CmsResource resource)
2953    throws CmsException {
2954
2955        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2956        List<CmsRole> result = null;
2957        try {
2958            result = m_driverManager.getRolesForResource(dbc, user, resource);
2959        } catch (Exception e) {
2960            dbc.report(
2961                null,
2962                Messages.get().container(
2963                    Messages.ERR_GET_ROLES_FOR_RESOURCE_2,
2964                    user.getName(),
2965                    context.getSitePath(resource)),
2966                e);
2967        } finally {
2968            dbc.clear();
2969        }
2970        return result;
2971    }
2972
2973    /**
2974     * Returns an instance of the common sql manager.<p>
2975     *
2976     * @return an instance of the common sql manager
2977     */
2978    public CmsSqlManager getSqlManager() {
2979
2980        return m_driverManager.getSqlManager();
2981    }
2982
2983    /**
2984     * Returns a delivery-capable storage backend by its stable storage identifier.<p>
2985     *
2986     * @param context the current request context
2987     * @param storage the stable storage identifier
2988     *
2989     * @return the delivery-capable storage backend, or <code>null</code> if not supported
2990     *
2991     * @throws CmsException if something goes wrong
2992     */
2993    public I_CmsStorageDelivery getStoredContentDelivery(CmsRequestContext context, String storage)
2994    throws CmsException {
2995
2996        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
2997        try {
2998            return m_driverManager.getStoredContentDelivery(dbc, storage);
2999        } finally {
3000            dbc.clear();
3001        }
3002    }
3003
3004    /**
3005     * Returns all users of the given organizational unit.<p>
3006     *
3007     * @param context the current request context
3008     * @param orgUnit the organizational unit to get the users for
3009     * @param recursive if all users of sub-organizational units should be retrieved too
3010     *
3011     * @return all <code>{@link CmsUser}</code> objects in the organizational unit
3012     *
3013     * @throws CmsException if operation was not successful
3014     *
3015     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
3016     * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
3017     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
3018     */
3019    public List<CmsUser> getUsers(CmsRequestContext context, CmsOrganizationalUnit orgUnit, boolean recursive)
3020    throws CmsException {
3021
3022        List<CmsUser> result = null;
3023        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3024        try {
3025            result = m_driverManager.getUsers(dbc, orgUnit, recursive);
3026        } catch (Exception e) {
3027            dbc.report(null, Messages.get().container(Messages.ERR_READ_ORGUNIT_USERS_1, orgUnit.getName()), e);
3028        } finally {
3029            dbc.clear();
3030        }
3031        return result;
3032    }
3033
3034    /**
3035     * Returns a list of users in a group.<p>
3036     *
3037     * @param context the current request context
3038     * @param groupname the name of the group to list users from
3039     * @param includeOtherOuUsers include users of other organizational units
3040     * @param directUsersOnly if set only the direct assigned users will be returned,
3041     *                          if not also indirect users, ie. members of child groups
3042     * @param readRoles if to read roles or groups
3043     *
3044     * @return all <code>{@link CmsUser}</code> objects in the group
3045     *
3046     * @throws CmsException if operation was not successful
3047     */
3048    public List<CmsUser> getUsersOfGroup(
3049        CmsRequestContext context,
3050        String groupname,
3051        boolean includeOtherOuUsers,
3052        boolean directUsersOnly,
3053        boolean readRoles)
3054    throws CmsException {
3055
3056        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3057        List<CmsUser> result = null;
3058        try {
3059            result = m_driverManager.getUsersOfGroup(
3060                dbc,
3061                CmsOrganizationalUnit.removeLeadingSeparator(groupname),
3062                includeOtherOuUsers,
3063                directUsersOnly,
3064                readRoles);
3065        } catch (Exception e) {
3066            dbc.report(null, Messages.get().container(Messages.ERR_GET_USERS_OF_GROUP_1, groupname), e);
3067        } finally {
3068            dbc.clear();
3069        }
3070        return result;
3071    }
3072
3073    /**
3074     * Returns the current user's publish list.<p>
3075     *
3076     * @param context the request context
3077     *
3078     * @return the current user's publish list
3079     *
3080     * @throws CmsException if something goes wrong
3081     */
3082    public List<CmsResource> getUsersPubList(CmsRequestContext context) throws CmsException {
3083
3084        List<CmsResource> result = null;
3085        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3086        try {
3087            result = m_driverManager.getUsersPubList(dbc, context.getCurrentUser().getId());
3088        } catch (Exception e) {
3089            dbc.report(
3090                null,
3091                Messages.get().container(Messages.ERR_READ_USER_PUBLIST_1, context.getCurrentUser().getName()),
3092                e);
3093
3094        } finally {
3095            dbc.clear();
3096        }
3097        return result;
3098    }
3099
3100    /**
3101     * Returns all users of the given organizational unit.<p>
3102     *
3103     * @param context the current request context
3104     * @param orgUnit the organizational unit to get the users for
3105     * @param recursive if all users of sub-organizational units should be retrieved too
3106     *
3107     * @return all <code>{@link CmsUser}</code> objects in the organizational unit
3108     *
3109     * @throws CmsException if operation was not successful
3110     *
3111     * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String)
3112     * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean)
3113     * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean)
3114     */
3115    public List<CmsUser> getUsersWithoutAdditionalInfo(
3116        CmsRequestContext context,
3117        CmsOrganizationalUnit orgUnit,
3118        boolean recursive)
3119    throws CmsException {
3120
3121        List<CmsUser> result = null;
3122        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3123        try {
3124            result = m_driverManager.getUsersWithoutAdditionalInfo(dbc, orgUnit, recursive);
3125        } catch (Exception e) {
3126            dbc.report(null, Messages.get().container(Messages.ERR_READ_ORGUNIT_USERS_1, orgUnit.getName()), e);
3127        } finally {
3128            dbc.clear();
3129        }
3130        return result;
3131    }
3132
3133    /**
3134     * Performs a non-blocking permission check on a resource.<p>
3135     *
3136     * This test will not throw an exception in case the required permissions are not
3137     * available for the requested operation. Instead, it will return one of the
3138     * following values:<ul>
3139     * <li><code>{@link I_CmsPermissionHandler#PERM_ALLOWED}</code></li>
3140     * <li><code>{@link I_CmsPermissionHandler#PERM_FILTERED}</code></li>
3141     * <li><code>{@link I_CmsPermissionHandler#PERM_DENIED}</code></li></ul><p>
3142     *
3143     * @param context the current request context
3144     * @param resource the resource on which permissions are required
3145     * @param requiredPermissions the set of permissions required for the operation
3146     * @param checkLock if true, a lock for the current user is required for
3147     *      all write operations, if false it's ok to write as long as the resource
3148     *      is not locked by another user
3149     * @param filter the resource filter to use
3150     *
3151     * @return <code>{@link I_CmsPermissionHandler#PERM_ALLOWED}</code> if the user has sufficient permissions on the resource
3152     *      for the requested operation
3153     *
3154     * @throws CmsException in case of i/o errors (NOT because of insufficient permissions)
3155     *
3156     * @see #hasPermissions(CmsDbContext, CmsResource, CmsPermissionSet, LockCheck, CmsResourceFilter)
3157     */
3158    public I_CmsPermissionHandler.CmsPermissionCheckResult hasPermissions(
3159        CmsRequestContext context,
3160        CmsResource resource,
3161        CmsPermissionSet requiredPermissions,
3162        boolean checkLock,
3163        CmsResourceFilter filter)
3164    throws CmsException {
3165
3166        I_CmsPermissionHandler.CmsPermissionCheckResult result = null;
3167        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3168        try {
3169            result = hasPermissions(
3170                dbc,
3171                resource,
3172                requiredPermissions,
3173                checkLock ? LockCheck.yes : LockCheck.no,
3174                filter);
3175        } finally {
3176            dbc.clear();
3177        }
3178        return result;
3179    }
3180
3181    /**
3182     * Performs a non-blocking permission check on a resource.<p>
3183     *
3184     * This test will not throw an exception in case the required permissions are not
3185     * available for the requested operation. Instead, it will return one of the
3186     * following values:<ul>
3187     * <li><code>{@link I_CmsPermissionHandler#PERM_ALLOWED}</code></li>
3188     * <li><code>{@link I_CmsPermissionHandler#PERM_FILTERED}</code></li>
3189     * <li><code>{@link I_CmsPermissionHandler#PERM_DENIED}</code></li></ul><p>
3190     *
3191     * @param context the current request context
3192     * @param resource the resource on which permissions are required
3193     * @param requiredPermissions the set of permissions required for the operation
3194     * @param checkLock if true, a lock for the current user is required for
3195     *      all write operations, if false it's ok to write as long as the resource
3196     *      is not locked by another user
3197     * @param filter the resource filter to use
3198     *
3199     * @return <code>{@link I_CmsPermissionHandler#PERM_ALLOWED}</code> if the user has sufficient permissions on the resource
3200     *      for the requested operation
3201     *
3202     * @throws CmsException in case of i/o errors (NOT because of insufficient permissions)
3203     *
3204     * @see #hasPermissions(CmsDbContext, CmsResource, CmsPermissionSet, LockCheck, CmsResourceFilter)
3205     */
3206    public I_CmsPermissionHandler.CmsPermissionCheckResult hasPermissions(
3207        CmsRequestContext context,
3208        CmsResource resource,
3209        CmsPermissionSet requiredPermissions,
3210        LockCheck checkLock,
3211        CmsResourceFilter filter)
3212    throws CmsException {
3213
3214        I_CmsPermissionHandler.CmsPermissionCheckResult result = null;
3215        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3216        try {
3217            result = hasPermissions(dbc, resource, requiredPermissions, checkLock, filter);
3218        } finally {
3219            dbc.clear();
3220        }
3221        return result;
3222    }
3223
3224    /**
3225     * Checks if the given user has the given role in the given organizational unit.<p>
3226     *
3227     * If the organizational unit is <code>null</code>, this method will check if the
3228     * given user has the given role for at least one organizational unit.<p>
3229     *
3230     * @param dbc the current OpenCms users database context
3231     * @param user the user to check the role for
3232     * @param role the role to check
3233     *
3234     * @return <code>true</code> if the given user has the given role in the given organizational unit
3235     */
3236    public boolean hasRole(CmsDbContext dbc, CmsUser user, CmsRole role) {
3237
3238        // try to read from cache
3239        String key = role.getGroupName() + "," + role.getOuFqn();
3240        Boolean result = OpenCms.getMemoryMonitor().getGroupListCache().getHasRole(user.getId(), key);
3241        if (result != null) {
3242            return result.booleanValue();
3243        }
3244
3245        // read all roles of the current user
3246        List<CmsGroup> roles;
3247        try {
3248            roles = m_driverManager.getGroupsOfUser(
3249                dbc,
3250                user.getName(),
3251                "",
3252                true,
3253                true,
3254                false,
3255                dbc.getRequestContext().getRemoteAddress());
3256        } catch (CmsException e) {
3257            if (LOG.isErrorEnabled()) {
3258                LOG.error(e.getLocalizedMessage(), e);
3259            }
3260            // any exception: return false
3261            return false;
3262        }
3263
3264        boolean hasRole = hasRole(role, roles);
3265
3266        // hack: require individual user based confirmation for certain roles
3267        // this is for updated older systems where content managers have been WORKPLACE_USER only
3268        // to prevent access to certain ADE management functions
3269        if (hasRole && ((CmsRole.CATEGORY_EDITOR.equals(role)) || (CmsRole.GALLERY_EDITOR.equals(role)))) {
3270            String info = CmsRole.CONFIRM_ROLE_PREFIX + role.getRoleName();
3271            Object prop = OpenCms.getRuntimeProperty(info);
3272            if ((prop != null) && Boolean.valueOf(prop.toString()).booleanValue()) {
3273                // individual user based confirmation for the role is required
3274                // if the user is a WORKPLACE_USER
3275                Object val = user.getAdditionalInfo(info);
3276                if ((val == null) || !Boolean.valueOf(val.toString()).booleanValue()) {
3277                    // no individual user confirmation present
3278                    if (hasRole(CmsRole.WORKPLACE_USER, roles)
3279                        && !hasRole(CmsRole.DEVELOPER, roles)
3280                        && !hasRole(CmsRole.PROJECT_MANAGER, roles)
3281                        && !hasRole(CmsRole.ACCOUNT_MANAGER, roles)) {
3282                        // user is a WORKPLACE_USER, confirmation is required but not present
3283                        hasRole = false;
3284                    }
3285                }
3286            }
3287        }
3288
3289        result = Boolean.valueOf(hasRole);
3290        OpenCms.getMemoryMonitor().getGroupListCache().setHasRole(user, key, result);
3291        return result.booleanValue();
3292    }
3293
3294    /**
3295     * Checks if the given user has the given role in the given organizational unit.<p>
3296     *
3297     * If the organizational unit is <code>null</code>, this method will check if the
3298     * given user has the given role for at least one organizational unit.<p>
3299     *
3300     * @param context the current request context
3301     * @param user the user to check the role for
3302     * @param role the role to check
3303     *
3304     * @return <code>true</code> if the given user has the given role in the given organizational unit
3305     */
3306    public boolean hasRole(CmsRequestContext context, CmsUser user, CmsRole role) {
3307
3308        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3309        boolean result;
3310        try {
3311            result = hasRole(dbc, user, role);
3312        } finally {
3313            dbc.clear();
3314        }
3315        return result;
3316    }
3317
3318    /**
3319     * Checks if the given user has the given role for the given resource.<p>
3320     *
3321     * @param dbc the current OpenCms users database context
3322     * @param user the user to check the role for
3323     * @param role the role to check
3324     * @param resource the resource to check the role for
3325     *
3326     * @return <code>true</code> if the given user has the given role for the given resource
3327     */
3328    public boolean hasRoleForResource(CmsDbContext dbc, CmsUser user, CmsRole role, CmsResource resource) {
3329
3330        // guest user has no role
3331        if (user.isGuestUser()) {
3332            return false;
3333        }
3334
3335        // try to read from cache
3336
3337        // ***
3338        // NOTE: We do intentionally *not* use the new group list cache here, as we have no good way to limit it at the moment, and this might generate a large amount of entries
3339        // ***
3340        String key = user.getId().toString() + role.getGroupName() + resource.getRootPath();
3341        Boolean result = OpenCms.getMemoryMonitor().getCachedRole(key);
3342        if (result != null) {
3343            return result.booleanValue();
3344        }
3345
3346        // read all roles of the current user
3347        List<CmsGroup> roles;
3348        try {
3349            roles = new ArrayList<CmsGroup>(
3350                m_driverManager.getGroupsOfUser(
3351                    dbc,
3352                    user.getName(),
3353                    "",
3354                    true,
3355                    true,
3356                    true,
3357                    dbc.getRequestContext().getRemoteAddress()));
3358        } catch (CmsException e) {
3359            if (LOG.isErrorEnabled()) {
3360                LOG.error(e.getLocalizedMessage(), e);
3361            }
3362            // any exception: return false
3363            return false;
3364        }
3365
3366        // first check the user has the role at all
3367        if (!hasRole(role.forOrgUnit(null), roles)) {
3368            result = Boolean.FALSE;
3369        }
3370
3371        // then check if one applies to the given resource
3372        Iterator<CmsGroup> it = roles.iterator();
3373        while ((result == null) && it.hasNext()) {
3374            CmsGroup group = it.next();
3375            CmsRole givenRole = CmsRole.valueOf(group);
3376            if (hasRole(role.forOrgUnit(null), Collections.singletonList(group))) {
3377                // we have the same role, now check the resource if needed
3378                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(givenRole.getOuFqn())) {
3379                    List<String> ouResourcePaths = OpenCms.getMemoryMonitor().getCachedOrgUnitResources(
3380                        givenRole.getOuFqn());
3381                    if (ouResourcePaths == null) {
3382                        try {
3383                            CmsOrganizationalUnit orgUnit = m_driverManager.readOrganizationalUnit(
3384                                dbc,
3385                                givenRole.getOuFqn());
3386                            List<CmsResource> ouResources = m_driverManager.getResourcesForOrganizationalUnit(
3387                                dbc,
3388                                orgUnit);
3389                            ouResourcePaths = ouResources.stream().map(res -> res.getRootPath()).collect(
3390                                Collectors.toList());
3391                            OpenCms.getMemoryMonitor().cacheOrgUnitResources(givenRole.getOuFqn(), ouResourcePaths);
3392                        } catch (Exception e) {
3393                            LOG.error(e);
3394                        }
3395                    }
3396                    if (ouResourcePaths != null) {
3397                        for (String ouResource : ouResourcePaths) {
3398                            if (resource.getRootPath().startsWith(ouResource)) {
3399                                result = Boolean.TRUE;
3400                                break;
3401                            }
3402                        }
3403                    }
3404
3405                } else {
3406                    result = Boolean.TRUE;
3407                }
3408            }
3409        }
3410
3411        if (result == null) {
3412            result = Boolean.FALSE;
3413        }
3414        OpenCms.getMemoryMonitor().cacheRole(key, result.booleanValue());
3415        return result.booleanValue();
3416    }
3417
3418    /**
3419     * Checks if the given user has the given role for the given resource.<p>
3420     *
3421     * @param context the current request context
3422     * @param user the user to check
3423     * @param role the role to check
3424     * @param resource the resource to check the role for
3425     *
3426     * @return <code>true</code> if the given user has the given role for the given resource
3427     */
3428    public boolean hasRoleForResource(CmsRequestContext context, CmsUser user, CmsRole role, CmsResource resource) {
3429
3430        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3431        boolean result;
3432        try {
3433            result = hasRoleForResource(dbc, user, role, resource);
3434        } finally {
3435            dbc.clear();
3436        }
3437        return result;
3438    }
3439
3440    /**
3441     * Writes a list of access control entries as new access control entries of a given resource.<p>
3442     *
3443     * Already existing access control entries of this resource are removed before.<p>
3444     *
3445     * Access is granted, if:<p>
3446     * <ul>
3447     * <li>the current user has control permission on the resource</li>
3448     * </ul><p>
3449     *
3450     * @param context the current request context
3451     * @param resource the resource
3452     * @param acEntries a list of <code>{@link CmsAccessControlEntry}</code> objects
3453     *
3454     * @throws CmsException if something goes wrong
3455     * @throws CmsSecurityException if the required permissions are not satisfied
3456     */
3457    public void importAccessControlEntries(
3458        CmsRequestContext context,
3459        CmsResource resource,
3460        List<CmsAccessControlEntry> acEntries)
3461    throws CmsException, CmsSecurityException {
3462
3463        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
3464            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3465            try {
3466                checkOfflineProject(dbc);
3467                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_CONTROL, true, CmsResourceFilter.ALL);
3468                m_driverManager.importAccessControlEntries(dbc, resource, acEntries);
3469                modContext.add(resource);
3470            } catch (Exception e) {
3471                dbc.report(
3472                    null,
3473                    Messages.get().container(Messages.ERR_IMPORT_ACL_ENTRIES_1, context.getSitePath(resource)),
3474                    e);
3475            } finally {
3476                dbc.clear();
3477            }
3478        }
3479    }
3480
3481    /**
3482     * Creates a new resource with the provided content and properties.<p>
3483     *
3484     * The <code>content</code> parameter may be null if the resource id already exists.
3485     * If so, the created resource will be made a sibling of the existing resource,
3486     * the existing content will remain unchanged.
3487     * This is used during file import for import of siblings as the
3488     * <code>manifest.xml</code> only contains one binary copy per file.
3489     * If the resource id exists but the <code>content</code> is not null,
3490     * the created resource will be made a sibling of the existing resource,
3491     * and both will share the new content.<p>
3492     *
3493     * @param context the current request context
3494     * @param resourcePath the name of the resource to create (full path)
3495     * @param resource the new resource to create
3496     * @param content the content for the new resource
3497     * @param properties the properties for the new resource
3498     * @param importCase if <code>true</code>, signals that this operation is done while importing resource,
3499     *                   causing different lock behavior and potential "lost and found" usage
3500     *
3501     * @return the created resource
3502     *
3503     * @throws CmsException if something goes wrong
3504     */
3505    public CmsResource importResource(
3506        CmsRequestContext context,
3507        String resourcePath,
3508        CmsResource resource,
3509        byte[] content,
3510        List<CmsProperty> properties,
3511        boolean importCase)
3512    throws CmsException {
3513
3514        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
3515            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3516            CmsResource newResource = null;
3517            try {
3518                checkOfflineProject(dbc);
3519                newResource = m_driverManager.createResource(
3520                    dbc,
3521                    resourcePath,
3522                    resource,
3523                    content,
3524                    properties,
3525                    importCase);
3526                modContext.add(newResource);
3527            } catch (Exception e) {
3528                dbc.report(
3529                    null,
3530                    Messages.get().container(
3531                        Messages.ERR_IMPORT_RESOURCE_2,
3532                        context.getSitePath(resource),
3533                        resourcePath),
3534                    e);
3535            } finally {
3536                dbc.clear();
3537            }
3538            return newResource;
3539        }
3540    }
3541
3542    /**
3543     * Imports a rewrite alias.<p>
3544     *
3545     * @param requestContext the current request context
3546     * @param siteRoot the site root
3547     * @param source the rewrite alias source
3548     * @param target the rewrite alias target
3549     * @param mode the alias mode
3550     * @return the import result
3551     *
3552     * @throws CmsException if something goes wrong
3553     */
3554    public CmsAliasImportResult importRewriteAlias(
3555        CmsRequestContext requestContext,
3556        String siteRoot,
3557        String source,
3558        String target,
3559        CmsAliasMode mode)
3560    throws CmsException {
3561
3562        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
3563        try {
3564            return m_driverManager.importRewriteAlias(dbc, siteRoot, source, target, mode);
3565        } catch (Exception e) {
3566            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
3567            return null;
3568        } finally {
3569            dbc.clear();
3570        }
3571    }
3572
3573    /**
3574     * Creates a new user by import.<p>
3575     *
3576     * @param context the current request context
3577     * @param id the id of the user
3578     * @param name the new name for the user
3579     * @param password the new password for the user
3580     * @param firstname the first name of the user
3581     * @param lastname the last name of the user
3582     * @param email the email of the user
3583     * @param flags the flags for a user (for example <code>{@link I_CmsPrincipal#FLAG_ENABLED}</code>)
3584     * @param dateCreated the creation date
3585     * @param additionalInfos the additional user infos
3586     *
3587     * @return the imported user
3588     *
3589     * @throws CmsException if something goes wrong
3590     * @throws CmsRoleViolationException if the  role {@link CmsRole#ACCOUNT_MANAGER} is not owned by the current user.
3591     */
3592    public CmsUser importUser(
3593        CmsRequestContext context,
3594        String id,
3595        String name,
3596        String password,
3597        String firstname,
3598        String lastname,
3599        String email,
3600        int flags,
3601        long dateCreated,
3602        Map<String, Object> additionalInfos)
3603    throws CmsException, CmsRoleViolationException {
3604
3605        CmsUser newUser = null;
3606
3607        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3608
3609        try {
3610            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(name)));
3611            newUser = m_driverManager.importUser(
3612                dbc,
3613                id,
3614                CmsOrganizationalUnit.removeLeadingSeparator(name),
3615                password,
3616                firstname,
3617                lastname,
3618                email,
3619                flags,
3620                dateCreated,
3621                additionalInfos);
3622
3623        } catch (Exception e) {
3624            dbc.report(
3625                null,
3626                Messages.get().container(
3627                    Messages.ERR_IMPORT_USER_7,
3628                    new Object[] {
3629                        name,
3630                        firstname,
3631                        lastname,
3632                        email,
3633                        Integer.valueOf(flags),
3634                        new Date(dateCreated),
3635                        additionalInfos}),
3636                e);
3637        } finally {
3638            dbc.clear();
3639        }
3640
3641        return newUser;
3642    }
3643
3644    /**
3645     * Increments a counter and returns its old value.<p>
3646     *
3647     * @param context the request context
3648     * @param name the name of the counter
3649     *
3650     * @return the value of the counter before incrementing
3651     *
3652     * @throws CmsException if something goes wrong
3653     */
3654    public int incrementCounter(CmsRequestContext context, String name) throws CmsException {
3655
3656        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3657        try {
3658            return m_driverManager.incrementCounter(dbc, name);
3659        } catch (Exception e) {
3660            dbc.report(null, Messages.get().container(Messages.ERR_INCREMENT_COUNTER_1, name), e);
3661            return -1; // will never be reached
3662        } finally {
3663            dbc.clear();
3664        }
3665    }
3666
3667    /**
3668     * Initializes this security manager with a given runtime info factory.<p>
3669     *
3670     * @param configurationManager the configurationManager
3671     * @param dbContextFactory the initialized OpenCms runtime info factory
3672     * @param publishEngine the publish engine
3673     *
3674     * @throws CmsInitException if the initialization fails
3675     */
3676    public void init(
3677        CmsConfigurationManager configurationManager,
3678        I_CmsDbContextFactory dbContextFactory,
3679        CmsPublishEngine publishEngine)
3680    throws CmsInitException {
3681
3682        if (dbContextFactory == null) {
3683            throw new CmsInitException(
3684                org.opencms.main.Messages.get().container(org.opencms.main.Messages.ERR_CRITICAL_NO_DB_CONTEXT_0));
3685        }
3686
3687        m_dbContextFactory = dbContextFactory;
3688
3689        CmsSystemConfiguration systemConfiguration = (CmsSystemConfiguration)configurationManager.getConfiguration(
3690            CmsSystemConfiguration.class);
3691
3692        // create the driver manager
3693        m_driverManager = CmsDriverManager.newInstance(configurationManager, this, dbContextFactory, publishEngine);
3694
3695        try {
3696            // invoke the init method of the driver manager
3697            m_driverManager.init(configurationManager, dbContextFactory);
3698            if (CmsLog.INIT.isInfoEnabled()) {
3699                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE4_OK_0));
3700            }
3701        } catch (Exception exc) {
3702            CmsMessageContainer message = Messages.get().container(Messages.LOG_ERR_DRIVER_MANAGER_START_0);
3703            if (LOG.isFatalEnabled()) {
3704                LOG.fatal(message.key(), exc);
3705            }
3706            throw new CmsInitException(message, exc);
3707        }
3708
3709        // create a new lock manager
3710        m_lockManager = m_driverManager.getLockManager();
3711
3712        // initialize the permission handler
3713        String permHandlerClassName = systemConfiguration.getPermissionHandler();
3714        if (permHandlerClassName == null) {
3715            // use default implementation
3716            m_permissionHandler = new CmsDefaultPermissionHandler();
3717        } else {
3718            // use configured permission handler
3719            try {
3720                m_permissionHandler = (I_CmsPermissionHandler)Class.forName(permHandlerClassName).newInstance();
3721            } catch (Exception e) {
3722                throw new CmsInitException(
3723                    org.opencms.main.Messages.get().container(
3724                        org.opencms.main.Messages.ERR_CRITICAL_CLASS_CREATION_1,
3725                        permHandlerClassName),
3726                    e);
3727            }
3728        }
3729
3730        m_permissionHandler.init(m_driverManager, systemConfiguration);
3731
3732        if (CmsLog.INIT.isInfoEnabled()) {
3733            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SECURITY_MANAGER_INIT_0));
3734        }
3735    }
3736
3737    /**
3738     * Initializes the default groups for an organizational unit.<p>
3739     *
3740     * @param context the request context
3741     * @param ou the organizational unit
3742     */
3743    public void initializeOrgUnit(CmsRequestContext context, CmsOrganizationalUnit ou) {
3744
3745        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3746        m_driverManager.initOrgUnit(dbc, ou);
3747
3748    }
3749
3750    /**
3751     * Checks if the specified resource is inside the current project.<p>
3752     *
3753     * The project "view" is determined by a set of path prefixes.
3754     * If the resource starts with any one of this prefixes, it is considered to
3755     * be "inside" the project.<p>
3756     *
3757     * @param context the current request context
3758     * @param resourcename the specified resource name (full path)
3759     *
3760     * @return <code>true</code>, if the specified resource is inside the current project
3761     */
3762    public boolean isInsideCurrentProject(CmsRequestContext context, String resourcename) {
3763
3764        boolean result = false;
3765        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3766        try {
3767            result = m_driverManager.isInsideCurrentProject(dbc, resourcename);
3768        } finally {
3769            dbc.clear();
3770        }
3771        return result;
3772    }
3773
3774    /**
3775     * Checks if the current user has management access to the current project.<p>
3776     *
3777     * @param context the current request context
3778     *
3779     * @return <code>true</code>, if the user has management access to the current project
3780     */
3781    public boolean isManagerOfProject(CmsRequestContext context) {
3782
3783        try {
3784            return getAllManageableProjects(
3785                context,
3786                readOrganizationalUnit(context, context.getCurrentProject().getOuFqn()),
3787                false).contains(context.getCurrentProject());
3788        } catch (CmsException e) {
3789            // should never happen
3790            if (LOG.isErrorEnabled()) {
3791                LOG.error(e.getLocalizedMessage(), e);
3792            }
3793            return false;
3794        }
3795    }
3796
3797    /**
3798     * Checks whether the subscription driver is available.<p>
3799     *
3800     * @return true if the subscription driver is available
3801     */
3802    public boolean isSubscriptionDriverAvailable() {
3803
3804        return m_driverManager.isSubscriptionDriverAvailable();
3805    }
3806
3807    /**
3808     * Locks a resource.<p>
3809     *
3810     * The <code>type</code> parameter controls what kind of lock is used.<br>
3811     * Possible values for this parameter are: <br>
3812     * <ul>
3813     * <li><code>{@link org.opencms.lock.CmsLockType#EXCLUSIVE}</code></li>
3814     * <li><code>{@link org.opencms.lock.CmsLockType#TEMPORARY}</code></li>
3815     * <li><code>{@link org.opencms.lock.CmsLockType#PUBLISH}</code></li>
3816     * </ul><p>
3817     *
3818     * @param context the current request context
3819     * @param resource the resource to lock
3820     * @param type type of the lock
3821     *
3822     * @throws CmsException if something goes wrong
3823     *
3824     * @see CmsObject#lockResource(String)
3825     * @see CmsObject#lockResourceTemporary(String)
3826     * @see org.opencms.file.types.I_CmsResourceType#lockResource(CmsObject, CmsSecurityManager, CmsResource, CmsLockType)
3827     */
3828    public void lockResource(CmsRequestContext context, CmsResource resource, CmsLockType type) throws CmsException {
3829
3830        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3831        try {
3832            checkOfflineProject(dbc);
3833            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL);
3834            m_driverManager.lockResource(dbc, resource, type);
3835        } catch (Exception e) {
3836            CmsMessageContainer messageContainer;
3837            if (e instanceof CmsLockException) {
3838                messageContainer = ((CmsLockException)e).getMessageContainer();
3839            } else {
3840                messageContainer = Messages.get().container(
3841                    Messages.ERR_LOCK_RESOURCE_2,
3842                    context.getSitePath(resource),
3843                    type.toString());
3844            }
3845            dbc.report(null, messageContainer, e);
3846        } finally {
3847            dbc.clear();
3848        }
3849    }
3850
3851    /**
3852     * Attempts to authenticate a user into OpenCms with the given password.<p>
3853     *
3854     * @param context the current request context
3855     * @param username the name of the user to be logged in
3856     * @param password the password of the user
3857     * @param code the additional login information for 2FA
3858     * @param remoteAddress the ip address of the request
3859     *
3860     * @return the logged in user
3861     *
3862     * @throws CmsException if the login was not successful
3863     */
3864    public CmsUser loginUser(
3865        CmsRequestContext context,
3866        String username,
3867        String password,
3868        CmsSecondFactorInfo code,
3869        String remoteAddress)
3870    throws CmsException {
3871
3872        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3873        CmsUser result = null;
3874        try {
3875            result = m_driverManager.loginUser(
3876                dbc,
3877                CmsOrganizationalUnit.removeLeadingSeparator(username),
3878                password,
3879                code,
3880                remoteAddress,
3881                CmsDriverManager.LoginUserMode.standard);
3882        } finally {
3883            dbc.clear();
3884        }
3885        return result;
3886    }
3887
3888    /**
3889     * Lookup and read the user or group with the given UUID.<p>
3890     *
3891     * @param context the current request context
3892     * @param principalId the UUID of the principal to lookup
3893     *
3894     * @return the principal (group or user) if found, otherwise <code>null</code>
3895     */
3896    public I_CmsPrincipal lookupPrincipal(CmsRequestContext context, CmsUUID principalId) {
3897
3898        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3899        I_CmsPrincipal result = null;
3900        try {
3901            result = m_driverManager.lookupPrincipal(dbc, principalId);
3902        } finally {
3903            dbc.clear();
3904        }
3905        return result;
3906    }
3907
3908    /**
3909     * Lookup and read the user or group with the given name.<p>
3910     *
3911     * @param context the current request context
3912     * @param principalName the name of the principal to lookup
3913     *
3914     * @return the principal (group or user) if found, otherwise <code>null</code>
3915     */
3916    public I_CmsPrincipal lookupPrincipal(CmsRequestContext context, String principalName) {
3917
3918        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3919        I_CmsPrincipal result = null;
3920        try {
3921            result = m_driverManager.lookupPrincipal(dbc, CmsOrganizationalUnit.removeLeadingSeparator(principalName));
3922        } finally {
3923            dbc.clear();
3924        }
3925        return result;
3926    }
3927
3928    /**
3929     * Mark the given resource as visited by the user.<p>
3930     *
3931     * @param context the request context
3932     * @param poolName the name of the database pool to use
3933     * @param resource the resource to mark as visited
3934     * @param user the user that visited the resource
3935     *
3936     * @throws CmsException if something goes wrong
3937     */
3938    public void markResourceAsVisitedBy(CmsRequestContext context, String poolName, CmsResource resource, CmsUser user)
3939    throws CmsException {
3940
3941        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3942        try {
3943            m_driverManager.markResourceAsVisitedBy(dbc, poolName, resource, user);
3944        } catch (Exception e) {
3945            dbc.report(
3946                null,
3947                Messages.get().container(
3948                    Messages.ERR_MARK_RESOURCE_AS_VISITED_2,
3949                    context.getSitePath(resource),
3950                    user.getName()),
3951                e);
3952        } finally {
3953            dbc.clear();
3954        }
3955    }
3956
3957    /**
3958     * Returns a new publish list that contains all resources of both given publish lists.<p>
3959     *
3960     * @param context the current request context
3961     * @param pubList1 the first publish list
3962     * @param pubList2 the second publish list
3963     *
3964     * @return a new publish list that contains all resources of both given publish lists
3965     *
3966     * @throws CmsException if something goes wrong
3967     *
3968     * @see org.opencms.publish.CmsPublishManager#mergePublishLists(CmsObject, CmsPublishList, CmsPublishList)
3969     */
3970    public CmsPublishList mergePublishLists(CmsRequestContext context, CmsPublishList pubList1, CmsPublishList pubList2)
3971    throws CmsException {
3972
3973        CmsPublishList ret = null;
3974        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
3975        try {
3976            // get all resources from the first list
3977            Set<CmsResource> publishResources = new HashSet<CmsResource>(pubList1.getAllResources());
3978            // get all resources from the second list
3979            publishResources.addAll(pubList2.getAllResources());
3980
3981            // create merged publish list
3982            ret = new CmsPublishList(
3983                pubList1.getDirectPublishResources(),
3984                pubList1.isPublishSiblings(),
3985                pubList1.isPublishSubResources());
3986            ret.addAll(publishResources, false); // ignore files that should not be published
3987            if (pubList1.isUserPublishList()) {
3988                ret.setUserPublishList(true);
3989            }
3990            ret.initialize(); // ensure sort order
3991
3992            checkPublishPermissions(dbc, ret);
3993        } catch (Exception e) {
3994            dbc.report(null, Messages.get().container(Messages.ERR_MERGING_PUBLISH_LISTS_0), e);
3995        } finally {
3996            dbc.clear();
3997        }
3998        return ret;
3999    }
4000
4001    /**
4002     * Moves a resource.<p>
4003     *
4004     * You must ensure that the destination path is an absolute, valid and
4005     * existing VFS path. Relative paths from the source are currently not supported.<p>
4006     *
4007     * The moved resource will always be locked to the current user
4008     * after the move operation.<p>
4009     *
4010     * In case the target resource already exists, it is overwritten with the
4011     * source resource.<p>
4012     *
4013     * @param context the current request context
4014     * @param source the resource to copy
4015     * @param destination the name of the copy destination with complete path
4016     *
4017     * @throws CmsException if something goes wrong
4018     * @throws CmsSecurityException if resource could not be copied
4019     *
4020     * @see CmsObject#moveResource(String, String)
4021     * @see org.opencms.file.types.I_CmsResourceType#moveResource(CmsObject, CmsSecurityManager, CmsResource, String)
4022     */
4023    public void moveResource(CmsRequestContext context, CmsResource source, String destination)
4024    throws CmsException, CmsSecurityException {
4025
4026        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
4027            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4028            try {
4029                checkOfflineProject(dbc);
4030                // checking if the destination folder exists and is not marked as deleted
4031                readResource(context, CmsResource.getParentFolder(destination), CmsResourceFilter.IGNORE_EXPIRATION);
4032                checkPermissions(dbc, source, CmsPermissionSet.ACCESS_READ, true, CmsResourceFilter.ALL);
4033                checkPermissions(dbc, source, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
4034
4035                checkSystemLocks(dbc, source);
4036                if (CmsModificationContext.isInOnlineFolder(
4037                    source.getRootPath()) != CmsModificationContext.isInOnlineFolder(destination)) {
4038                    List<CmsResource> filesToCheck = null;
4039                    if (source.isFolder()) {
4040                        filesToCheck = readResources(context, source, CmsResourceFilter.ALL.addRequireFile(), true);
4041                    } else {
4042                        filesToCheck = Collections.singletonList(source);
4043                    }
4044                    for (CmsResource file : filesToCheck) {
4045                        if (file.getSiblingCount() > 1) {
4046                            throw new CmsVfsException(
4047                                Messages.get().container(Messages.ERR_MOVE_SIBLING_FROM_OR_TO_ONLINE_FOLDER_0));
4048                        }
4049                    }
4050                }
4051
4052                // check write permissions for subresources in case of moving a folder
4053                if (source.isFolder()) {
4054                    dbc.getRequestContext().setAttribute(I_CmsVfsDriver.REQ_ATTR_CHECK_PERMISSIONS, Boolean.TRUE);
4055                    try {
4056                        m_driverManager.getVfsDriver(
4057                            dbc).moveResource(dbc, dbc.currentProject().getUuid(), source, destination);
4058                    } catch (CmsDataAccessException e) {
4059                        // unwrap the permission violation exception
4060                        if (e.getCause() instanceof CmsPermissionViolationException) {
4061                            throw (CmsPermissionViolationException)e.getCause();
4062                        } else {
4063                            throw e;
4064                        }
4065                    }
4066                    dbc.getRequestContext().removeAttribute(I_CmsVfsDriver.REQ_ATTR_CHECK_PERMISSIONS);
4067                }
4068                Set<CmsResource> allMovedResources = new HashSet<>();
4069                moveResource(dbc, source, destination, allMovedResources);
4070                if (!dbc.currentProject().isOnlineProject()) {
4071                    for (CmsResource movedResource : allMovedResources) {
4072                        m_driverManager.repairCategories(
4073                            dbc,
4074                            dbc.getRequestContext().getCurrentProject().getUuid(),
4075                            movedResource);
4076                    }
4077                }
4078
4079            } catch (Exception e) {
4080                dbc.report(
4081                    null,
4082                    Messages.get().container(
4083                        Messages.ERR_MOVE_RESOURCE_2,
4084                        dbc.removeSiteRoot(source.getRootPath()),
4085                        dbc.removeSiteRoot(destination)),
4086                    e);
4087            } finally {
4088                dbc.clear();
4089            }
4090        }
4091    }
4092
4093    /**
4094     * Moves a resource to the "lost and found" folder.<p>
4095     *
4096     * The method can also be used to check get the name of a resource
4097     * in the "lost and found" folder only without actually moving the
4098     * the resource. To do this, the <code>returnNameOnly</code> flag
4099     * must be set to <code>true</code>.<p>
4100     *
4101     * In general, it is the same name as the given resource has, the only exception is
4102     * if a resource in the "lost and found" folder with the same name already exists.
4103     * In such case, a counter is added to the resource name.<p>
4104     *
4105     * @param context the current request context
4106     * @param resource the resource to apply this operation to
4107     * @param returnNameOnly if <code>true</code>, only the name of the resource in the "lost and found"
4108     *        folder is returned, the move operation is not really performed
4109     *
4110     * @return the name of the resource inside the "lost and found" folder
4111     *
4112     * @throws CmsException if something goes wrong
4113     *
4114     * @see CmsObject#moveToLostAndFound(String)
4115     * @see CmsObject#getLostAndFoundName(String)
4116     */
4117    public String moveToLostAndFound(CmsRequestContext context, CmsResource resource, boolean returnNameOnly)
4118    throws CmsException {
4119
4120        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4121        String result = null;
4122        try {
4123            checkOfflineProject(dbc);
4124            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, true, CmsResourceFilter.ALL);
4125            if (!returnNameOnly) {
4126                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
4127            }
4128            result = m_driverManager.moveToLostAndFound(dbc, resource, returnNameOnly);
4129        } catch (Exception e) {
4130            dbc.report(
4131                null,
4132                Messages.get().container(
4133                    Messages.ERR_MOVE_TO_LOST_AND_FOUND_1,
4134                    dbc.removeSiteRoot(resource.getRootPath())),
4135                e);
4136        } finally {
4137            dbc.clear();
4138        }
4139        return result;
4140    }
4141
4142    /**
4143     * Publishes the resources of a specified publish list.<p>
4144     *
4145     * @param cms the current request context
4146     * @param publishList a publish list
4147     * @param report an instance of <code>{@link I_CmsReport}</code> to print messages
4148     *
4149     * @return the publish history id of the published project
4150     *
4151     * @throws CmsException if something goes wrong
4152     *
4153     * @see #fillPublishList(CmsRequestContext, CmsPublishList)
4154     */
4155    public CmsUUID publishProject(CmsObject cms, CmsPublishList publishList, I_CmsReport report) throws CmsException {
4156
4157        CmsRequestContext context = cms.getRequestContext();
4158        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4159        try {
4160            // check if the current user has the required publish permissions
4161            checkPublishPermissions(dbc, publishList);
4162            m_driverManager.publishProject(cms, dbc, publishList, report);
4163        } finally {
4164            dbc.clear();
4165        }
4166        return publishList.getPublishHistoryId();
4167    }
4168
4169    /**
4170     * Reads the alias with a given path in a given site.<p>
4171     *
4172     * @param context the current request context
4173     * @param siteRoot the site root
4174     * @param path the site relative alias path
4175     * @return the alias for the path, or null if no such alias exists
4176     *
4177     * @throws CmsException if something goes wrong
4178     */
4179    public CmsAlias readAliasByPath(CmsRequestContext context, String siteRoot, String path) throws CmsException {
4180
4181        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4182        try {
4183            CmsAlias alias = m_driverManager.readAliasByPath(dbc, context.getCurrentProject(), siteRoot, path);
4184            return alias;
4185        } catch (Exception e) {
4186            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
4187            return null; // will never be executed
4188        } finally {
4189            dbc.clear();
4190        }
4191    }
4192
4193    /**
4194     * Reads the aliases for a resource with a given structure id.<p>
4195     *
4196     * @param context the current request context
4197     * @param structureId the structure id for which the aliases should be read
4198     *
4199     * @return the aliases for the structure id
4200     *
4201     * @throws CmsException if something goes wrong
4202     */
4203    public List<CmsAlias> readAliasesById(CmsRequestContext context, CmsUUID structureId) throws CmsException {
4204
4205        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4206        try {
4207            List<CmsAlias> aliases = m_driverManager.readAliasesByStructureId(
4208                dbc,
4209                context.getCurrentProject(),
4210                structureId);
4211            return aliases;
4212        } catch (Exception e) {
4213            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
4214            return null; // will never be executed
4215        } finally {
4216            dbc.clear();
4217        }
4218
4219    }
4220
4221    /**
4222     * Reads all historical versions of a resource.<p>
4223     *
4224     * The reading excludes the file content, if the resource is a file.<p>
4225     *
4226     * @param context the current request context
4227     * @param resource the resource to be read
4228     *
4229     * @return a list of historical versions, as <code>{@link I_CmsHistoryResource}</code> objects
4230     *
4231     * @throws CmsException if something goes wrong
4232     */
4233    public List<I_CmsHistoryResource> readAllAvailableVersions(CmsRequestContext context, CmsResource resource)
4234    throws CmsException {
4235
4236        List<I_CmsHistoryResource> result = null;
4237        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4238        try {
4239            result = m_driverManager.readAllAvailableVersions(dbc, resource);
4240        } catch (Exception e) {
4241            dbc.report(
4242                null,
4243                Messages.get().container(Messages.ERR_READ_ALL_HISTORY_FILE_HEADERS_1, context.getSitePath(resource)),
4244                e);
4245        } finally {
4246            dbc.clear();
4247        }
4248        return result;
4249    }
4250
4251    /**
4252     * Reads all property definitions for the given mapping type.<p>
4253     *
4254     * @param context the current request context
4255     *
4256     * @return a list with the <code>{@link CmsPropertyDefinition}</code> objects (may be empty)
4257     *
4258     * @throws CmsException if something goes wrong
4259     */
4260    public List<CmsPropertyDefinition> readAllPropertyDefinitions(CmsRequestContext context) throws CmsException {
4261
4262        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4263        List<CmsPropertyDefinition> result = null;
4264        try {
4265            result = m_driverManager.readAllPropertyDefinitions(dbc);
4266        } catch (Exception e) {
4267            dbc.report(null, Messages.get().container(Messages.ERR_READ_ALL_PROPDEF_0), e);
4268        } finally {
4269            dbc.clear();
4270        }
4271        return result;
4272    }
4273
4274    /**
4275     * Returns all resources subscribed by the given user or group.<p>
4276     *
4277     * @param context the request context
4278     * @param poolName the name of the database pool to use
4279     * @param principal the principal to read the subscribed resources
4280     *
4281     * @return all resources subscribed by the given user or group
4282     *
4283     * @throws CmsException if something goes wrong
4284     */
4285    public List<CmsResource> readAllSubscribedResources(
4286        CmsRequestContext context,
4287        String poolName,
4288        CmsPrincipal principal)
4289    throws CmsException {
4290
4291        List<CmsResource> result = null;
4292        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4293        try {
4294            result = m_driverManager.readAllSubscribedResources(dbc, poolName, principal);
4295        } catch (Exception e) {
4296            if (principal instanceof CmsUser) {
4297                dbc.report(
4298                    null,
4299                    Messages.get().container(Messages.ERR_READ_SUBSCRIBED_RESOURCES_ALL_USER_1, principal.getName()),
4300                    e);
4301            } else {
4302                dbc.report(
4303                    null,
4304                    Messages.get().container(Messages.ERR_READ_SUBSCRIBED_RESOURCES_ALL_GROUP_1, principal.getName()),
4305                    e);
4306            }
4307        } finally {
4308            dbc.clear();
4309        }
4310        return result;
4311    }
4312
4313    /**
4314     * Reads all URL name mapping entries for a given structure id.<p>
4315     *
4316     * @param context the request context
4317     * @param id the structure id
4318     *
4319     * @return the list of URL names for the given structure id
4320     * @throws CmsException if something goes wrong
4321     */
4322    public List<String> readAllUrlNameMappingEntries(CmsRequestContext context, CmsUUID id) throws CmsException {
4323
4324        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4325        try {
4326            List<CmsUrlNameMappingEntry> entries = m_driverManager.readUrlNameMappingEntries(
4327                dbc,
4328                context.getCurrentProject().isOnlineProject(),
4329                CmsUrlNameMappingFilter.ALL.filterStructureId(id));
4330            List<String> result = new ArrayList<String>();
4331            for (CmsUrlNameMappingEntry entry : entries) {
4332                result.add(entry.getName());
4333            }
4334            return result;
4335        } catch (Exception e) {
4336            CmsMessageContainer message = Messages.get().container(
4337                Messages.ERR_READ_NEWEST_URLNAME_FOR_ID_1,
4338                id.toString());
4339            dbc.report(null, message, e);
4340            return null;
4341        } finally {
4342            dbc.clear();
4343        }
4344
4345    }
4346
4347    /**
4348     * Returns the first ancestor folder matching the filter criteria.<p>
4349     *
4350     * If no folder matching the filter criteria is found, null is returned.<p>
4351     *
4352     * @param context the context of the current request
4353     * @param resource the resource to start
4354     * @param filter the resource filter to match while reading the ancestors
4355     *
4356     * @return the first ancestor folder matching the filter criteria or <code>null</code> if no folder was found
4357     *
4358     * @throws CmsException if something goes wrong
4359     */
4360    public CmsFolder readAncestor(CmsRequestContext context, CmsResource resource, CmsResourceFilter filter)
4361    throws CmsException {
4362
4363        // get the full folder path of the resource to start from
4364        String path = CmsResource.getFolderPath(resource.getRootPath());
4365        do {
4366            // check if the current folder matches the given filter
4367            if (existsResource(context, path, filter)) {
4368                // folder matches, return it
4369                return readFolder(context, path, filter);
4370            } else {
4371                // folder does not match filter criteria, go up one folder
4372                path = CmsResource.getParentFolder(path);
4373            }
4374
4375            if (CmsStringUtil.isEmpty(path) || !path.startsWith(context.getSiteRoot())) {
4376                // site root or root folder reached and no matching folder found
4377                return null;
4378            }
4379        } while (true);
4380    }
4381
4382    /**
4383     * Reads the newest URL name which is mapped to the given structure id.<p>
4384     *
4385     * If the structure id is not mapped to any name, null will be returned.<p>
4386     *
4387     * @param context the request context
4388     * @param id the structure id for which the newest mapped name should be returned
4389     * @param locale the locale for the mapping
4390     * @param defaultLocales the default locales to use if there is no URL name mapping for the requested locale
4391     *
4392     * @return an URL name or null
4393     *
4394     * @throws CmsException if something goes wrong
4395     */
4396    public String readBestUrlName(CmsRequestContext context, CmsUUID id, Locale locale, List<Locale> defaultLocales)
4397    throws CmsException {
4398
4399        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4400        try {
4401            return m_driverManager.readBestUrlName(dbc, id, locale, defaultLocales);
4402        } catch (Exception e) {
4403            CmsMessageContainer message = Messages.get().container(
4404                Messages.ERR_READ_NEWEST_URLNAME_FOR_ID_1,
4405                id.toString());
4406            dbc.report(null, message, e);
4407            return null; // will never be reached
4408        } finally {
4409            dbc.clear();
4410        }
4411    }
4412
4413    /**
4414     * Returns the child resources of a resource, that is the resources
4415     * contained in a folder.<p>
4416     *
4417     * With the parameters <code>getFolders</code> and <code>getFiles</code>
4418     * you can control what type of resources you want in the result list:
4419     * files, folders, or both.<p>
4420     *
4421     * This method is mainly used by the workplace explorer.<p>
4422     *
4423     * @param context the current request context
4424     * @param resource the resource to return the child resources for
4425     * @param filter the resource filter to use
4426     * @param getFolders if true the child folders are included in the result
4427     * @param getFiles if true the child files are included in the result
4428     *
4429     * @return a list of all child resources
4430     *
4431     * @throws CmsException if something goes wrong
4432     * @throws CmsSecurityException if the user has insufficient permission for the given resource (read is required)
4433     *
4434     */
4435    public List<CmsResource> readChildResources(
4436        CmsRequestContext context,
4437        CmsResource resource,
4438        CmsResourceFilter filter,
4439        boolean getFolders,
4440        boolean getFiles)
4441    throws CmsException, CmsSecurityException {
4442
4443        List<CmsResource> result = null;
4444        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4445        try {
4446            // check the access permissions
4447            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, true, CmsResourceFilter.ALL);
4448            result = m_driverManager.readChildResources(dbc, resource, filter, getFolders, getFiles, true);
4449        } catch (Exception e) {
4450            dbc.report(
4451                null,
4452                Messages.get().container(Messages.ERR_READ_CHILD_RESOURCES_1, context.getSitePath(resource)),
4453                e);
4454        } finally {
4455            dbc.clear();
4456        }
4457        return result;
4458    }
4459
4460    /**
4461     * Returns the default file for the given folder.<p>
4462     *
4463     * If the given resource is a file, then this file is returned.<p>
4464     *
4465     * Otherwise, in case of a folder:<br>
4466     * <ol>
4467     *   <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and
4468     *   <li>if still no file could be found, the configured default files in the
4469     *       <code>opencms-vfs.xml</code> configuration are iterated until a match is
4470     *       found, and
4471     *   <li>if still no file could be found, <code>null</code> is returned
4472     * </ol><p>
4473     *
4474     * @param context the request context
4475     * @param resource the folder to get the default file for
4476     * @param resourceFilter the resource filter
4477     *
4478     * @return the default file for the given folder
4479     *
4480     * @throws CmsSecurityException if the user has no permissions to read the resulting file
4481     *
4482     * @see CmsObject#readDefaultFile(String)
4483     */
4484    public CmsResource readDefaultFile(
4485        CmsRequestContext context,
4486        CmsResource resource,
4487        CmsResourceFilter resourceFilter)
4488    throws CmsSecurityException {
4489
4490        CmsResource result = null;
4491        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4492        try {
4493            CmsResource tempResult = m_driverManager.readDefaultFile(dbc, resource, resourceFilter);
4494            if (tempResult != null) {
4495                // check if the user has read access to the resource
4496                checkPermissions(dbc, tempResult, CmsPermissionSet.ACCESS_READ, true, resourceFilter);
4497                result = tempResult;
4498            }
4499        } catch (CmsSecurityException se) {
4500            // permissions deny access to the resource
4501            throw se;
4502        } catch (CmsException e) {
4503            // ignore all other exceptions
4504            LOG.debug(e.getLocalizedMessage(), e);
4505        } finally {
4506            dbc.clear();
4507        }
4508        return result;
4509    }
4510
4511    /**
4512     * Reads all deleted (historical) resources below the given path,
4513     * including the full tree below the path, if required.<p>
4514     *
4515     * @param context the current request context
4516     * @param resource the parent resource to read the resources from
4517     * @param readTree <code>true</code> to read all subresources
4518     *
4519     * @return a list of <code>{@link I_CmsHistoryResource}</code> objects
4520     *
4521     * @throws CmsException if something goes wrong
4522     *
4523     * @see CmsObject#readResource(CmsUUID, int)
4524     * @see CmsObject#readResources(String, CmsResourceFilter, boolean)
4525     * @see CmsObject#readDeletedResources(String, boolean)
4526     */
4527    public List<I_CmsHistoryResource> readDeletedResources(
4528        CmsRequestContext context,
4529        CmsResource resource,
4530        boolean readTree)
4531    throws CmsException {
4532
4533        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4534        List<I_CmsHistoryResource> result = null;
4535        try {
4536            boolean isVfsManager = hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource);
4537            result = m_driverManager.readDeletedResources(dbc, resource, readTree, isVfsManager);
4538        } catch (CmsException e) {
4539            dbc.report(
4540                null,
4541                Messages.get().container(
4542                    Messages.ERR_READING_DELETED_RESOURCES_1,
4543                    dbc.removeSiteRoot(resource.getRootPath())),
4544                e);
4545        } finally {
4546            dbc.clear();
4547        }
4548        return result;
4549    }
4550
4551    /**
4552     * Reads a file resource (including it's binary content) from the VFS.<p>
4553     *
4554     * In case you do not need the file content,
4555     * use <code>{@link #readResource(CmsRequestContext, String, CmsResourceFilter)}</code> instead.<p>
4556     *
4557     * @param context the current request context
4558     * @param resource the resource to be read
4559     *
4560     * @return the file read from the VFS
4561     *
4562     * @throws CmsException if something goes wrong
4563     */
4564    public CmsFile readFile(CmsRequestContext context, CmsResource resource) throws CmsException {
4565
4566        CmsFile result = null;
4567        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4568        try {
4569            result = m_driverManager.readFile(dbc, resource);
4570        } catch (Exception e) {
4571            reportReadFileException(dbc, context, resource, e);
4572        } finally {
4573            dbc.clear();
4574        }
4575        return result;
4576    }
4577
4578    /**
4579     * Reads a file resource content from the VFS and passes it to an input stream handler.<p>
4580     *
4581     * @param context the current request context
4582     * @param resource the resource to be read
4583     * @param handler the stream handler
4584     *
4585     * @throws CmsException if something goes wrong
4586     */
4587    public void readFileContentFrom(
4588        CmsRequestContext context,
4589        CmsResource resource,
4590        I_CmsFileContentStreamHandler handler)
4591    throws CmsException {
4592
4593        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4594        try {
4595            m_driverManager.readFileContentFrom(dbc, resource, handler);
4596        } catch (Exception e) {
4597            reportReadFileException(dbc, context, resource, e);
4598        } finally {
4599            dbc.clear();
4600        }
4601    }
4602
4603    /**
4604     * Reads a file resource content from the VFS and writes it to an output stream.<p>
4605     *
4606     * @param context the current request context
4607     * @param resource the resource to be read
4608     * @param out the output stream to write to
4609     *
4610     * @throws CmsException if something goes wrong
4611     */
4612    public void readFileContentTo(CmsRequestContext context, CmsResource resource, OutputStream out)
4613    throws CmsException {
4614
4615        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4616        try {
4617            m_driverManager.readFileContentTo(dbc, resource, out);
4618        } catch (Exception e) {
4619            reportReadFileException(dbc, context, resource, e);
4620        } finally {
4621            dbc.clear();
4622        }
4623    }
4624
4625    /**
4626     * Reads a folder resource from the VFS,
4627     * using the specified resource filter.<p>
4628     *
4629     * The specified filter controls what kind of resources should be "found"
4630     * during the read operation. This will depend on the application. For example,
4631     * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
4632     * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
4633     * will ignore the date release / date expired information of the resource.<p>
4634     *
4635     * @param context the current request context
4636     * @param resourcename the name of the folder to read (full path)
4637     * @param filter the resource filter to use while reading
4638     *
4639     * @return the folder that was read
4640     *
4641     * @throws CmsException if something goes wrong
4642     */
4643    public CmsFolder readFolder(CmsRequestContext context, String resourcename, CmsResourceFilter filter)
4644    throws CmsException {
4645
4646        CmsFolder result = null;
4647        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4648        try {
4649            result = readFolder(dbc, resourcename, filter);
4650        } catch (Exception e) {
4651            dbc.report(null, Messages.get().container(Messages.ERR_READ_FOLDER_2, resourcename, filter), e);
4652        } finally {
4653            dbc.clear();
4654        }
4655        return result;
4656    }
4657
4658    /**
4659     * Reads folder size statistis.
4660     *
4661     * @param context the request context
4662     * @param options the options that control what is actually read
4663     * @return the folder size entries
4664     *
4665     * @throws CmsException if something goes wrong
4666     */
4667    public List<CmsFolderSizeEntry> readFolderSizeStats(CmsRequestContext context, CmsFolderSizeOptions options)
4668    throws CmsException {
4669
4670        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4671        try {
4672            return m_driverManager.readFolderSizeStats(dbc, options);
4673        } finally {
4674            dbc.clear();
4675        }
4676    }
4677
4678    /**
4679     * Reads the group of a project.<p>
4680     *
4681     * @param context the current request context
4682     * @param project the project to read from
4683     *
4684     * @return the group of a resource
4685     */
4686    public CmsGroup readGroup(CmsRequestContext context, CmsProject project) {
4687
4688        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4689        CmsGroup result = null;
4690        try {
4691            result = m_driverManager.readGroup(dbc, project);
4692        } finally {
4693            dbc.clear();
4694        }
4695        return result;
4696    }
4697
4698    /**
4699     * Reads a group based on its id.<p>
4700     *
4701     * @param context the current request context
4702     * @param groupId the id of the group that is to be read
4703     *
4704     * @return the requested group
4705     *
4706     * @throws CmsException if operation was not successful
4707     */
4708    public CmsGroup readGroup(CmsRequestContext context, CmsUUID groupId) throws CmsException {
4709
4710        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4711        CmsGroup result = null;
4712        try {
4713            result = m_driverManager.readGroup(dbc, groupId);
4714        } catch (Exception e) {
4715            dbc.report(null, Messages.get().container(Messages.ERR_READ_GROUP_FOR_ID_1, groupId.toString()), e);
4716        } finally {
4717            dbc.clear();
4718        }
4719        return result;
4720    }
4721
4722    /**
4723     * Reads a group based on its name.<p>
4724     *
4725     * @param context the current request context
4726     * @param groupname the name of the group that is to be read
4727     *
4728     * @return the requested group
4729     *
4730     * @throws CmsException if operation was not successful
4731     */
4732    public CmsGroup readGroup(CmsRequestContext context, String groupname) throws CmsException {
4733
4734        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4735        CmsGroup result = null;
4736        try {
4737            result = m_driverManager.readGroup(dbc, CmsOrganizationalUnit.removeLeadingSeparator(groupname));
4738        } catch (Exception e) {
4739            dbc.report(null, Messages.get().container(Messages.ERR_READ_GROUP_FOR_NAME_1, groupname), e);
4740        } finally {
4741            dbc.clear();
4742        }
4743        return result;
4744    }
4745
4746    /**
4747     * Reads a principal (an user or group) from the historical archive based on its ID.<p>
4748     *
4749     * @param context the current request context
4750     * @param principalId the id of the principal to read
4751     *
4752     * @return the historical principal entry with the given id
4753     *
4754     * @throws CmsException if something goes wrong, ie. {@link CmsDbEntryNotFoundException}
4755     *
4756     * @see CmsObject#readUser(CmsUUID)
4757     * @see CmsObject#readGroup(CmsUUID)
4758     * @see CmsObject#readHistoryPrincipal(CmsUUID)
4759     */
4760    public CmsHistoryPrincipal readHistoricalPrincipal(CmsRequestContext context, CmsUUID principalId)
4761    throws CmsException {
4762
4763        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4764        CmsHistoryPrincipal result = null;
4765        try {
4766            result = m_driverManager.readHistoricalPrincipal(dbc, principalId);
4767        } catch (Exception e) {
4768            dbc.report(null, Messages.get().container(Messages.ERR_READ_HISTORY_PRINCIPAL_1, principalId), e);
4769        } finally {
4770            dbc.clear();
4771        }
4772        return result;
4773    }
4774
4775    /**
4776     * Returns the latest historical project entry with the given id.<p>
4777     *
4778     * @param context the current request context
4779     * @param projectId the project id
4780     *
4781     * @return the requested historical project entry
4782     *
4783     * @throws CmsException if something goes wrong
4784     */
4785    public CmsHistoryProject readHistoryProject(CmsRequestContext context, CmsUUID projectId) throws CmsException {
4786
4787        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4788        CmsHistoryProject result = null;
4789        try {
4790            result = m_driverManager.readHistoryProject(dbc, projectId);
4791        } catch (Exception e) {
4792            dbc.report(
4793                null,
4794                Messages.get().container(
4795                    Messages.ERR_READ_HISTORY_PROJECT_2,
4796                    projectId,
4797                    dbc.currentProject().getName()),
4798                e);
4799        } finally {
4800            dbc.clear();
4801        }
4802        return result;
4803    }
4804
4805    /**
4806     * Returns a historical project entry.<p>
4807     *
4808     * @param context the current request context
4809     * @param publishTag the publish tag of the project
4810     *
4811     * @return the requested historical project entry
4812     *
4813     * @throws CmsException if something goes wrong
4814     */
4815    public CmsHistoryProject readHistoryProject(CmsRequestContext context, int publishTag) throws CmsException {
4816
4817        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4818        CmsHistoryProject result = null;
4819        try {
4820            result = m_driverManager.readHistoryProject(dbc, publishTag);
4821        } catch (Exception e) {
4822            dbc.report(
4823                null,
4824                Messages.get().container(
4825                    Messages.ERR_READ_HISTORY_PROJECT_2,
4826                    Integer.valueOf(publishTag),
4827                    dbc.currentProject().getName()),
4828                e);
4829        } finally {
4830            dbc.clear();
4831        }
4832        return result;
4833    }
4834
4835    /**
4836     * Reads the list of all <code>{@link CmsProperty}</code> objects that belong to the given historical resource.<p>
4837     *
4838     * @param context the current request context
4839     * @param resource the historical resource entry to read the properties for
4840     *
4841     * @return the list of <code>{@link CmsProperty}</code> objects
4842     *
4843     * @throws CmsException if something goes wrong
4844     */
4845    public List<CmsProperty> readHistoryPropertyObjects(CmsRequestContext context, I_CmsHistoryResource resource)
4846    throws CmsException {
4847
4848        List<CmsProperty> result = null;
4849        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4850        try {
4851            result = m_driverManager.readHistoryPropertyObjects(dbc, resource);
4852        } catch (Exception e) {
4853            dbc.report(
4854                null,
4855                Messages.get().container(
4856                    Messages.ERR_READ_PROPS_FOR_RESOURCE_1,
4857                    context.getSitePath((CmsResource)resource)),
4858                e);
4859        } finally {
4860            dbc.clear();
4861        }
4862        return result;
4863    }
4864
4865    /**
4866     * Reads the structure id which is mapped to the given URL name, or null if the name is not
4867     * mapped to any structure IDs.<p>
4868     *
4869     * @param context the request context
4870     * @param name an URL name
4871     *
4872     * @return the structure ID which is mapped to the given name
4873     *
4874     * @throws CmsException if something goes wrong
4875     */
4876    public CmsUUID readIdForUrlName(CmsRequestContext context, String name) throws CmsException {
4877
4878        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4879        try {
4880            return m_driverManager.readIdForUrlName(dbc, name);
4881        } catch (Exception e) {
4882            CmsMessageContainer message = Messages.get().container(Messages.ERR_READ_ID_FOR_URLNAME_1, name);
4883            dbc.report(null, message, e);
4884            return null; // will never be reached
4885        } finally {
4886            dbc.clear();
4887        }
4888
4889    }
4890
4891    /**
4892     * Reads the locks that were saved to the database in the previous run of OpenCms.<p>
4893     *
4894     * @throws CmsException if something goes wrong
4895     */
4896    public void readLocks() throws CmsException {
4897
4898        CmsDbContext dbc = m_dbContextFactory.getDbContext();
4899        try {
4900            m_driverManager.readLocks(dbc);
4901        } finally {
4902            dbc.clear();
4903        }
4904    }
4905
4906    /**
4907     * Reads the manager group of a project.<p>
4908     *
4909     * @param context the current request context
4910     * @param project the project to read from
4911     *
4912     * @return the group of a resource
4913     */
4914    public CmsGroup readManagerGroup(CmsRequestContext context, CmsProject project) {
4915
4916        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4917        CmsGroup result = null;
4918        try {
4919            result = m_driverManager.readManagerGroup(dbc, project);
4920        } finally {
4921            dbc.clear();
4922        }
4923        return result;
4924    }
4925
4926    /**
4927     * Reads an organizational Unit based on its fully qualified name.<p>
4928     *
4929     * @param context the current request context
4930     * @param ouFqn the fully qualified name of the organizational Unit to be read
4931     *
4932     * @return the organizational Unit that with the provided fully qualified name
4933     *
4934     * @throws CmsException if something goes wrong
4935     */
4936    public CmsOrganizationalUnit readOrganizationalUnit(CmsRequestContext context, String ouFqn) throws CmsException {
4937
4938        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4939        CmsOrganizationalUnit result = null;
4940        try {
4941            result = m_driverManager.readOrganizationalUnit(dbc, CmsOrganizationalUnit.removeLeadingSeparator(ouFqn));
4942        } catch (Exception e) {
4943            dbc.report(null, Messages.get().container(Messages.ERR_READ_ORGUNIT_1, ouFqn), e);
4944        } finally {
4945            dbc.clear();
4946        }
4947        return result;
4948    }
4949
4950    /**
4951     * Reads the owner of a project from the OpenCms.<p>
4952     *
4953     * @param context the current request context
4954     * @param project the project to get the owner from
4955     *
4956     * @return the owner of a resource
4957     *
4958     * @throws CmsException if something goes wrong
4959     */
4960    public CmsUser readOwner(CmsRequestContext context, CmsProject project) throws CmsException {
4961
4962        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4963        CmsUser result = null;
4964        try {
4965            result = m_driverManager.readOwner(dbc, project);
4966        } catch (Exception e) {
4967            dbc.report(
4968                null,
4969                Messages.get().container(Messages.ERR_READ_OWNER_FOR_PROJECT_2, project.getName(), project.getUuid()),
4970                e);
4971        } finally {
4972            dbc.clear();
4973        }
4974        return result;
4975    }
4976
4977    /**
4978     * Returns the parent folder to the given structure id.<p>
4979     *
4980     * @param context the current request context
4981     * @param structureId the child structure id
4982     *
4983     * @return the parent folder <code>{@link CmsResource}</code>
4984     *
4985     * @throws CmsException if something goes wrong
4986     */
4987    public CmsResource readParentFolder(CmsRequestContext context, CmsUUID structureId) throws CmsException {
4988
4989        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
4990        CmsResource result = null;
4991        try {
4992            result = m_driverManager.readParentFolder(dbc, structureId);
4993        } catch (Exception e) {
4994            dbc.report(
4995                null,
4996                Messages.get().container(
4997                    Messages.ERR_READ_PARENT_FOLDER_2,
4998                    dbc.currentProject().getName(),
4999                    structureId),
5000                e);
5001        } finally {
5002            dbc.clear();
5003        }
5004        return result;
5005    }
5006
5007    /**
5008     * Builds a list of resources for a given path.<p>
5009     *
5010     * @param context the current request context
5011     * @param path the requested path
5012     * @param filter a filter object (only "includeDeleted" information is used!)
5013     *
5014     * @return list of <code>{@link CmsResource}</code>s
5015     *
5016     * @throws CmsException if something goes wrong
5017     */
5018    public List<CmsResource> readPath(CmsRequestContext context, String path, CmsResourceFilter filter)
5019    throws CmsException {
5020
5021        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5022        List<CmsResource> result = null;
5023        try {
5024            result = m_driverManager.readPath(dbc, path, filter);
5025        } catch (Exception e) {
5026            dbc.report(
5027                null,
5028                Messages.get().container(Messages.ERR_READ_PATH_2, dbc.currentProject().getName(), path),
5029                e);
5030        } finally {
5031            dbc.clear();
5032        }
5033        return result;
5034    }
5035
5036    /**
5037     * Reads a project given the projects id.<p>
5038     *
5039     * @param id the id of the project
5040     *
5041     * @return the project read
5042     *
5043     * @throws CmsException if something goes wrong
5044     */
5045    public CmsProject readProject(CmsUUID id) throws CmsException {
5046
5047        CmsDbContext dbc = m_dbContextFactory.getDbContext();
5048        CmsProject result = null;
5049        try {
5050            result = m_driverManager.readProject(dbc, id);
5051        } catch (Exception e) {
5052            dbc.report(null, Messages.get().container(Messages.ERR_READ_PROJECT_FOR_ID_1, id), e);
5053        } finally {
5054            dbc.clear();
5055        }
5056        return result;
5057    }
5058
5059    /**
5060     * Reads a project.<p>
5061     *
5062     * Important: Since a project name can be used multiple times, this is NOT the most efficient
5063     * way to read the project. This is only a convenience for front end developing.
5064     * Reading a project by name will return the first project with that name.
5065     * All core classes must use the id version {@link #readProject(CmsUUID)} to ensure the right project is read.<p>
5066     *
5067     * @param name the name of the project
5068     *
5069     * @return the project read
5070     *
5071     * @throws CmsException if something goes wrong
5072     */
5073    public CmsProject readProject(String name) throws CmsException {
5074
5075        CmsDbContext dbc = m_dbContextFactory.getDbContext();
5076        CmsProject result = null;
5077        try {
5078            result = m_driverManager.readProject(dbc, CmsOrganizationalUnit.removeLeadingSeparator(name));
5079        } catch (Exception e) {
5080            dbc.report(null, Messages.get().container(Messages.ERR_READ_PROJECT_FOR_NAME_1, name), e);
5081        } finally {
5082            dbc.clear();
5083        }
5084        return result;
5085    }
5086
5087    /**
5088     * Returns the list of all resource names that define the "view" of the given project.<p>
5089     *
5090     * @param context the current request context
5091     * @param project the project to get the project resources for
5092     *
5093     * @return the list of all resources, as <code>{@link String}</code> objects
5094     *              that define the "view" of the given project
5095     *
5096     * @throws CmsException if something goes wrong
5097     */
5098    public List<String> readProjectResources(CmsRequestContext context, CmsProject project) throws CmsException {
5099
5100        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5101        List<String> result = null;
5102        try {
5103            result = m_driverManager.readProjectResources(dbc, project);
5104        } catch (Exception e) {
5105            dbc.report(
5106                null,
5107                Messages.get().container(Messages.ERR_READ_PROJECT_RESOURCES_2, project.getName(), project.getUuid()),
5108                e);
5109        } finally {
5110            dbc.clear();
5111        }
5112        return result;
5113    }
5114
5115    /**
5116     * Reads all resources of a project that match a given state from the VFS.<p>
5117     *
5118     * Possible values for the <code>state</code> parameter are:<br>
5119     * <ul>
5120     * <li><code>{@link CmsResource#STATE_CHANGED}</code>: Read all "changed" resources in the project</li>
5121     * <li><code>{@link CmsResource#STATE_NEW}</code>: Read all "new" resources in the project</li>
5122     * <li><code>{@link CmsResource#STATE_DELETED}</code>: Read all "deleted" resources in the project</li>
5123     * <li><code>{@link CmsResource#STATE_KEEP}</code>: Read all resources either "changed", "new" or "deleted" in the project</li>
5124     * </ul><p>
5125     *
5126     * @param context the current request context
5127     * @param projectId the id of the project to read the file resources for
5128     * @param state the resource state to match
5129     *
5130     * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria
5131     *
5132     * @throws CmsException if something goes wrong
5133     *
5134     * @see CmsObject#readProjectView(CmsUUID, CmsResourceState)
5135     */
5136    public List<CmsResource> readProjectView(CmsRequestContext context, CmsUUID projectId, CmsResourceState state)
5137    throws CmsException {
5138
5139        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5140        List<CmsResource> result = null;
5141        try {
5142            result = m_driverManager.readProjectView(dbc, projectId, state);
5143        } catch (Exception e) {
5144            dbc.report(null, Messages.get().container(Messages.ERR_READ_PROJECT_VIEW_1, projectId), e);
5145        } finally {
5146            dbc.clear();
5147        }
5148        return result;
5149    }
5150
5151    /**
5152     * Reads a property definition.<p>
5153     *
5154     * If no property definition with the given name is found,
5155     * <code>null</code> is returned.<p>
5156     *
5157     * @param context the current request context
5158     * @param name the name of the property definition to read
5159     *
5160     * @return the property definition that was read
5161     *
5162     * @throws CmsException a CmsDbEntryNotFoundException is thrown if the property definition does not exist
5163     */
5164    public CmsPropertyDefinition readPropertyDefinition(CmsRequestContext context, String name) throws CmsException {
5165
5166        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5167        CmsPropertyDefinition result = null;
5168        try {
5169            result = m_driverManager.readPropertyDefinition(dbc, name);
5170        } catch (Exception e) {
5171            dbc.report(null, Messages.get().container(Messages.ERR_READ_PROPDEF_1, name), e);
5172        } finally {
5173            dbc.clear();
5174        }
5175        return result;
5176    }
5177
5178    /**
5179     * Reads a property object from a resource specified by a property name.<p>
5180     *
5181     * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p>
5182     *
5183     * @param context the context of the current request
5184     * @param resource the resource where the property is mapped to
5185     * @param key the property key name
5186     * @param search if <code>true</code>, the property is searched on all parent folders of the resource.
5187     *      if it's not found attached directly to the resource.
5188     *
5189     * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found
5190     *
5191     * @throws CmsException if something goes wrong
5192     */
5193    public CmsProperty readPropertyObject(CmsRequestContext context, CmsResource resource, String key, boolean search)
5194    throws CmsException {
5195
5196        return readPropertyObject(context, resource, key, search, null);
5197    }
5198
5199    /**
5200     * Reads a property object from a resource specified by a property name.<p>
5201     *
5202     * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p>
5203     *
5204     * @param context the context of the current request
5205     * @param resource the resource where the property is mapped to
5206     * @param key the property key name
5207     * @param search if <code>true</code>, the property is searched on all parent folders of the resource.
5208     *      if it's not found attached directly to the resource.
5209     * @param locale the locale for which the property should be read.
5210     *
5211     * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found
5212     *
5213     * @throws CmsException if something goes wrong
5214     */
5215    public CmsProperty readPropertyObject(
5216        CmsRequestContext context,
5217        CmsResource resource,
5218        String key,
5219        boolean search,
5220        Locale locale)
5221    throws CmsException {
5222
5223        CmsProperty result = null;
5224        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5225        try {
5226            if (null == locale) {
5227                result = m_driverManager.readPropertyObject(dbc, resource, key, search);
5228            } else {
5229                result = m_driverManager.readPropertyObject(dbc, resource, key, search, locale);
5230            }
5231        } catch (Exception e) {
5232            dbc.report(
5233                null,
5234                Messages.get().container(Messages.ERR_READ_PROP_FOR_RESOURCE_2, key, context.getSitePath(resource)),
5235                e);
5236        } finally {
5237            dbc.clear();
5238        }
5239        return result;
5240    }
5241
5242    /**
5243     * Reads all property objects from a resource.<p>
5244     *
5245     * Returns an empty list if no properties are found.<p>
5246     *
5247     * If the <code>search</code> parameter is <code>true</code>, the properties of all
5248     * parent folders of the resource are also read. The results are merged with the
5249     * properties directly attached to the resource. While merging, a property
5250     * on a parent folder that has already been found will be ignored.
5251     * So e.g. if a resource has a property "Title" attached, and it's parent folder
5252     * has the same property attached but with a different value, the result list will
5253     * contain only the property with the value from the resource, not form the parent folder(s).<p>
5254     *
5255     * @param context the context of the current request
5256     * @param resource the resource where the property is mapped to
5257     * @param search <code>true</code>, if the properties should be searched on all parent folders  if not found on the resource
5258     *
5259     * @return a list of <code>{@link CmsProperty}</code> objects
5260     *
5261     * @throws CmsException if something goes wrong
5262     */
5263    public List<CmsProperty> readPropertyObjects(CmsRequestContext context, CmsResource resource, boolean search)
5264    throws CmsException {
5265
5266        List<CmsProperty> result = null;
5267        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5268        try {
5269            result = m_driverManager.readPropertyObjects(dbc, resource, search);
5270        } catch (Exception e) {
5271            dbc.report(
5272                null,
5273                Messages.get().container(Messages.ERR_READ_PROPS_FOR_RESOURCE_1, context.getSitePath(resource)),
5274                e);
5275        } finally {
5276            dbc.clear();
5277        }
5278        return result;
5279    }
5280
5281    /**
5282     * Reads the resources that were published in a publish task for a given publish history ID.<p>
5283     *
5284     * @param context the current request context
5285     * @param publishHistoryId unique ID to identify each publish task in the publish history
5286     *
5287     * @return a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects
5288     *
5289     * @throws CmsException if something goes wrong
5290     */
5291    public List<CmsPublishedResource> readPublishedResources(CmsRequestContext context, CmsUUID publishHistoryId)
5292    throws CmsException {
5293
5294        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5295        List<CmsPublishedResource> result = null;
5296        try {
5297            result = m_driverManager.readPublishedResources(dbc, publishHistoryId);
5298        } catch (Exception e) {
5299            dbc.report(
5300                null,
5301                Messages.get().container(Messages.ERR_READ_PUBLISHED_RESOURCES_FOR_ID_1, publishHistoryId.toString()),
5302                e);
5303        } finally {
5304            dbc.clear();
5305        }
5306        return result;
5307    }
5308
5309    /**
5310     * Reads the historical resource entry for the given resource with the given version number.<p>
5311     *
5312     * @param context the current request context
5313     * @param resource the resource to be read the version for
5314     * @param version the version number to retrieve
5315     *
5316     * @return the resource that was read
5317     *
5318     * @throws CmsException if the resource could not be read for any reason
5319     *
5320     * @see CmsObject#readFile(CmsResource)
5321     * @see CmsObject#restoreResourceVersion(CmsUUID, int)
5322     * @see CmsObject#readResource(CmsUUID, int)
5323     */
5324    public I_CmsHistoryResource readResource(CmsRequestContext context, CmsResource resource, int version)
5325    throws CmsException {
5326
5327        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5328        I_CmsHistoryResource result = null;
5329        try {
5330            result = m_driverManager.readResource(dbc, resource, version);
5331        } catch (CmsException e) {
5332            dbc.report(
5333                null,
5334                Messages.get().container(
5335                    Messages.ERR_READING_RESOURCE_VERSION_2,
5336                    dbc.removeSiteRoot(resource.getRootPath()),
5337                    Integer.valueOf(version)),
5338                e);
5339        } finally {
5340            dbc.clear();
5341        }
5342        return result;
5343    }
5344
5345    /**
5346     * Reads a resource from the VFS,
5347     * using the specified resource filter.<p>
5348     *
5349     * A resource may be of type <code>{@link CmsFile}</code> or
5350     * <code>{@link CmsFolder}</code>. In case of
5351     * a file, the resource will not contain the binary file content. Since reading
5352     * the binary content is a cost-expensive database operation, it's recommended
5353     * to work with resources if possible, and only read the file content when absolutely
5354     * required. To "upgrade" a resource to a file,
5355     * use <code>{@link CmsObject#readFile(CmsResource)}</code>.<p>
5356     *
5357     * The specified filter controls what kind of resources should be "found"
5358     * during the read operation. This will depend on the application. For example,
5359     * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
5360     * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
5361     * will ignore the date release / date expired information of the resource.<p>
5362     *
5363     * @param context the current request context
5364     * @param structureID the ID of the structure which will be used)
5365     * @param filter the resource filter to use while reading
5366     *
5367     * @return the resource that was read
5368     *
5369     * @throws CmsException if the resource could not be read for any reason
5370     *
5371     * @see CmsObject#readResource(CmsUUID, CmsResourceFilter)
5372     * @see CmsObject#readResource(CmsUUID)
5373     * @see CmsObject#readFile(CmsResource)
5374     */
5375    public CmsResource readResource(CmsRequestContext context, CmsUUID structureID, CmsResourceFilter filter)
5376    throws CmsException {
5377
5378        CmsResource result = null;
5379        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5380        try {
5381            result = readResource(dbc, structureID, filter);
5382        } catch (Exception e) {
5383            dbc.report(null, Messages.get().container(Messages.ERR_READ_RESOURCE_FOR_ID_1, structureID), e);
5384        } finally {
5385            dbc.clear();
5386        }
5387        return result;
5388    }
5389
5390    /**
5391     * Reads a resource from the VFS,
5392     * using the specified resource filter.<p>
5393     *
5394     * A resource may be of type <code>{@link CmsFile}</code> or
5395     * <code>{@link CmsFolder}</code>. In case of
5396     * a file, the resource will not contain the binary file content. Since reading
5397     * the binary content is a cost-expensive database operation, it's recommended
5398     * to work with resources if possible, and only read the file content when absolutely
5399     * required. To "upgrade" a resource to a file,
5400     * use <code>{@link CmsObject#readFile(CmsResource)}</code>.<p>
5401     *
5402     * The specified filter controls what kind of resources should be "found"
5403     * during the read operation. This will depend on the application. For example,
5404     * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently
5405     * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code>
5406     * will ignore the date release / date expired information of the resource.<p>
5407     *
5408     * @param context the current request context
5409     * @param resourcePath the name of the resource to read (full path)
5410     * @param filter the resource filter to use while reading
5411     *
5412     * @return the resource that was read
5413     *
5414     * @throws CmsException if the resource could not be read for any reason
5415     *
5416     * @see CmsObject#readResource(String, CmsResourceFilter)
5417     * @see CmsObject#readResource(String)
5418     * @see CmsObject#readFile(CmsResource)
5419     */
5420    public CmsResource readResource(CmsRequestContext context, String resourcePath, CmsResourceFilter filter)
5421    throws CmsException {
5422
5423        CmsResource result = null;
5424        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5425        try {
5426            result = readResource(dbc, resourcePath, filter);
5427        } catch (Exception e) {
5428            dbc.report(
5429                null,
5430                Messages.get().container(Messages.ERR_READ_RESOURCE_1, dbc.removeSiteRoot(resourcePath)),
5431                e);
5432        } finally {
5433            dbc.clear();
5434        }
5435        return result;
5436    }
5437
5438    /**
5439     * Reads all resources below the given path matching the filter criteria,
5440     * including the full tree below the path only in case the <code>readTree</code>
5441     * parameter is <code>true</code>.<p>
5442     *
5443     * @param context the current request context
5444     * @param parent the parent path to read the resources from
5445     * @param filter the filter
5446     * @param readTree <code>true</code> to read all subresources
5447     *
5448     * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria
5449     *
5450     * @throws CmsSecurityException if the user has insufficient permission for the given resource (read is required)
5451     * @throws CmsException if something goes wrong
5452     *
5453     */
5454    public List<CmsResource> readResources(
5455        CmsRequestContext context,
5456        CmsResource parent,
5457        CmsResourceFilter filter,
5458        boolean readTree)
5459    throws CmsException, CmsSecurityException {
5460
5461        List<CmsResource> result = null;
5462        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5463        try {
5464            // check the access permissions
5465            checkPermissions(dbc, parent, CmsPermissionSet.ACCESS_READ, true, CmsResourceFilter.ALL);
5466            result = m_driverManager.readResources(dbc, parent, filter, readTree);
5467        } catch (Exception e) {
5468            dbc.report(
5469                null,
5470                Messages.get().container(Messages.ERR_READ_RESOURCES_1, context.removeSiteRoot(parent.getRootPath())),
5471                e);
5472        } finally {
5473            dbc.clear();
5474        }
5475        return result;
5476    }
5477
5478    /**
5479     * Returns the resources that were visited by a user set in the filter.<p>
5480     *
5481     * @param context the request context
5482     * @param poolName the name of the database pool to use
5483     * @param filter the filter that is used to get the visited resources
5484     *
5485     * @return the resources that were visited by a user set in the filter
5486     *
5487     * @throws CmsException if something goes wrong
5488     */
5489    public List<CmsResource> readResourcesVisitedBy(
5490        CmsRequestContext context,
5491        String poolName,
5492        CmsVisitedByFilter filter)
5493    throws CmsException {
5494
5495        List<CmsResource> result = null;
5496        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5497        try {
5498            result = m_driverManager.readResourcesVisitedBy(dbc, poolName, filter);
5499        } catch (Exception e) {
5500            dbc.report(null, Messages.get().container(Messages.ERR_READ_VISITED_RESOURCES_1, filter.toString()), e);
5501        } finally {
5502            dbc.clear();
5503        }
5504        return result;
5505    }
5506
5507    /**
5508     * Reads all resources that have a value (containing the specified value) set
5509     * for the specified property (definition) in the given path.<p>
5510     *
5511     * If the <code>value</code> parameter is <code>null</code>, all resources having the
5512     * given property set are returned.<p>
5513     *
5514     * Both individual and shared properties of a resource are checked.<p>
5515     *
5516     * @param context the current request context
5517     * @param folder the folder to get the resources with the property from
5518     * @param propertyDefinition the name of the property (definition) to check for
5519     * @param value the string to search in the value of the property
5520     * @param filter the resource filter to apply to the result set
5521     *
5522     * @return a list of all <code>{@link CmsResource}</code> objects
5523     *          that have a value set for the specified property.
5524     *
5525     * @throws CmsException if something goes wrong
5526     */
5527    public List<CmsResource> readResourcesWithProperty(
5528        CmsRequestContext context,
5529        CmsResource folder,
5530        String propertyDefinition,
5531        String value,
5532        CmsResourceFilter filter)
5533    throws CmsException {
5534
5535        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5536        List<CmsResource> result = null;
5537        try {
5538            result = m_driverManager.readResourcesWithProperty(dbc, folder, propertyDefinition, value, filter);
5539        } catch (Exception e) {
5540            dbc.report(
5541                null,
5542                Messages.get().container(
5543                    Messages.ERR_READ_RESOURCES_FOR_PROP_VALUE_3,
5544                    context.removeSiteRoot(folder.getRootPath()),
5545                    propertyDefinition,
5546                    value),
5547                e);
5548        } finally {
5549            dbc.clear();
5550        }
5551        return result;
5552    }
5553
5554    /**
5555     * Returns a set of users that are responsible for a specific resource.<p>
5556     *
5557     * @param context the current request context
5558     * @param resource the resource to get the responsible users from
5559     *
5560     * @return the set of users that are responsible for a specific resource
5561     *
5562     * @throws CmsException if something goes wrong
5563     */
5564    public Set<I_CmsPrincipal> readResponsiblePrincipals(CmsRequestContext context, CmsResource resource)
5565    throws CmsException {
5566
5567        Set<I_CmsPrincipal> result = null;
5568        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5569        try {
5570            result = m_driverManager.readResponsiblePrincipals(dbc, resource);
5571        } catch (Exception e) {
5572            dbc.report(
5573                null,
5574                Messages.get().container(Messages.ERR_READ_RESPONSIBLE_USERS_1, resource.getRootPath()),
5575                e);
5576        } finally {
5577            dbc.clear();
5578        }
5579        return result;
5580    }
5581
5582    /**
5583     * Returns a set of users that are responsible for a specific resource.<p>
5584     *
5585     * @param context the current request context
5586     * @param resource the resource to get the responsible users from
5587     *
5588     * @return the set of users that are responsible for a specific resource
5589     *
5590     * @throws CmsException if something goes wrong
5591     */
5592    public Set<CmsUser> readResponsibleUsers(CmsRequestContext context, CmsResource resource) throws CmsException {
5593
5594        Set<CmsUser> result = null;
5595        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5596        try {
5597            result = m_driverManager.readResponsibleUsers(dbc, resource);
5598        } catch (Exception e) {
5599            dbc.report(
5600                null,
5601                Messages.get().container(Messages.ERR_READ_RESPONSIBLE_USERS_1, resource.getRootPath()),
5602                e);
5603        } finally {
5604            dbc.clear();
5605        }
5606        return result;
5607    }
5608
5609    /**
5610     * Returns a List of all siblings of the specified resource,
5611     * the specified resource being always part of the result set.<p>
5612     *
5613     * @param context the request context
5614     * @param resource the specified resource
5615     * @param filter a filter object
5616     *
5617     * @return a list of <code>{@link CmsResource}</code>s that
5618     *          are siblings to the specified resource,
5619     *          including the specified resource itself
5620     *
5621     * @throws CmsException if something goes wrong
5622     */
5623    public List<CmsResource> readSiblings(CmsRequestContext context, CmsResource resource, CmsResourceFilter filter)
5624    throws CmsException {
5625
5626        List<CmsResource> result = null;
5627        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5628        try {
5629            result = m_driverManager.readSiblings(dbc, resource, filter);
5630        } catch (Exception e) {
5631            dbc.report(null, Messages.get().container(Messages.ERR_READ_SIBLINGS_1, context.getSitePath(resource)), e);
5632        } finally {
5633            dbc.clear();
5634        }
5635        return result;
5636    }
5637
5638    /**
5639     * Returns the parameters of a resource in the table of all published template resources.<p>
5640     *
5641     * @param context the current request context
5642     * @param rfsName the rfs name of the resource
5643     *
5644     * @return the parameter string of the requested resource
5645     *
5646     * @throws CmsException if something goes wrong
5647     */
5648    public String readStaticExportPublishedResourceParameters(CmsRequestContext context, String rfsName)
5649    throws CmsException {
5650
5651        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5652        String result = null;
5653        try {
5654            result = m_driverManager.readStaticExportPublishedResourceParameters(dbc, rfsName);
5655        } catch (Exception e) {
5656            dbc.report(
5657                null,
5658                Messages.get().container(Messages.ERR_READ_STATEXP_PUBLISHED_RESOURCE_PARAMS_1, rfsName),
5659                e);
5660        } finally {
5661            dbc.clear();
5662        }
5663        return result;
5664    }
5665
5666    /**
5667     * Returns a list of all template resources which must be processed during a static export.<p>
5668     *
5669     * @param context the current request context
5670     * @param parameterResources flag for reading resources with parameters (1) or without (0)
5671     * @param timestamp for reading the data from the db
5672     *
5673     * @return a list of template resources as <code>{@link String}</code> objects
5674     *
5675     * @throws CmsException if something goes wrong
5676     */
5677    public List<String> readStaticExportResources(CmsRequestContext context, int parameterResources, long timestamp)
5678    throws CmsException {
5679
5680        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5681        List<String> result = null;
5682        try {
5683            result = m_driverManager.readStaticExportResources(dbc, parameterResources, timestamp);
5684        } catch (Exception e) {
5685            dbc.report(null, Messages.get().container(Messages.ERR_READ_STATEXP_RESOURCES_1, new Date(timestamp)), e);
5686        } finally {
5687            dbc.clear();
5688        }
5689        return result;
5690    }
5691
5692    /**
5693     * Reads information about where the file content is stored without loading the content bytes.<p>
5694     *
5695     * @param context the current request context
5696     * @param resource the resource to read
5697     *
5698     * @return the stored content info
5699     *
5700     * @throws CmsException if something goes wrong
5701     */
5702    public CmsStoredContentInfo readStoredContentInfo(CmsRequestContext context, CmsResource resource)
5703    throws CmsException {
5704
5705        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5706        try {
5707            return m_driverManager.readStoredContentInfo(dbc, resource);
5708        } catch (Exception e) {
5709            reportReadFileException(dbc, context, resource, e);
5710        } finally {
5711            dbc.clear();
5712        }
5713        return null;
5714    }
5715
5716    /**
5717     * Returns the subscribed history resources that were deleted.<p>
5718     *
5719     * @param context the request context
5720     * @param poolName the name of the database pool to use
5721     * @param user the user that subscribed to the resource
5722     * @param groups the groups to check subscribed resources for
5723     * @param parent the parent resource (folder) of the deleted resources, if <code>null</code> all deleted resources will be returned
5724     * @param includeSubFolders indicates if the sub folders of the specified folder path should be considered, too
5725     * @param deletedFrom the time stamp from which the resources should have been deleted
5726     *
5727     * @return the subscribed history resources that were deleted
5728     *
5729     * @throws CmsException if something goes wrong
5730     */
5731    public List<I_CmsHistoryResource> readSubscribedDeletedResources(
5732        CmsRequestContext context,
5733        String poolName,
5734        CmsUser user,
5735        List<CmsGroup> groups,
5736        CmsResource parent,
5737        boolean includeSubFolders,
5738        long deletedFrom)
5739    throws CmsException {
5740
5741        List<I_CmsHistoryResource> result = null;
5742        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5743        try {
5744            result = m_driverManager.readSubscribedDeletedResources(
5745                dbc,
5746                poolName,
5747                user,
5748                groups,
5749                parent,
5750                includeSubFolders,
5751                deletedFrom);
5752        } catch (Exception e) {
5753            dbc.report(
5754                null,
5755                Messages.get().container(Messages.ERR_READ_SUBSCRIBED_DELETED_RESOURCES_1, user.getName()),
5756                e);
5757        } finally {
5758            dbc.clear();
5759        }
5760        return result;
5761    }
5762
5763    /**
5764     * Returns the resources that were subscribed by a user or group set in the filter.<p>
5765     *
5766     * @param context the request context
5767     * @param poolName the name of the database pool to use
5768     * @param filter the filter that is used to get the subscribed resources
5769     *
5770     * @return the resources that were subscribed by a user or group set in the filter
5771     *
5772     * @throws CmsException if something goes wrong
5773     */
5774    public List<CmsResource> readSubscribedResources(
5775        CmsRequestContext context,
5776        String poolName,
5777        CmsSubscriptionFilter filter)
5778    throws CmsException {
5779
5780        List<CmsResource> result = null;
5781        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5782        try {
5783            result = m_driverManager.readSubscribedResources(dbc, poolName, filter);
5784        } catch (Exception e) {
5785            dbc.report(null, Messages.get().container(Messages.ERR_READ_SUBSCRIBED_RESOURCES_1, filter.toString()), e);
5786        } finally {
5787            dbc.clear();
5788        }
5789        return result;
5790    }
5791
5792    /**
5793     * Reads the URL name mappings matching a given filter.<p>
5794     *
5795     * @param context the current request context
5796     * @param filter the filter to match
5797     *
5798     * @return the matching URL name mappings
5799     *
5800     * @throws CmsException if something goes wrong
5801     */
5802    public List<CmsUrlNameMappingEntry> readUrlNameMappings(CmsRequestContext context, CmsUrlNameMappingFilter filter)
5803    throws CmsException {
5804
5805        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5806        try {
5807            return m_driverManager.readUrlNameMappings(dbc, filter);
5808        } catch (Exception e) {
5809            CmsMessageContainer message = Messages.get().container(
5810                Messages.ERR_DB_OPERATION_1,
5811                e.getLocalizedMessage());
5812            dbc.report(null, message, e);
5813            return null; // will never be reached
5814        } finally {
5815            dbc.clear();
5816        }
5817    }
5818
5819    /**
5820     * Reads the newest URL names of a structure id for all locales.<p>
5821     *
5822     * @param context the current context
5823     * @param id a structure id
5824     *
5825     * @return the list of URL names for all
5826     * @throws CmsException if something goes wrong
5827     */
5828    public List<String> readUrlNamesForAllLocales(CmsRequestContext context, CmsUUID id) throws CmsException {
5829
5830        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5831        try {
5832            return m_driverManager.readUrlNamesForAllLocales(dbc, id);
5833        } catch (Exception e) {
5834            CmsMessageContainer message = Messages.get().container(
5835                Messages.ERR_READ_NEWEST_URLNAME_FOR_ID_1,
5836                id.toString());
5837            dbc.report(null, message, e);
5838            return null; // will never be reached
5839        } finally {
5840            dbc.clear();
5841        }
5842    }
5843
5844    /**
5845     * Returns a user object based on the id of a user.<p>
5846     *
5847     * @param context the current request context
5848     * @param id the id of the user to read
5849     *
5850     * @return the user read
5851     *
5852     * @throws CmsException if something goes wrong
5853     */
5854    public CmsUser readUser(CmsRequestContext context, CmsUUID id) throws CmsException {
5855
5856        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5857        CmsUser result = null;
5858        try {
5859            result = m_driverManager.readUser(dbc, id);
5860        } catch (Exception e) {
5861            dbc.report(null, Messages.get().container(Messages.ERR_READ_USER_FOR_ID_1, id.toString()), e);
5862        } finally {
5863            dbc.clear();
5864        }
5865        return result;
5866    }
5867
5868    /**
5869     * Returns a user object.<p>
5870     *
5871     * @param context the current request context
5872     * @param username the name of the user that is to be read
5873     *
5874     * @return user read form the cms
5875     *
5876     * @throws CmsException if operation was not successful
5877     */
5878    public CmsUser readUser(CmsRequestContext context, String username) throws CmsException {
5879
5880        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5881        CmsUser result = null;
5882        try {
5883            result = m_driverManager.readUser(dbc, CmsOrganizationalUnit.removeLeadingSeparator(username));
5884        } catch (Exception e) {
5885            dbc.report(null, Messages.get().container(Messages.ERR_READ_USER_FOR_NAME_1, username), e);
5886        } finally {
5887            dbc.clear();
5888        }
5889        return result;
5890    }
5891
5892    /**
5893     * Returns a user object if the password for the user is correct.<p>
5894     *
5895     * If the user/password pair is not valid a <code>{@link CmsException}</code> is thrown.<p>
5896     *
5897     * @param context the current request context
5898     * @param username the user name of the user that is to be read
5899     * @param password the password of the user that is to be read
5900     *
5901     * @return user read
5902     *
5903     * @throws CmsException if operation was not successful
5904     */
5905    public CmsUser readUser(CmsRequestContext context, String username, String password) throws CmsException {
5906
5907        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5908        CmsUser result = null;
5909        try {
5910            result = m_driverManager.readUser(dbc, CmsOrganizationalUnit.removeLeadingSeparator(username), password);
5911        } catch (Exception e) {
5912            dbc.report(null, Messages.get().container(Messages.ERR_READ_USER_FOR_NAME_1, username), e);
5913        } finally {
5914            dbc.clear();
5915        }
5916        return result;
5917    }
5918
5919    /**
5920     * Removes an access control entry for a given resource and principal.<p>
5921     *
5922     * @param context the current request context
5923     * @param resource the resource
5924     * @param principal the id of the principal to remove the the access control entry for
5925     *
5926     * @throws CmsException if something goes wrong
5927     * @throws CmsSecurityException if the user has insufficient permission for the given resource (control of access control is required).
5928     *
5929     */
5930    public void removeAccessControlEntry(CmsRequestContext context, CmsResource resource, CmsUUID principal)
5931    throws CmsException, CmsSecurityException {
5932
5933        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5934        try {
5935            checkOfflineProject(dbc);
5936            checkPermissions(
5937                dbc,
5938                resource,
5939                CmsPermissionSet.ACCESS_CONTROL,
5940                LockCheck.shallowOnly,
5941                CmsResourceFilter.ALL);
5942            m_driverManager.removeAccessControlEntry(dbc, resource, principal);
5943        } catch (Exception e) {
5944            dbc.report(
5945                null,
5946                Messages.get().container(
5947                    Messages.ERR_REMOVE_ACL_ENTRY_2,
5948                    context.getSitePath(resource),
5949                    principal.toString()),
5950                e);
5951        } finally {
5952            dbc.clear();
5953        }
5954    }
5955
5956    /**
5957     * Removes a resource from the given organizational unit.<p>
5958     *
5959     * @param context the current request context
5960     * @param orgUnit the organizational unit to remove the resource from
5961     * @param resource the resource that is to be removed from the organizational unit
5962     *
5963     * @throws CmsException if something goes wrong
5964     *
5965     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
5966     * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String)
5967     */
5968    public void removeResourceFromOrgUnit(
5969        CmsRequestContext context,
5970        CmsOrganizationalUnit orgUnit,
5971        CmsResource resource)
5972    throws CmsException {
5973
5974        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
5975        try {
5976            checkRole(dbc, CmsRole.ADMINISTRATOR.forOrgUnit(orgUnit.getName()));
5977            checkOfflineProject(dbc);
5978            m_driverManager.removeResourceFromOrgUnit(dbc, orgUnit, resource);
5979        } catch (Exception e) {
5980            dbc.report(
5981                null,
5982                Messages.get().container(
5983                    Messages.ERR_REMOVE_RESOURCE_FROM_ORGUNIT_2,
5984                    orgUnit.getName(),
5985                    dbc.removeSiteRoot(resource.getRootPath())),
5986                e);
5987        } finally {
5988            dbc.clear();
5989        }
5990    }
5991
5992    /**
5993     * Removes a resource from the current project of the user.<p>
5994     *
5995     * @param context the current request context
5996     * @param resource the resource to apply this operation to
5997     *
5998     * @throws CmsException if something goes wrong
5999     * @throws CmsRoleViolationException if the current user does not have management access to the project
6000     *
6001     * @see org.opencms.file.types.I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource)
6002     */
6003    public void removeResourceFromProject(CmsRequestContext context, CmsResource resource)
6004    throws CmsException, CmsRoleViolationException {
6005
6006        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6007        try {
6008            checkOfflineProject(dbc);
6009            checkManagerOfProjectRole(dbc, context.getCurrentProject());
6010
6011            m_driverManager.removeResourceFromProject(dbc, resource);
6012        } catch (Exception e) {
6013            dbc.report(
6014                null,
6015                Messages.get().container(
6016                    Messages.ERR_COPY_RESOURCE_TO_PROJECT_2,
6017                    context.getSitePath(resource),
6018                    context.getCurrentProject().getName()),
6019                e);
6020        } finally {
6021            dbc.clear();
6022        }
6023    }
6024
6025    /**
6026     * Removes the given resource to the given user's publish list.<p>
6027     *
6028     * @param context the request context
6029     * @param structureIds the collection of structure IDs to remove
6030     *
6031     * @throws CmsException if something goes wrong
6032     */
6033    public void removeResourceFromUsersPubList(CmsRequestContext context, Collection<CmsUUID> structureIds)
6034    throws CmsException {
6035
6036        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6037        try {
6038            m_driverManager.removeResourceFromUsersPubList(dbc, context.getCurrentUser().getId(), structureIds);
6039        } catch (Exception e) {
6040            dbc.report(
6041                null,
6042                Messages.get().container(
6043                    Messages.ERR_REMOVE_RESOURCE_FROM_PUBLIST_2,
6044                    context.getCurrentUser().getName(),
6045                    structureIds),
6046                e);
6047
6048        } finally {
6049            dbc.clear();
6050        }
6051    }
6052
6053    /**
6054     * Removes a user from a group.<p>
6055     *
6056     * @param context the current request context
6057     * @param username the name of the user that is to be removed from the group
6058     * @param groupname the name of the group
6059     * @param readRoles if to read roles or groups
6060     *
6061     * @throws CmsException if operation was not successful
6062     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
6063     *
6064     */
6065    public void removeUserFromGroup(CmsRequestContext context, String username, String groupname, boolean readRoles)
6066    throws CmsException, CmsRoleViolationException {
6067
6068        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6069        try {
6070            CmsRole role = CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(groupname));
6071            checkRoleForUserModification(dbc, username, role);
6072            m_driverManager.removeUserFromGroup(
6073                dbc,
6074                CmsOrganizationalUnit.removeLeadingSeparator(username),
6075                CmsOrganizationalUnit.removeLeadingSeparator(groupname),
6076                readRoles);
6077        } catch (Exception e) {
6078            dbc.report(null, Messages.get().container(Messages.ERR_REMOVE_USER_FROM_GROUP_2, username, groupname), e);
6079        } finally {
6080            dbc.clear();
6081        }
6082    }
6083
6084    /**
6085     * Replaces the content, type and properties of a resource.<p>
6086     *
6087     * @param context the current request context
6088     * @param resource the name of the resource to apply this operation to
6089     * @param type the new type of the resource
6090     * @param content the new content of the resource
6091     * @param properties the new properties of the resource
6092     *
6093     * @throws CmsException if something goes wrong
6094     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6095     *
6096     * @see CmsObject#replaceResource(String, int, byte[], List)
6097     * @see org.opencms.file.types.I_CmsResourceType#replaceResource(CmsObject, CmsSecurityManager, CmsResource, int, byte[], List)
6098     */
6099    @SuppressWarnings("javadoc")
6100    public void replaceResource(
6101        CmsRequestContext context,
6102        CmsResource resource,
6103        int type,
6104        byte[] content,
6105        List<CmsProperty> properties)
6106    throws CmsException, CmsSecurityException {
6107
6108        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6109            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6110            try {
6111                checkOfflineProject(dbc);
6112                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
6113                if (CmsResourceTypeJsp.isJspTypeId(type)) {
6114                    // security check preventing the creation of a jsp file without permissions
6115                    checkRoleForResource(dbc, CmsRole.VFS_MANAGER, resource);
6116                }
6117                m_driverManager.replaceResource(dbc, resource, type, content, properties);
6118                modContext.add(resource);
6119            } catch (Exception e) {
6120                dbc.report(
6121                    null,
6122                    Messages.get().container(Messages.ERR_REPLACE_RESOURCE_1, context.getSitePath(resource)),
6123                    e);
6124            } finally {
6125                dbc.clear();
6126            }
6127        }
6128    }
6129
6130    /**
6131     * Resets the password for a specified user.<p>
6132     *
6133     * @param context the current request context
6134     * @param username the name of the user
6135     * @param oldPassword the old password
6136     * @param secondFactor the additional information for 2FA
6137     * @param newPassword the new password
6138     *
6139     * @throws CmsException if the user data could not be read from the database
6140     * @throws CmsSecurityException if the specified user name and old password could not be verified
6141     */
6142    public void resetPassword(
6143        CmsRequestContext context,
6144        String username,
6145        String oldPassword,
6146        CmsSecondFactorInfo secondFactor,
6147        String newPassword)
6148    throws CmsException, CmsSecurityException {
6149
6150        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6151        try {
6152            m_driverManager.resetPassword(
6153                dbc,
6154                CmsOrganizationalUnit.removeLeadingSeparator(username),
6155                oldPassword,
6156                secondFactor,
6157                newPassword);
6158        } catch (Exception e) {
6159            dbc.report(null, Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), e);
6160        } finally {
6161            dbc.clear();
6162        }
6163    }
6164
6165    /**
6166     * Returns the original path of given resource, that is the online path for the resource.<p>
6167     *
6168     * If it differs from the offline path, the resource has been moved.<p>
6169     *
6170     * @param context the current request context
6171     * @param resource the resource to get the path for
6172     *
6173     * @return the online path
6174     *
6175     * @throws CmsException if something goes wrong
6176     *
6177     * @see org.opencms.workplace.commons.CmsUndoChanges#resourceOriginalPath(CmsObject, String)
6178     */
6179    public String resourceOriginalPath(CmsRequestContext context, CmsResource resource) throws CmsException {
6180
6181        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6182        String result = null;
6183        try {
6184            checkOfflineProject(dbc);
6185            result = m_driverManager.getVfsDriver(
6186                dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getStructureId(), true).getRootPath();
6187        } catch (Exception e) {
6188            dbc.report(
6189                null,
6190                Messages.get().container(
6191                    Messages.ERR_TEST_MOVED_RESOURCE_1,
6192                    dbc.removeSiteRoot(resource.getRootPath())),
6193                e);
6194        } finally {
6195            dbc.clear();
6196        }
6197        return result;
6198    }
6199
6200    /**
6201     * Restores a deleted resource identified by its structure id from the historical archive.<p>
6202     *
6203     * @param context the current request context
6204     * @param structureId the structure id of the resource to restore
6205     *
6206     * @throws CmsException if something goes wrong
6207     *
6208     * @see CmsObject#restoreDeletedResource(CmsUUID)
6209     */
6210    public void restoreDeletedResource(CmsRequestContext context, CmsUUID structureId) throws CmsException {
6211
6212        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6213
6214            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6215            try {
6216                checkOfflineProject(dbc);
6217                // write permissions on parent folder are checked later
6218                CmsResource resource = m_driverManager.restoreDeletedResource(dbc, structureId);
6219                modContext.add(resource);
6220            } catch (Exception e) {
6221                dbc.report(null, Messages.get().container(Messages.ERR_RESTORE_DELETED_RESOURCE_1, structureId), e);
6222            } finally {
6223                dbc.clear();
6224            }
6225        }
6226    }
6227
6228    /**
6229     * Restores a resource in the current project with the given version from the historical archive.<p>
6230     *
6231     * @param context the current request context
6232     * @param resource the resource to restore from the archive
6233     * @param version the version number to restore
6234     *
6235     * @throws CmsException if something goes wrong
6236     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6237     *
6238     * @see CmsObject#restoreResourceVersion(CmsUUID, int)
6239     * @see org.opencms.file.types.I_CmsResourceType#restoreResource(CmsObject, CmsSecurityManager, CmsResource, int)
6240     */
6241    public void restoreResource(CmsRequestContext context, CmsResource resource, int version)
6242    throws CmsException, CmsSecurityException {
6243
6244        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6245            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6246            try {
6247                checkOfflineProject(dbc);
6248                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
6249                m_driverManager.restoreResource(dbc, resource, version);
6250                modContext.add(resource);
6251            } catch (Exception e) {
6252                dbc.report(
6253                    null,
6254                    Messages.get().container(
6255                        Messages.ERR_RESTORE_RESOURCE_2,
6256                        context.getSitePath(resource),
6257                        Integer.valueOf(version)),
6258                    e);
6259            } finally {
6260                dbc.clear();
6261            }
6262        }
6263    }
6264
6265    /**
6266     * Saves the aliases for a given resource.<p>
6267     *
6268     * This method completely replaces any existing aliases for the same structure id.
6269     *
6270     * @param context the request context
6271     * @param resource the resource for which the aliases should be written
6272     * @param aliases the list of aliases to write, where all aliases must have the same structure id as the resource
6273     *
6274     * @throws CmsException if something goes wrong
6275     */
6276    public void saveAliases(CmsRequestContext context, CmsResource resource, List<CmsAlias> aliases)
6277    throws CmsException {
6278
6279        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6280        try {
6281            if ((aliases.size() > 0) && !(resource.getStructureId().equals(aliases.get(0).getStructureId()))) {
6282                throw new IllegalArgumentException("Resource does not match aliases!");
6283            }
6284            checkPermissions(context, resource, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL);
6285            m_driverManager.saveAliases(dbc, context.getCurrentProject(), resource.getStructureId(), aliases);
6286            Map<String, Object> eventData = new HashMap<String, Object>();
6287            eventData.put(I_CmsEventListener.KEY_RESOURCE, resource);
6288            eventData.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CmsDriverManager.CHANGED_RESOURCE));
6289            OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, eventData));
6290        } catch (Exception e) {
6291            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
6292        } finally {
6293            dbc.clear();
6294        }
6295    }
6296
6297    /**
6298     * Replaces the rewrite aliases for a given site root.<p>
6299     *
6300     * @param requestContext the current request context
6301     * @param siteRoot the site root for which the rewrite aliases should be replaced
6302     * @param newAliases the new list of aliases for the given site root
6303     *
6304     * @throws CmsException if something goes wrong
6305     */
6306    public void saveRewriteAliases(CmsRequestContext requestContext, String siteRoot, List<CmsRewriteAlias> newAliases)
6307    throws CmsException {
6308
6309        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
6310        try {
6311            //            checkOfflineProject(dbc);
6312            //            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
6313            m_driverManager.saveRewriteAliases(dbc, siteRoot, newAliases);
6314        } catch (Exception e) {
6315            dbc.report(null, Messages.get().container(Messages.ERR_DB_OPERATION_0), e);
6316        } finally {
6317            dbc.clear();
6318        }
6319    }
6320
6321    /**
6322     * Searches users by search criteria.<p>
6323     *
6324     * @param requestContext the request context
6325     * @param searchParams the search criteria object
6326     *
6327     * @return a list of users
6328     * @throws CmsException if something goes wrong
6329     */
6330    public List<CmsUser> searchUsers(CmsRequestContext requestContext, CmsUserSearchParameters searchParams)
6331    throws CmsException {
6332
6333        CmsDbContext dbc = m_dbContextFactory.getDbContext(requestContext);
6334        try {
6335            return m_driverManager.searchUsers(dbc, searchParams);
6336        } catch (Exception e) {
6337            dbc.report(null, Messages.get().container(Messages.ERR_SEARCH_USERS_0), e);
6338            return null;
6339        } finally {
6340            dbc.clear();
6341        }
6342    }
6343
6344    /**
6345     * Changes the "expire" date of a resource.<p>
6346     *
6347     * @param context the current request context
6348     * @param resource the resource to touch
6349     * @param dateExpired the new expire date of the changed resource
6350     *
6351     * @throws CmsException if something goes wrong
6352     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6353     *
6354     * @see CmsObject#setDateExpired(String, long, boolean)
6355     * @see org.opencms.file.types.I_CmsResourceType#setDateExpired(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
6356     */
6357    public void setDateExpired(CmsRequestContext context, CmsResource resource, long dateExpired)
6358    throws CmsException, CmsSecurityException {
6359
6360        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6361            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6362            try {
6363                checkOfflineProject(dbc);
6364                checkPermissions(
6365                    dbc,
6366                    resource,
6367                    CmsPermissionSet.ACCESS_WRITE,
6368                    true,
6369                    CmsResourceFilter.IGNORE_EXPIRATION);
6370                m_driverManager.setDateExpired(dbc, resource, dateExpired);
6371                modContext.add(resource);
6372            } catch (Exception e) {
6373                dbc.report(
6374                    null,
6375                    Messages.get().container(
6376                        Messages.ERR_SET_DATE_EXPIRED_2,
6377                        new Object[] {new Date(dateExpired), context.getSitePath(resource)}),
6378                    e);
6379            } finally {
6380                dbc.clear();
6381            }
6382        }
6383    }
6384
6385    /**
6386     * Changes the "last modified" time stamp of a resource.<p>
6387     *
6388     * @param context the current request context
6389     * @param resource the resource to touch
6390     * @param dateLastModified the new time stamp of the changed resource
6391     *
6392     * @throws CmsException if something goes wrong
6393     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6394     *
6395     * @see CmsObject#setDateLastModified(String, long, boolean)
6396     * @see org.opencms.file.types.I_CmsResourceType#setDateLastModified(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
6397     */
6398
6399    public void setDateLastModified(CmsRequestContext context, CmsResource resource, long dateLastModified)
6400    throws CmsException, CmsSecurityException {
6401
6402        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6403            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6404            try {
6405                checkOfflineProject(dbc);
6406                checkPermissions(
6407                    dbc,
6408                    resource,
6409                    CmsPermissionSet.ACCESS_WRITE,
6410                    true,
6411                    CmsResourceFilter.IGNORE_EXPIRATION);
6412                m_driverManager.setDateLastModified(dbc, resource, dateLastModified);
6413                modContext.add(resource);
6414            } catch (Exception e) {
6415                dbc.report(
6416                    null,
6417                    Messages.get().container(
6418                        Messages.ERR_SET_DATE_LAST_MODIFIED_2,
6419                        new Object[] {new Date(dateLastModified), context.getSitePath(resource)}),
6420                    e);
6421            } finally {
6422                dbc.clear();
6423            }
6424        }
6425    }
6426
6427    /**
6428     * Changes the "release" date of a resource.<p>
6429     *
6430     * @param context the current request context
6431     * @param resource the resource to touch
6432     * @param dateReleased the new release date of the changed resource
6433     *
6434     * @throws CmsException if something goes wrong
6435     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6436     *
6437     * @see CmsObject#setDateReleased(String, long, boolean)
6438     * @see org.opencms.file.types.I_CmsResourceType#setDateReleased(CmsObject, CmsSecurityManager, CmsResource, long, boolean)
6439     */
6440    public void setDateReleased(CmsRequestContext context, CmsResource resource, long dateReleased)
6441    throws CmsException, CmsSecurityException {
6442
6443        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6444
6445            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6446            try {
6447                checkOfflineProject(dbc);
6448                checkPermissions(
6449                    dbc,
6450                    resource,
6451                    CmsPermissionSet.ACCESS_WRITE,
6452                    true,
6453                    CmsResourceFilter.IGNORE_EXPIRATION);
6454                m_driverManager.setDateReleased(dbc, resource, dateReleased);
6455                modContext.add(resource);
6456            } catch (Exception e) {
6457                dbc.report(
6458                    null,
6459                    Messages.get().container(
6460                        Messages.ERR_SET_DATE_RELEASED_2,
6461                        new Object[] {new Date(dateReleased), context.getSitePath(resource)}),
6462                    e);
6463            } finally {
6464                dbc.clear();
6465            }
6466        }
6467    }
6468
6469    /**
6470     * Sets a new parent-group for an already existing group.<p>
6471     *
6472     * @param context the current request context
6473     * @param groupName the name of the group that should be written
6474     * @param parentGroupName the name of the parent group to set,
6475     *                      or <code>null</code> if the parent
6476     *                      group should be deleted.
6477     *
6478     * @throws CmsException if operation was not successful
6479     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
6480     *
6481     */
6482    public void setParentGroup(CmsRequestContext context, String groupName, String parentGroupName)
6483    throws CmsException, CmsRoleViolationException {
6484
6485        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6486
6487        try {
6488            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(groupName)));
6489            m_driverManager.setParentGroup(
6490                dbc,
6491                CmsOrganizationalUnit.removeLeadingSeparator(groupName),
6492                CmsOrganizationalUnit.removeLeadingSeparator(parentGroupName));
6493        } catch (Exception e) {
6494            dbc.report(null, Messages.get().container(Messages.ERR_SET_PARENT_GROUP_2, parentGroupName, groupName), e);
6495        } finally {
6496            dbc.clear();
6497        }
6498    }
6499
6500    /**
6501     * Sets the password for a user.<p>
6502     *
6503     * @param context the current request context
6504     * @param username the name of the user
6505     * @param newPassword the new password
6506     *
6507     * @throws CmsException if operation was not successful
6508     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
6509     */
6510    public void setPassword(CmsRequestContext context, String username, String newPassword)
6511    throws CmsException, CmsRoleViolationException {
6512
6513        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6514        try {
6515            CmsRole role = CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(username));
6516            checkRoleForUserModification(dbc, username, role);
6517            m_driverManager.setPassword(dbc, CmsOrganizationalUnit.removeLeadingSeparator(username), newPassword);
6518        } catch (Exception e) {
6519            dbc.report(null, Messages.get().container(Messages.ERR_SET_PASSWORD_1, username), e);
6520        } finally {
6521            dbc.clear();
6522        }
6523    }
6524
6525    /**
6526     * Sets/clears the 'restricted' status for the given resource and group.
6527     *
6528     * <p>The 'restricted' status causes files to be inaccessible to users who are not in the group if the file is expired or unreleased.
6529     * <p>It is implemented as an access control entry with the 'responsible' flag, but the permission check for this method is different from the chacc() methods: It doesn't require control
6530     * permissions on the target resource, but the user has to be a member of the given group and have write access to the resource.
6531     *
6532     * @param context the current request context
6533     * @param resource the target resource
6534     * @param group a group (current user must be a member)
6535     * @param restricted true if the restriction status should be set
6536     * @throws CmsException if something goes wrong
6537     */
6538    public void setRestricted(CmsRequestContext context, CmsResource resource, CmsGroup group, boolean restricted)
6539    throws CmsException {
6540
6541        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
6542            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6543            try {
6544                checkOfflineProject(dbc);
6545                if (!resource.isFile()) {
6546                    throw new RestrictionNotSupportedForFoldersException();
6547                }
6548                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
6549
6550                if (!hasRole(context, context.getCurrentUser(), CmsAccessRestrictionInfo.ROLE_CAN_IGNORE_GROUP)
6551                    && !userInGroup(context, context.getCurrentUser().getName(), group.getName())) {
6552                    throw new RestrictionGroupMembershipException();
6553                }
6554                List<CmsAccessControlEntry> aces = getAccessControlEntries(context, resource, false);
6555                CmsAccessControlEntry foundAce = null;
6556                for (CmsAccessControlEntry ace : aces) {
6557                    if (ace.getPrincipal().equals(group.getId())) {
6558                        foundAce = ace;
6559                        break;
6560                    }
6561                }
6562                CmsAccessControlEntry aceToWrite = null;
6563                if (foundAce != null) {
6564                    // make a copy so we can compare it to the original later
6565                    aceToWrite = new CmsAccessControlEntry(
6566                        foundAce.getResource(),
6567                        foundAce.getPrincipal(),
6568                        foundAce.getAllowedPermissions(),
6569                        foundAce.getDeniedPermissions(),
6570                        foundAce.getFlags());
6571                    if (restricted) {
6572                        aceToWrite.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE);
6573                    } else {
6574                        aceToWrite.resetFlags(CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE);
6575                    }
6576                    if ((aceToWrite.getAllowedPermissions() == 0)
6577                        && (aceToWrite.getDeniedPermissions() == 0)
6578                        && ((aceToWrite.getFlags() & ~CmsAccessControlEntry.ACCESS_FLAGS_GROUP) == 0)) {
6579                        // an empty ACE (no permissions, no flags except group marker) is equivalent to no ACE at all - delete the existing one
6580                        m_driverManager.removeAccessControlEntry(dbc, resource, group.getId());
6581                        modContext.add(resource);
6582                    } else if (!aceToWrite.equals(foundAce)) {
6583                        m_driverManager.writeAccessControlEntry(dbc, resource, aceToWrite);
6584                        modContext.add(resource);
6585                    }
6586                } else if (restricted) { // if restricted=false and no entry is found, we don't need to change anything
6587                    int flags = CmsAccessControlEntry.ACCESS_FLAGS_GROUP
6588                        | CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE;
6589                    aceToWrite = new CmsAccessControlEntry(resource.getResourceId(), group.getId(), 0, 0, flags);
6590                    m_driverManager.writeAccessControlEntry(dbc, resource, aceToWrite);
6591                    modContext.add(resource);
6592                }
6593            } catch (Exception e) {
6594                dbc.report(
6595                    null,
6596                    Messages.get().container(Messages.ERR_WRITE_ACL_ENTRY_1, context.getSitePath(resource)),
6597                    e);
6598            } finally {
6599                dbc.clear();
6600            }
6601        }
6602
6603    }
6604
6605    /**
6606     * Marks a subscribed resource as deleted.<p>
6607     *
6608     * @param context the request context
6609     * @param poolName the name of the database pool to use
6610     * @param resource the subscribed resource to mark as deleted
6611     *
6612     * @throws CmsException if something goes wrong
6613     */
6614    public void setSubscribedResourceAsDeleted(CmsRequestContext context, String poolName, CmsResource resource)
6615    throws CmsException {
6616
6617        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6618        try {
6619            m_driverManager.setSubscribedResourceAsDeleted(dbc, poolName, resource);
6620        } catch (Exception e) {
6621
6622            dbc.report(
6623                null,
6624                Messages.get().container(
6625                    Messages.ERR_SET_SUBSCRIBED_RESOURCE_AS_DELETED_1,
6626                    context.getSitePath(resource)),
6627                e);
6628
6629        } finally {
6630            dbc.clear();
6631        }
6632    }
6633
6634    /**
6635     * Moves an user to the given organizational unit.<p>
6636     *
6637     * @param context the current request context
6638     * @param orgUnit the organizational unit to add the principal to
6639     * @param user the user that is to be move to the organizational unit
6640     *
6641     * @throws CmsException if something goes wrong
6642     *
6643     * @see org.opencms.security.CmsOrgUnitManager#setUsersOrganizationalUnit(CmsObject, String, String)
6644     */
6645    public void setUsersOrganizationalUnit(CmsRequestContext context, CmsOrganizationalUnit orgUnit, CmsUser user)
6646    throws CmsException {
6647
6648        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6649        try {
6650            checkRole(dbc, CmsRole.ADMINISTRATOR.forOrgUnit(orgUnit.getName()));
6651            checkOfflineProject(dbc);
6652            m_driverManager.setUsersOrganizationalUnit(dbc, orgUnit, user);
6653        } catch (Exception e) {
6654            dbc.report(
6655                null,
6656                Messages.get().container(Messages.ERR_SET_USERS_ORGUNIT_2, orgUnit.getName(), user.getName()),
6657                e);
6658        } finally {
6659            dbc.clear();
6660        }
6661    }
6662
6663    /**
6664     * Subscribes the user or group to the resource.<p>
6665     *
6666     * @param context the request context
6667     * @param poolName the name of the database pool to use
6668     * @param principal the principal that subscribes to the resource
6669     * @param resource the resource to subscribe to
6670     *
6671     * @throws CmsException if something goes wrong
6672     */
6673    public void subscribeResourceFor(
6674        CmsRequestContext context,
6675        String poolName,
6676        CmsPrincipal principal,
6677        CmsResource resource)
6678    throws CmsException {
6679
6680        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6681        try {
6682            m_driverManager.subscribeResourceFor(dbc, poolName, principal, resource);
6683        } catch (Exception e) {
6684            if (principal instanceof CmsUser) {
6685                dbc.report(
6686                    null,
6687                    Messages.get().container(
6688                        Messages.ERR_SUBSCRIBE_RESOURCE_FOR_USER_2,
6689                        context.getSitePath(resource),
6690                        principal.getName()),
6691                    e);
6692            } else {
6693                dbc.report(
6694                    null,
6695                    Messages.get().container(
6696                        Messages.ERR_SUBSCRIBE_RESOURCE_FOR_GROUP_2,
6697                        context.getSitePath(resource),
6698                        principal.getName()),
6699                    e);
6700            }
6701        } finally {
6702            dbc.clear();
6703        }
6704    }
6705
6706    /**
6707     * Undelete the resource by resetting it's state.<p>
6708     *
6709     * @param context the current request context
6710     * @param resource the name of the resource to apply this operation to
6711     *
6712     * @throws CmsException if something goes wrong
6713     *
6714     * @see CmsObject#undeleteResource(String, boolean)
6715     * @see org.opencms.file.types.I_CmsResourceType#undelete(CmsObject, CmsSecurityManager, CmsResource, boolean)
6716     */
6717    public void undelete(CmsRequestContext context, CmsResource resource) throws CmsException {
6718
6719        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6720        try {
6721            checkOfflineProject(dbc);
6722            checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
6723            checkSystemLocks(dbc, resource);
6724
6725            m_driverManager.undelete(dbc, resource);
6726        } catch (Exception e) {
6727            dbc.report(
6728                null,
6729                Messages.get().container(Messages.ERR_UNDELETE_FOR_RESOURCE_1, context.getSitePath(resource)),
6730                e);
6731        } finally {
6732            dbc.clear();
6733        }
6734    }
6735
6736    /**
6737     * Undos all changes in the resource by restoring the version from the
6738     * online project to the current offline project.<p>
6739     *
6740     * @param context the current request context
6741     * @param resource the name of the resource to apply this operation to
6742     * @param mode the undo mode, one of the <code>{@link CmsResource}#UNDO_XXX</code> constants
6743     *
6744     * @throws CmsException if something goes wrong
6745     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6746     *
6747     * @see CmsObject#undoChanges(String, CmsResource.CmsResourceUndoMode)
6748     * @see org.opencms.file.types.I_CmsResourceType#undoChanges(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode)
6749     */
6750    public void undoChanges(CmsRequestContext context, CmsResource resource, CmsResource.CmsResourceUndoMode mode)
6751    throws CmsException, CmsSecurityException {
6752
6753        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6754        try {
6755            checkOfflineProject(dbc);
6756            checkPermissions(
6757                dbc,
6758                resource,
6759                CmsPermissionSet.ACCESS_WRITE,
6760                resource.isFile() || mode.isRecursive() || (mode == CmsResource.UNDO_MOVE_CONTENT)
6761                ? LockCheck.yes
6762                : LockCheck.shallowOnly,
6763                CmsResourceFilter.ALL);
6764            checkSystemLocks(dbc, resource);
6765
6766            m_driverManager.undoChanges(dbc, resource, mode);
6767        } catch (Exception e) {
6768            dbc.report(
6769                null,
6770                Messages.get().container(Messages.ERR_UNDO_CHANGES_FOR_RESOURCE_1, context.getSitePath(resource)),
6771                e);
6772        } finally {
6773            dbc.clear();
6774        }
6775    }
6776
6777    /**
6778     * Unlocks all resources in this project.<p>
6779     *
6780     * @param context the current request context
6781     * @param projectId the id of the project to be published
6782     *
6783     * @throws CmsException if something goes wrong
6784     * @throws CmsRoleViolationException if the current user does not own the required permissions
6785     */
6786    public void unlockProject(CmsRequestContext context, CmsUUID projectId)
6787    throws CmsException, CmsRoleViolationException {
6788
6789        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6790        CmsProject project = m_driverManager.readProject(dbc, projectId);
6791
6792        try {
6793            checkManagerOfProjectRole(dbc, project);
6794            m_driverManager.unlockProject(project);
6795        } catch (Exception e) {
6796            dbc.report(
6797                null,
6798                Messages.get().container(Messages.ERR_UNLOCK_PROJECT_2, projectId, dbc.currentUser().getName()),
6799                e);
6800        } finally {
6801            dbc.clear();
6802        }
6803    }
6804
6805    /**
6806     * Unlocks a resource.<p>
6807     *
6808     * @param context the current request context
6809     * @param resource the resource to unlock
6810     *
6811     * @throws CmsException if something goes wrong
6812     * @throws CmsSecurityException if the user has insufficient permission for the given resource (write access permission is required)
6813     *
6814     * @see CmsObject#unlockResource(String)
6815     * @see org.opencms.file.types.I_CmsResourceType#unlockResource(CmsObject, CmsSecurityManager, CmsResource)
6816     */
6817    public void unlockResource(CmsRequestContext context, CmsResource resource)
6818    throws CmsException, CmsSecurityException {
6819
6820        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6821        try {
6822            checkOfflineProject(dbc);
6823            checkPermissions(
6824                dbc,
6825                resource,
6826                CmsPermissionSet.ACCESS_WRITE,
6827                LockCheck.shallowOnly,
6828                CmsResourceFilter.ALL);
6829            m_driverManager.unlockResource(dbc, resource, false, false);
6830        } catch (CmsException e) {
6831            dbc.report(
6832                null,
6833                Messages.get().container(
6834                    Messages.ERR_UNLOCK_RESOURCE_3,
6835                    context.getSitePath(resource),
6836                    dbc.currentUser().getName(),
6837                    e.getLocalizedMessage(dbc.getRequestContext().getLocale())),
6838                e);
6839        } finally {
6840            dbc.clear();
6841        }
6842    }
6843
6844    /**
6845     * Unsubscribes all deleted resources that were deleted before the specified time stamp.<p>
6846     *
6847     * @param context the request context
6848     * @param poolName the name of the database pool to use
6849     * @param deletedTo the time stamp to which the resources have been deleted
6850     *
6851     * @throws CmsException if something goes wrong
6852     */
6853    public void unsubscribeAllDeletedResources(CmsRequestContext context, String poolName, long deletedTo)
6854    throws CmsException {
6855
6856        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6857        try {
6858            m_driverManager.unsubscribeAllDeletedResources(dbc, poolName, deletedTo);
6859        } catch (Exception e) {
6860
6861            dbc.report(null, Messages.get().container(Messages.ERR_UNSUBSCRIBE_ALL_DELETED_RESOURCES_USER_0), e);
6862
6863        } finally {
6864            dbc.clear();
6865        }
6866    }
6867
6868    /**
6869     * Unsubscribes the user or group from all resources.<p>
6870     *
6871     * @param context the request context
6872     * @param poolName the name of the database pool to use
6873     * @param principal the principal that unsubscribes from all resources
6874     *
6875     * @throws CmsException if something goes wrong
6876     */
6877    public void unsubscribeAllResourcesFor(CmsRequestContext context, String poolName, CmsPrincipal principal)
6878    throws CmsException {
6879
6880        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6881        try {
6882            m_driverManager.unsubscribeAllResourcesFor(dbc, poolName, principal);
6883        } catch (Exception e) {
6884            if (principal instanceof CmsUser) {
6885                dbc.report(
6886                    null,
6887                    Messages.get().container(Messages.ERR_UNSUBSCRIBE_ALL_RESOURCES_USER_1, principal.getName()),
6888                    e);
6889            } else {
6890                dbc.report(
6891                    null,
6892                    Messages.get().container(Messages.ERR_UNSUBSCRIBE_ALL_RESOURCES_GROUP_1, principal.getName()),
6893                    e);
6894            }
6895        } finally {
6896            dbc.clear();
6897        }
6898    }
6899
6900    /**
6901     * Unsubscribes the principal from the resource.<p>
6902     *
6903     * @param context the request context
6904     * @param poolName the name of the database pool to use
6905     * @param principal the principal that unsubscribes from the resource
6906     * @param resource the resource to unsubscribe from
6907     *
6908     * @throws CmsException if something goes wrong
6909     */
6910    public void unsubscribeResourceFor(
6911        CmsRequestContext context,
6912        String poolName,
6913        CmsPrincipal principal,
6914        CmsResource resource)
6915    throws CmsException {
6916
6917        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6918        try {
6919            m_driverManager.unsubscribeResourceFor(dbc, poolName, principal, resource);
6920        } catch (Exception e) {
6921            dbc.report(
6922                null,
6923                Messages.get().container(
6924                    Messages.ERR_UNSUBSCRIBE_RESOURCE_FOR_GROUP_2,
6925                    context.getSitePath(resource),
6926                    principal.getName()),
6927                e);
6928        } finally {
6929            dbc.clear();
6930        }
6931    }
6932
6933    /**
6934     * Unsubscribes all groups and users from the resource.<p>
6935     *
6936     * @param context the request context
6937     * @param poolName the name of the database pool to use
6938     * @param resource the resource to unsubscribe all groups and users from
6939     *
6940     * @throws CmsException if something goes wrong
6941     */
6942    public void unsubscribeResourceForAll(CmsRequestContext context, String poolName, CmsResource resource)
6943    throws CmsException {
6944
6945        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6946        try {
6947            m_driverManager.unsubscribeResourceForAll(dbc, poolName, resource);
6948        } catch (Exception e) {
6949            dbc.report(
6950                null,
6951                Messages.get().container(Messages.ERR_UNSUBSCRIBE_RESOURCE_ALL_1, context.getSitePath(resource)),
6952                e);
6953        } finally {
6954            dbc.clear();
6955        }
6956    }
6957
6958    /**
6959     * Updates the last login date on the given user to the current time.<p>
6960     *
6961     * @param context the current request context
6962     * @param user the user to be updated
6963     *
6964     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER} for the current project
6965     * @throws CmsException if operation was not successful
6966     */
6967    public void updateLastLoginDate(CmsRequestContext context, CmsUser user)
6968    throws CmsException, CmsRoleViolationException {
6969
6970        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
6971        try {
6972            CmsRole role = CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(user.getName()));
6973            checkRoleForUserModification(dbc, user.getName(), role);
6974            m_driverManager.updateLastLoginDate(dbc, user);
6975        } catch (Exception e) {
6976            dbc.report(null, Messages.get().container(Messages.ERR_WRITE_USER_1, user.getName()), e);
6977        } finally {
6978            dbc.clear();
6979        }
6980    }
6981
6982    /**
6983     * Logs everything that has not been written to DB jet.<p>
6984     *
6985     * @throws CmsException if something goes wrong
6986     */
6987    public void updateLog() throws CmsException {
6988
6989        if (m_dbContextFactory == null) {
6990            // already shutdown
6991            return;
6992        }
6993        CmsDbContext dbc = m_dbContextFactory.getDbContext();
6994        try {
6995            m_driverManager.updateLog(dbc);
6996        } finally {
6997            dbc.clear();
6998        }
6999    }
7000
7001    /**
7002     * Updates/Creates the relations for the given resource.<p>
7003     *
7004     * @param context the current user context
7005     * @param resource the resource to update the relations for
7006     * @param relations the relations to update
7007     * @param updateSiblingState if true, sets the state of siblings with changed relations to 'changed' (unless they are new or deleted)
7008     *
7009     * @throws CmsException if something goes wrong
7010     *
7011     * @see CmsDriverManager#updateRelationsForResource(CmsDbContext, CmsResource, List, boolean)
7012     */
7013    public void updateRelationsForResource(
7014        CmsRequestContext context,
7015        CmsResource resource,
7016        List<CmsLink> relations,
7017        boolean updateSiblingState)
7018    throws CmsException {
7019
7020        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7021        try {
7022            m_driverManager.updateRelationsForResource(dbc, resource, relations, updateSiblingState);
7023        } catch (Exception e) {
7024            dbc.report(
7025                null,
7026                Messages.get().container(Messages.ERR_UPDATE_RELATIONS_1, dbc.removeSiteRoot(resource.getRootPath())),
7027                e);
7028        } finally {
7029            dbc.clear();
7030        }
7031    }
7032
7033    /**
7034     * Tests if a user is member of the given group.<p>
7035     *
7036     * @param context the current request context
7037     * @param username the name of the user to check
7038     * @param groupname the name of the group to check
7039     *
7040     * @return <code>true</code>, if the user is in the group; or <code>false</code> otherwise
7041     *
7042     * @throws CmsException if operation was not successful
7043     */
7044    public boolean userInGroup(CmsRequestContext context, String username, String groupname) throws CmsException {
7045
7046        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7047        boolean result = false;
7048        try {
7049            result = m_driverManager.userInGroup(
7050                dbc,
7051                CmsOrganizationalUnit.removeLeadingSeparator(username),
7052                CmsOrganizationalUnit.removeLeadingSeparator(groupname),
7053                false);
7054        } catch (Exception e) {
7055            dbc.report(null, Messages.get().container(Messages.ERR_USER_IN_GROUP_2, username, groupname), e);
7056        } finally {
7057            dbc.clear();
7058        }
7059        return result;
7060    }
7061
7062    /**
7063     * Checks if a new password follows the rules for
7064     * new passwords, which are defined by a Class implementing the
7065     * <code>{@link org.opencms.security.I_CmsPasswordHandler}</code>
7066     * interface and configured in the opencms.properties file.<p>
7067     *
7068     * If this method throws no exception the password is valid.<p>
7069     *
7070     * @param password the new password that has to be checked
7071     *
7072     * @throws CmsSecurityException if the password is not valid
7073     */
7074    public void validatePassword(String password) throws CmsSecurityException {
7075
7076        m_driverManager.validatePassword(password);
7077    }
7078
7079    /**
7080     * Validates the relations for the given resources.<p>
7081     *
7082     * @param context the current request context
7083     * @param publishList the resources to validate during publishing
7084     * @param report a report to write the messages to
7085     *
7086     * @return a map with lists of invalid links
7087     *          (<code>{@link org.opencms.relations.CmsRelation}}</code> objects)
7088     *          keyed by root paths
7089     *
7090     * @throws Exception if something goes wrong
7091     */
7092    public Map<String, List<CmsRelation>> validateRelations(
7093        CmsRequestContext context,
7094        CmsPublishList publishList,
7095        I_CmsReport report)
7096    throws Exception {
7097
7098        Map<String, List<CmsRelation>> result = null;
7099        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7100        try {
7101            result = m_driverManager.validateRelations(dbc, publishList, report);
7102        } catch (Exception e) {
7103            dbc.report(null, Messages.get().container(Messages.ERR_VALIDATE_RELATIONS_0), e);
7104        } finally {
7105            dbc.clear();
7106        }
7107        return result;
7108    }
7109
7110    /**
7111     * Writes an access control entries to a given resource.<p>
7112     *
7113     * @param context the current request context
7114     * @param resource the resource
7115     * @param ace the entry to write
7116     *
7117     * @throws CmsSecurityException if the user has insufficient permission for the given resource ({@link CmsPermissionSet#ACCESS_CONTROL} required)
7118     * @throws CmsException if something goes wrong
7119     */
7120    public void writeAccessControlEntry(CmsRequestContext context, CmsResource resource, CmsAccessControlEntry ace)
7121    throws CmsException, CmsSecurityException {
7122
7123        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7124        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7125
7126            try {
7127                checkOfflineProject(dbc);
7128                checkPermissions(
7129                    dbc,
7130                    resource,
7131                    CmsPermissionSet.ACCESS_CONTROL,
7132                    LockCheck.shallowOnly,
7133                    CmsResourceFilter.ALL);
7134                if (ace.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_ID)) {
7135                    // only vfs managers can set the overwrite all ACE
7136                    checkRoleForResource(dbc, CmsRole.VFS_MANAGER, resource);
7137                }
7138                m_driverManager.writeAccessControlEntry(dbc, resource, ace);
7139                modContext.add(resource);
7140            } catch (Exception e) {
7141                dbc.report(
7142                    null,
7143                    Messages.get().container(Messages.ERR_WRITE_ACL_ENTRY_1, context.getSitePath(resource)),
7144                    e);
7145            } finally {
7146                dbc.clear();
7147            }
7148        }
7149    }
7150
7151    /**
7152     * Writes a resource to the OpenCms VFS, including it's content.<p>
7153     *
7154     * Applies only to resources of type <code>{@link CmsFile}</code>
7155     * i.e. resources that have a binary content attached.<p>
7156     *
7157     * Certain resource types might apply content validation or transformation rules
7158     * before the resource is actually written to the VFS. The returned result
7159     * might therefore be a modified version from the provided original.<p>
7160     *
7161     * @param context the current request context
7162     * @param resource the resource to apply this operation to
7163     *
7164     * @return the written resource (may have been modified)
7165     *
7166     * @throws CmsSecurityException if the user has insufficient permission for the given resource ({@link CmsPermissionSet#ACCESS_WRITE} required)
7167     * @throws CmsException if something goes wrong
7168     *
7169     * @see CmsObject#writeFile(CmsFile)
7170     * @see org.opencms.file.types.I_CmsResourceType#writeFile(CmsObject, CmsSecurityManager, CmsFile)
7171     */
7172    public CmsFile writeFile(CmsRequestContext context, CmsFile resource) throws CmsException, CmsSecurityException {
7173
7174        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7175            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7176            CmsFile result = null;
7177            try {
7178                checkOfflineProject(dbc);
7179                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
7180
7181                result = m_driverManager.writeFile(dbc, resource);
7182                modContext.add(result.getCopy());
7183            } catch (Exception e) {
7184                dbc.report(null, Messages.get().container(Messages.ERR_WRITE_FILE_1, context.getSitePath(resource)), e);
7185            } finally {
7186                dbc.clear();
7187            }
7188            return result;
7189        }
7190    }
7191
7192    /**
7193     * Writes an already existing group.<p>
7194     *
7195     * The group id has to be a valid OpenCms group id.<br>
7196     *
7197     * The group with the given id will be completely overridden
7198     * by the given data.<p>
7199     *
7200     * @param context the current request context
7201     * @param group the group that should be written
7202     *
7203     * @throws CmsRoleViolationException if the current user does not own the role {@link CmsRole#ACCOUNT_MANAGER} for the current project
7204     * @throws CmsException if operation was not successful
7205     */
7206    public void writeGroup(CmsRequestContext context, CmsGroup group) throws CmsException, CmsRoleViolationException {
7207
7208        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7209        try {
7210            checkRole(dbc, CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(group.getName())));
7211            m_driverManager.writeGroup(dbc, group);
7212        } catch (Exception e) {
7213            dbc.report(null, Messages.get().container(Messages.ERR_WRITE_GROUP_1, group.getName()), e);
7214        } finally {
7215            dbc.clear();
7216        }
7217    }
7218
7219    /**
7220     * Creates a historical entry of the current project.<p>
7221     *
7222     * @param context the current request context
7223     * @param publishTag the correlative publish tag
7224     * @param publishDate the date of publishing
7225     *
7226     * @throws CmsException if operation was not successful
7227     */
7228    public void writeHistoryProject(CmsRequestContext context, int publishTag, long publishDate) throws CmsException {
7229
7230        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7231        try {
7232            m_driverManager.writeHistoryProject(dbc, publishTag, publishDate);
7233        } catch (Exception e) {
7234            dbc.report(
7235                null,
7236                Messages.get().container(
7237                    Messages.ERR_HISTORY_PROJECT_4,
7238                    new Object[] {
7239                        Integer.valueOf(publishTag),
7240                        dbc.currentProject().getName(),
7241                        dbc.currentProject().getUuid(),
7242                        Long.valueOf(publishDate)}),
7243                e);
7244        } finally {
7245            dbc.clear();
7246        }
7247    }
7248
7249    /**
7250     * Writes the locks that are currently stored in-memory to the database to allow restoring them in
7251     * later startups.<p>
7252     *
7253     * This overwrites the locks previously stored in the underlying database table.<p>
7254     *
7255     * @throws CmsException if something goes wrong
7256     */
7257    public void writeLocks() throws CmsException {
7258
7259        if (m_dbContextFactory == null) {
7260            // already shutdown
7261            return;
7262        }
7263        CmsDbContext dbc = m_dbContextFactory.getDbContext();
7264        try {
7265            m_driverManager.writeLocks(dbc);
7266        } finally {
7267            dbc.clear();
7268        }
7269    }
7270
7271    /**
7272     * Writes an already existing organizational unit.<p>
7273     *
7274     * The organizational unit id has to be a valid OpenCms organizational unit id.<p>
7275     *
7276     * The organizational unit with the given id will be completely overridden
7277     * by the given data.<p>
7278     *
7279     * @param context the current request context
7280     * @param organizationalUnit the organizational unit that should be written
7281     *
7282     * @throws CmsException if operation was not successful
7283     *
7284     * @see org.opencms.security.CmsOrgUnitManager#writeOrganizationalUnit(CmsObject, CmsOrganizationalUnit)
7285     */
7286    public void writeOrganizationalUnit(CmsRequestContext context, CmsOrganizationalUnit organizationalUnit)
7287    throws CmsException {
7288
7289        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7290        try {
7291            checkRole(dbc, CmsRole.ADMINISTRATOR.forOrgUnit(organizationalUnit.getName()));
7292            checkOfflineProject(dbc);
7293            m_driverManager.writeOrganizationalUnit(dbc, organizationalUnit);
7294        } catch (Exception e) {
7295            dbc.report(null, Messages.get().container(Messages.ERR_WRITE_ORGUNIT_1, organizationalUnit.getName()), e);
7296        } finally {
7297            dbc.clear();
7298        }
7299    }
7300
7301    /**
7302     * Writes an already existing project.<p>
7303     *
7304     * The project id has to be a valid OpenCms project id.<br>
7305     *
7306     * The project with the given id will be completely overridden
7307     * by the given data.<p>
7308     *
7309     * @param project the project that should be written
7310     * @param context the current request context
7311     *
7312     * @throws CmsRoleViolationException if the current user does not own the required permissions
7313     * @throws CmsException if operation was not successful
7314     */
7315    public void writeProject(CmsRequestContext context, CmsProject project)
7316    throws CmsRoleViolationException, CmsException {
7317
7318        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7319        try {
7320            checkManagerOfProjectRole(dbc, project);
7321            m_driverManager.writeProject(dbc, project);
7322        } catch (Exception e) {
7323            dbc.report(null, Messages.get().container(Messages.ERR_WRITE_PROJECT_1, project.getName()), e);
7324        } finally {
7325            dbc.clear();
7326        }
7327    }
7328
7329    /**
7330     * Writes a property for a specified resource.<p>
7331     *
7332     * @param context the current request context
7333     * @param resource the resource to write the property for
7334     * @param property the property to write
7335     *
7336     * @throws CmsException if something goes wrong
7337     * @throws CmsSecurityException if the user has insufficient permission for the given resource ({@link CmsPermissionSet#ACCESS_WRITE} required)
7338     *
7339     * @see CmsObject#writePropertyObject(String, CmsProperty)
7340     * @see org.opencms.file.types.I_CmsResourceType#writePropertyObject(CmsObject, CmsSecurityManager, CmsResource, CmsProperty)
7341     */
7342    public void writePropertyObject(CmsRequestContext context, CmsResource resource, CmsProperty property)
7343    throws CmsException, CmsSecurityException {
7344
7345        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7346
7347            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7348            try {
7349                checkOfflineProject(dbc);
7350                checkPermissions(
7351                    dbc,
7352                    resource,
7353                    CmsPermissionSet.ACCESS_WRITE,
7354                    LockCheck.shallowOnly,
7355                    CmsResourceFilter.IGNORE_EXPIRATION);
7356                m_driverManager.writePropertyObject(dbc, resource, property);
7357                modContext.add(resource);
7358            } catch (Exception e) {
7359                dbc.report(
7360                    null,
7361                    Messages.get().container(
7362                        Messages.ERR_WRITE_PROP_2,
7363                        property.getName(),
7364                        context.getSitePath(resource)),
7365                    e);
7366            } finally {
7367                dbc.clear();
7368            }
7369        }
7370    }
7371
7372    /**
7373     * Writes a list of properties for a specified resource.<p>
7374     *
7375     * Code calling this method has to ensure that the no properties
7376     * <code>a, b</code> are contained in the specified list so that <code>a.equals(b)</code>,
7377     * otherwise an exception is thrown.<p>
7378     *
7379     * @param context the current request context
7380     * @param resource the resource to write the properties for
7381     * @param properties the list of properties to write
7382     *
7383     * @throws CmsException if something goes wrong
7384     * @throws CmsSecurityException if the user has insufficient permission for the given resource ({@link CmsPermissionSet#ACCESS_WRITE} required)
7385     *
7386     * @see CmsObject#writePropertyObjects(String, List)
7387     * @see org.opencms.file.types.I_CmsResourceType#writePropertyObjects(CmsObject, CmsSecurityManager, CmsResource, List)
7388     */
7389    public void writePropertyObjects(CmsRequestContext context, CmsResource resource, List<CmsProperty> properties)
7390    throws CmsException, CmsSecurityException {
7391
7392        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7393
7394            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7395            try {
7396                checkOfflineProject(dbc);
7397                checkPermissions(
7398                    dbc,
7399                    resource,
7400                    CmsPermissionSet.ACCESS_WRITE,
7401                    LockCheck.shallowOnly,
7402                    CmsResourceFilter.IGNORE_EXPIRATION);
7403                // write the properties
7404                m_driverManager.writePropertyObjects(dbc, resource, properties, true);
7405                modContext.add(resource);
7406            } catch (Exception e) {
7407                dbc.report(
7408                    null,
7409                    Messages.get().container(Messages.ERR_WRITE_PROPS_1, context.getSitePath(resource)),
7410                    e);
7411            } finally {
7412                dbc.clear();
7413            }
7414        }
7415    }
7416
7417    /**
7418     * Writes a resource to the OpenCms VFS.<p>
7419     *
7420     * @param context the current request context
7421     * @param resource the resource to write
7422     *
7423     * @throws CmsSecurityException if the user has insufficient permission for the given resource ({@link CmsPermissionSet#ACCESS_WRITE} required)
7424     * @throws CmsException if something goes wrong
7425     */
7426    public void writeResource(CmsRequestContext context, CmsResource resource)
7427    throws CmsException, CmsSecurityException {
7428
7429        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7430            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7431            try {
7432                checkOfflineProject(dbc);
7433                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
7434                m_driverManager.writeResource(dbc, resource);
7435                modContext.add(resource);
7436            } catch (Exception e) {
7437                dbc.report(
7438                    null,
7439                    Messages.get().container(Messages.ERR_WRITE_RESOURCE_1, context.getSitePath(resource)),
7440                    e);
7441            } finally {
7442                dbc.clear();
7443            }
7444        }
7445    }
7446
7447    /**
7448     * Writes the 'projectlastmodified' field of a resource record.<p>
7449     *
7450     * @param context the current database context
7451     * @param resource the resource which should be modified
7452     * @param project the project whose project id should be written into the resource record
7453     *
7454     * @throws CmsException if something goes wrong
7455     */
7456    public void writeResourceProjectLastModified(CmsRequestContext context, CmsResource resource, CmsProject project)
7457    throws CmsException {
7458
7459        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7460
7461            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7462            try {
7463                checkOfflineProject(dbc);
7464                checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_WRITE, true, CmsResourceFilter.ALL);
7465                m_driverManager.writeProjectLastModified(dbc, resource, project.getUuid());
7466                modContext.add(resource);
7467            } catch (Exception e) {
7468                dbc.report(
7469                    null,
7470                    Messages.get().container(Messages.ERR_WRITE_RESOURCE_1, context.getSitePath(resource)),
7471                    e);
7472            } finally {
7473                dbc.clear();
7474            }
7475        }
7476    }
7477
7478    /**
7479     * Inserts an entry in the published resource table.<p>
7480     *
7481     * This is done during static export.<p>
7482     *
7483     * @param context the current request context
7484     * @param resourceName The name of the resource to be added to the static export
7485     * @param linkType the type of resource exported (0= non-parameter, 1=parameter)
7486     * @param linkParameter the parameters added to the resource
7487     * @param timestamp a time stamp for writing the data into the db
7488     *
7489     * @throws CmsException if something goes wrong
7490     */
7491    public void writeStaticExportPublishedResource(
7492        CmsRequestContext context,
7493        String resourceName,
7494        int linkType,
7495        String linkParameter,
7496        long timestamp)
7497    throws CmsException {
7498
7499        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7500        try {
7501            m_driverManager.writeStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter, timestamp);
7502        } catch (Exception e) {
7503            dbc.report(
7504                null,
7505                Messages.get().container(
7506                    Messages.ERR_WRITE_STATEXP_PUBLISHED_RESOURCES_3,
7507                    resourceName,
7508                    linkParameter,
7509                    new Date(timestamp)),
7510                e);
7511        } finally {
7512            dbc.clear();
7513        }
7514    }
7515
7516    /**
7517     * Writes a new URL name mapping for a given resource.<p>
7518     *
7519     * The first name from the given sequence which is not already mapped to another resource will be used for
7520     * the URL name mapping.<p>
7521     *
7522     * @param context the request context
7523     * @param nameSeq the sequence of URL name candidates
7524     * @param structureId the structure id which should be mapped to the name
7525     * @param locale the locale for the mapping
7526     * @param replaceOnPublish mappings for which this is set will replace all other mappings for the same resource on publishing
7527     *
7528     * @return the name which was actually mapped to the structure id
7529     *
7530     * @throws CmsException if something goes wrong
7531     */
7532    public String writeUrlNameMapping(
7533        CmsRequestContext context,
7534        Iterator<String> nameSeq,
7535        CmsUUID structureId,
7536        String locale,
7537        boolean replaceOnPublish)
7538    throws CmsException {
7539
7540        try (CmsModificationContext modContext = CmsModificationContext.acquire(context)) {
7541
7542            CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7543            try {
7544                String result = m_driverManager.writeUrlNameMapping(
7545                    dbc,
7546                    nameSeq,
7547                    structureId,
7548                    locale,
7549                    replaceOnPublish);
7550                modContext.addId(structureId);
7551                return result;
7552            } catch (Exception e) {
7553                CmsMessageContainer message = Messages.get().container(
7554                    Messages.ERR_ADD_URLNAME_MAPPING_2,
7555                    nameSeq.toString(),
7556                    structureId.toString());
7557                dbc.report(null, message, e);
7558                return null;
7559            } finally {
7560                dbc.clear();
7561            }
7562        }
7563
7564    }
7565
7566    /**
7567     * Updates the user information. <p>
7568     *
7569     * The user id has to be a valid OpenCms user id.<br>
7570     *
7571     * The user with the given id will be completely overridden
7572     * by the given data.<p>
7573     *
7574     * @param context the current request context
7575     * @param user the user to be updated
7576     *
7577     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER} for the current project
7578     * @throws CmsException if operation was not successful
7579     */
7580    public void writeUser(CmsRequestContext context, CmsUser user) throws CmsException, CmsRoleViolationException {
7581
7582        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
7583        try {
7584            CmsRole role = CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(user.getName()));
7585            checkRoleForUserModification(dbc, user.getName(), role);
7586            m_driverManager.writeUser(dbc, user);
7587        } catch (Exception e) {
7588            dbc.report(null, Messages.get().container(Messages.ERR_WRITE_USER_1, user.getName()), e);
7589        } finally {
7590            dbc.clear();
7591        }
7592    }
7593
7594    /**
7595     * Performs a blocking permission check on a resource.<p>
7596     *
7597     * If the required permissions are not satisfied by the permissions the user has on the resource,
7598     * an exception is thrown.<p>
7599     *
7600     * @param dbc the current database context
7601     * @param resource the resource on which permissions are required
7602     * @param requiredPermissions the set of permissions required to access the resource
7603     * @param checkLock if true, the lock status of the resource is also checked
7604     * @param filter the filter for the resource
7605     *
7606     * @throws CmsException in case of any i/o error
7607     * @throws CmsSecurityException if the required permissions are not satisfied
7608     *
7609     * @see #hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter)
7610     */
7611    protected void checkPermissions(
7612        CmsDbContext dbc,
7613        CmsResource resource,
7614        CmsPermissionSet requiredPermissions,
7615        boolean checkLock,
7616        CmsResourceFilter filter)
7617    throws CmsException, CmsSecurityException {
7618
7619        // get the permissions
7620        I_CmsPermissionHandler.CmsPermissionCheckResult permissions = hasPermissions(
7621            dbc,
7622            resource,
7623            requiredPermissions,
7624            checkLock ? LockCheck.yes : LockCheck.no,
7625            filter);
7626        if (!permissions.isAllowed()) {
7627            checkPermissions(dbc.getRequestContext(), resource, requiredPermissions, permissions);
7628        }
7629    }
7630
7631    /**
7632     * Performs a blocking permission check on a resource.<p>
7633     *
7634     * If the required permissions are not satisfied by the permissions the user has on the resource,
7635     * an exception is thrown.<p>
7636     *
7637     * @param dbc the current database context
7638     * @param resource the resource on which permissions are required
7639     * @param requiredPermissions the set of permissions required to access the resource
7640     * @param checkLock if true, the lock status of the resource is also checked
7641     * @param filter the filter for the resource
7642     *
7643     * @throws CmsException in case of any i/o error
7644     * @throws CmsSecurityException if the required permissions are not satisfied
7645     *
7646     * @see #hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter)
7647     */
7648    protected void checkPermissions(
7649        CmsDbContext dbc,
7650        CmsResource resource,
7651        CmsPermissionSet requiredPermissions,
7652        LockCheck checkLock,
7653        CmsResourceFilter filter)
7654    throws CmsException, CmsSecurityException {
7655
7656        // get the permissions
7657        I_CmsPermissionHandler.CmsPermissionCheckResult permissions = hasPermissions(
7658            dbc,
7659            resource,
7660            requiredPermissions,
7661            checkLock,
7662            filter);
7663        if (!permissions.isAllowed()) {
7664            checkPermissions(dbc.getRequestContext(), resource, requiredPermissions, permissions);
7665        }
7666    }
7667
7668    /**
7669     * Applies the permission check result of a previous call
7670     * to {@link #hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter)}.<p>
7671     *
7672     * @param context the current request context
7673     * @param resource the resource on which permissions are required
7674     * @param requiredPermissions the set of permissions required to access the resource
7675     * @param permissions the permissions to check
7676     *
7677     * @throws CmsSecurityException if the required permissions are not satisfied
7678     * @throws CmsLockException if the lock status is not as required
7679     * @throws CmsVfsResourceNotFoundException if the required resource has been filtered
7680     */
7681    protected void checkPermissions(
7682        CmsRequestContext context,
7683        CmsResource resource,
7684        CmsPermissionSet requiredPermissions,
7685        I_CmsPermissionHandler.CmsPermissionCheckResult permissions)
7686    throws CmsSecurityException, CmsLockException, CmsVfsResourceNotFoundException {
7687
7688        if (permissions == I_CmsPermissionHandler.PERM_FILTERED) {
7689            throw new CmsVfsResourceNotFoundException(
7690                Messages.get().container(Messages.ERR_PERM_FILTERED_1, context.getSitePath(resource)));
7691        }
7692        if (permissions == I_CmsPermissionHandler.PERM_DENIED) {
7693            throw new CmsPermissionViolationException(
7694                Messages.get().container(
7695                    Messages.ERR_PERM_DENIED_2,
7696                    context.getSitePath(resource),
7697                    requiredPermissions.getPermissionString()));
7698        }
7699        if (permissions == I_CmsPermissionHandler.PERM_NOTLOCKED) {
7700            throw new CmsLockException(
7701                Messages.get().container(
7702                    Messages.ERR_PERM_NOTLOCKED_2,
7703                    context.getSitePath(resource),
7704                    context.getCurrentUser().getName()));
7705        }
7706    }
7707
7708    /**
7709     * Checks that the current user has enough permissions to modify the given user.<p>
7710     *
7711     * @param dbc the database context
7712     * @param username the name of the user to modify
7713     * @param role the needed role
7714     *
7715     * @throws CmsDataAccessException if something goes wrong accessing the database
7716     * @throws CmsRoleViolationException if the user has not the needed permissions
7717     */
7718    protected void checkRoleForUserModification(CmsDbContext dbc, String username, CmsRole role)
7719    throws CmsDataAccessException, CmsRoleViolationException {
7720
7721        CmsUser userToModify = m_driverManager.readUser(dbc, CmsOrganizationalUnit.removeLeadingSeparator(username));
7722        if (dbc.currentUser().equals(userToModify)) {
7723            // a user is allowed to write his own data
7724            return;
7725        }
7726        if (hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) {
7727            // a user with the ROOT_ADMIN role may change any other user
7728            return;
7729        }
7730        if (hasRole(dbc, userToModify, CmsRole.ADMINISTRATOR)) {
7731            // check the user that is going to do the modification is administrator
7732            checkRole(dbc, CmsRole.ADMINISTRATOR);
7733        } else {
7734            // check the user that is going to do the modification has the given role
7735            checkRole(dbc, role);
7736        }
7737
7738    }
7739
7740    /**
7741     * Checks if the given resource contains a resource that has a system lock.<p>
7742     *
7743     * @param dbc the current database context
7744     * @param resource the resource to check
7745     *
7746     * @throws CmsException in case there is a system lock contained in the given resource
7747     */
7748    protected void checkSystemLocks(CmsDbContext dbc, CmsResource resource) throws CmsException {
7749
7750        if (m_lockManager.hasSystemLocks(dbc, resource)) {
7751            throw new CmsLockException(
7752                Messages.get().container(
7753                    Messages.ERR_RESOURCE_SYSTEM_LOCKED_1,
7754                    dbc.removeSiteRoot(resource.getRootPath())));
7755        }
7756    }
7757
7758    /**
7759     * Internal recursive method for deleting a resource.<p>
7760     *
7761     * @param dbc the db context
7762     * @param resource the name of the resource to delete (full path)
7763     * @param siblingMode indicates how to handle siblings of the deleted resource
7764     *
7765     * @throws CmsException if something goes wrong
7766     */
7767    protected void deleteResource(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceDeleteMode siblingMode)
7768    throws CmsException {
7769
7770        try (CmsModificationContext modContext = CmsModificationContext.acquire(dbc.getRequestContext())) {
7771            if (resource.isFolder()) {
7772                // collect all resources in the folder (but exclude deleted ones)
7773                List<CmsResource> resources = m_driverManager.readChildResources(
7774                    dbc,
7775                    resource,
7776                    CmsResourceFilter.IGNORE_EXPIRATION,
7777                    true,
7778                    true,
7779                    false);
7780
7781                Set<CmsUUID> deletedResources = new HashSet<CmsUUID>();
7782                // now walk through all sub-resources in the folder
7783                for (int i = 0; i < resources.size(); i++) {
7784                    CmsResource childResource = resources.get(i);
7785                    if ((siblingMode == CmsResource.DELETE_REMOVE_SIBLINGS)
7786                        && deletedResources.contains(childResource.getResourceId())) {
7787                        // sibling mode is "delete all siblings" and another sibling of the current child resource has already
7788                        // been deleted- do nothing and continue with the next child resource.
7789                        continue;
7790                    }
7791                    if (childResource.isFolder()) {
7792                        // recurse into this method for subfolders
7793                        deleteResource(dbc, childResource, siblingMode);
7794                    } else {
7795                        // handle child resources
7796                        m_driverManager.deleteResource(dbc, childResource, siblingMode);
7797                        modContext.add(childResource);
7798                    }
7799                    deletedResources.add(childResource.getResourceId());
7800                }
7801                deletedResources.clear();
7802            }
7803            // handle the resource itself
7804            m_driverManager.deleteResource(dbc, resource, siblingMode);
7805            modContext.add(resource);
7806        }
7807    }
7808
7809    /**
7810     * Deletes a user, where all permissions and resources attributes of the user
7811     * were transfered to a replacement user, if given.<p>
7812     *
7813     * @param context the current request context
7814     * @param user the user to be deleted
7815     * @param replacement the user to be transfered, can be <code>null</code>
7816     *
7817     * @throws CmsRoleViolationException if the current user does not own the rule {@link CmsRole#ACCOUNT_MANAGER}
7818     * @throws CmsSecurityException in case the user is a default user
7819     * @throws CmsException if something goes wrong
7820     */
7821    protected void deleteUser(CmsRequestContext context, CmsUser user, CmsUser replacement)
7822    throws CmsException, CmsSecurityException, CmsRoleViolationException {
7823
7824        if (OpenCms.getDefaultUsers().isDefaultUser(user.getName())) {
7825            throw new CmsSecurityException(
7826                org.opencms.security.Messages.get().container(
7827                    org.opencms.security.Messages.ERR_CANT_DELETE_DEFAULT_USER_1,
7828                    user.getName()));
7829        }
7830        if (context.getCurrentUser().equals(user)) {
7831            throw new CmsSecurityException(Messages.get().container(Messages.ERR_USER_CANT_DELETE_ITSELF_USER_0));
7832        }
7833
7834        CmsDbContext dbc = null;
7835        try {
7836            dbc = getDbContextForDeletePrincipal(context);
7837            CmsRole role = CmsRole.ACCOUNT_MANAGER.forOrgUnit(getParentOrganizationalUnit(user.getName()));
7838            checkRoleForUserModification(dbc, user.getName(), role);
7839            m_driverManager.deleteUser(
7840                dbc,
7841                dbc.getRequestContext().getCurrentProject(),
7842                user.getName(),
7843                null == replacement ? null : replacement.getName());
7844        } catch (Exception e) {
7845            CmsDbContext dbcForException = m_dbContextFactory.getDbContext(context);
7846            dbcForException.report(null, Messages.get().container(Messages.ERR_DELETE_USER_1, user.getName()), e);
7847            dbcForException.clear();
7848        } finally {
7849            if (null != dbc) {
7850                dbc.clear();
7851            }
7852        }
7853    }
7854
7855    /**
7856     * Returns all resources of organizational units for which the current user has
7857     * the given role role.<p>
7858     *
7859     * @param dbc the current database context
7860     * @param role the role to check
7861     *
7862     * @return a list of {@link org.opencms.file.CmsResource} objects
7863     *
7864     * @throws CmsException if something goes wrong
7865     */
7866    protected List<CmsResource> getManageableResources(CmsDbContext dbc, CmsRole role) throws CmsException {
7867
7868        CmsOrganizationalUnit ou = m_driverManager.readOrganizationalUnit(dbc, role.getOuFqn());
7869        if (hasRole(dbc, dbc.currentUser(), role)) {
7870            return m_driverManager.getResourcesForOrganizationalUnit(dbc, ou);
7871        }
7872        List<CmsResource> resources = new ArrayList<CmsResource>();
7873        Iterator<CmsOrganizationalUnit> it = m_driverManager.getOrganizationalUnits(dbc, ou, false).iterator();
7874        while (it.hasNext()) {
7875            CmsOrganizationalUnit orgUnit = it.next();
7876            resources.addAll(getManageableResources(dbc, role.forOrgUnit(orgUnit.getName())));
7877        }
7878        return resources;
7879    }
7880
7881    /**
7882     * Returns the organizational unit for the parent of the given fully qualified name.<p>
7883     *
7884     * @param fqn the fully qualified name to get the parent organizational unit for
7885     *
7886     * @return the parent organizational unit for the fully qualified name
7887     */
7888    protected String getParentOrganizationalUnit(String fqn) {
7889
7890        String ouFqn = CmsOrganizationalUnit.getParentFqn(CmsOrganizationalUnit.removeLeadingSeparator(fqn));
7891        if (ouFqn == null) {
7892            ouFqn = "";
7893        }
7894        return ouFqn;
7895    }
7896
7897    /**
7898     * Performs a non-blocking permission check on a resource.<p>
7899     *
7900     * This test will not throw an exception in case the required permissions are not
7901     * available for the requested operation. Instead, it will return one of the
7902     * following values:<ul>
7903     * <li><code>{@link I_CmsPermissionHandler#PERM_ALLOWED}</code></li>
7904     * <li><code>{@link I_CmsPermissionHandler#PERM_FILTERED}</code></li>
7905     * <li><code>{@link I_CmsPermissionHandler#PERM_DENIED}</code></li></ul><p>
7906     *
7907     * @param dbc the current database context
7908     * @param resource the resource on which permissions are required
7909     * @param requiredPermissions the set of permissions required for the operation
7910     * @param checkLock if true, a lock for the current user is required for
7911     *      all write operations, if false it's ok to write as long as the resource
7912     *      is not locked by another user
7913     * @param filter the resource filter to use
7914     *
7915     * @return <code>{@link I_CmsPermissionHandler#PERM_ALLOWED}</code> if the user has sufficient permissions on the resource
7916     *      for the requested operation
7917     *
7918     * @throws CmsException in case of i/o errors (NOT because of insufficient permissions)
7919     */
7920    protected I_CmsPermissionHandler.CmsPermissionCheckResult hasPermissions(
7921        CmsDbContext dbc,
7922        CmsResource resource,
7923        CmsPermissionSet requiredPermissions,
7924        LockCheck checkLock,
7925        CmsResourceFilter filter)
7926    throws CmsException {
7927
7928        return m_permissionHandler.hasPermissions(dbc, resource, requiredPermissions, checkLock, filter);
7929    }
7930
7931    /**
7932     * Returns <code>true</code> if at least one of the given group names is equal to a group name
7933     * of the given role in the given organizational unit.<p>
7934     *
7935     * This checks the given list against the group of the given role as well as against the role group
7936     * of all parent roles.<p>
7937     *
7938     * If the organizational unit is <code>null</code>, this method will check if the
7939     * given user has the given role for at least one organizational unit.<p>
7940     *
7941     * @param role the role to check
7942     * @param roles the groups to match the role groups against
7943     *
7944     * @return <code>true</code> if at last one of the given group names is equal to a group name
7945     *      of this role
7946     */
7947    protected boolean hasRole(CmsRole role, List<CmsGroup> roles) {
7948
7949        // iterates the role groups the user is in
7950        for (CmsGroup group : roles) {
7951            String groupName = group.getName();
7952            // iterate the role hierarchy
7953            for (String distictGroupName : role.getDistinctGroupNames()) {
7954                if (distictGroupName.startsWith(CmsOrganizationalUnit.SEPARATOR)) {
7955                    // this is a ou independent role
7956                    // we need an exact match, and we ignore the ou parameter
7957                    if (groupName.equals(distictGroupName.substring(1))) {
7958                        return true;
7959                    }
7960                } else {
7961                    // first check if the user has the role at all
7962                    if (groupName.endsWith(CmsOrganizationalUnit.SEPARATOR + distictGroupName)
7963                        || groupName.equals(distictGroupName)) {
7964                        // this is a ou dependent role
7965                        if (role.getOuFqn() == null) {
7966                            // ou parameter is null, so the user needs to have the role in at least one ou does not matter which
7967                            return true;
7968                        } else {
7969                            // the user needs to have the role in the given ou or in a parent ou
7970                            // now check that the ou matches
7971                            String groupFqn = CmsOrganizationalUnit.getParentFqn(groupName);
7972                            if (role.getOuFqn().startsWith(groupFqn)) {
7973                                return true;
7974                            }
7975                        }
7976                    }
7977                }
7978            }
7979        }
7980        return false;
7981    }
7982
7983    /**
7984     * Internal recursive method to move a resource.<p>
7985     *
7986     * @param dbc the db context
7987     * @param source the source resource
7988     * @param destination the destination path
7989     * @param allMovedResources a set used to collect all moved resources
7990     *
7991     * @throws CmsException if something goes wrong
7992     */
7993    protected void moveResource(
7994        CmsDbContext dbc,
7995        CmsResource source,
7996        String destination,
7997        Set<CmsResource> allMovedResources)
7998    throws CmsException {
7999
8000        try (CmsModificationContext modContext = CmsModificationContext.acquire(dbc.getRequestContext())) {
8001
8002            List<CmsResource> resources = null;
8003
8004            if (source.isFolder()) {
8005                if (!CmsResource.isFolder(destination)) {
8006                    // ensure folder name end's with a /
8007                    destination = destination.concat("/");
8008                }
8009                // collect all resources in the folder without checking permissions
8010                resources = m_driverManager.readChildResources(dbc, source, CmsResourceFilter.ALL, true, true, false);
8011            } else {
8012
8013            }
8014
8015            // target permissions will be checked later
8016            m_driverManager.moveResource(dbc, source, destination, false);
8017
8018            // make sure lock is set
8019            CmsResource destinationResource = m_driverManager.readResource(dbc, destination, CmsResourceFilter.ALL);
8020            try {
8021                // the destination must always get a new lock
8022                m_driverManager.lockResource(dbc, destinationResource, CmsLockType.EXCLUSIVE);
8023            } catch (Exception e) {
8024                // could happen with with shared locks on single files
8025                if (LOG.isWarnEnabled()) {
8026                    LOG.warn(e.getLocalizedMessage(), e);
8027                }
8028            }
8029
8030            if (resources != null) {
8031                // Ensure consistent order that is not database-dependent, since readChildResources doesn't specify an ordering.
8032                // this is necessary to make test cases more useful.
8033                Collections.sort(resources, (r1, r2) -> r1.getRootPath().compareTo(r2.getRootPath()));
8034
8035                // now walk through all sub-resources in the folder
8036                for (int i = 0; i < resources.size(); i++) {
8037                    CmsResource childResource = resources.get(i);
8038                    String childDestination = destination.concat(childResource.getName());
8039                    // recurse with child resource
8040                    moveResource(dbc, childResource, childDestination, allMovedResources);
8041                }
8042            }
8043
8044            List<CmsResource> movedResources = m_driverManager.readChildResources(
8045                dbc,
8046                destinationResource,
8047                CmsResourceFilter.ALL,
8048                true,
8049                true,
8050                false);
8051            allMovedResources.add(destinationResource);
8052            modContext.add(destinationResource);
8053            allMovedResources.addAll(movedResources);
8054        }
8055
8056    }
8057
8058    protected void publishJob(CmsObject cms, CmsDbContext dbc, CmsPublishList pubList, I_CmsReport report)
8059    throws CmsException {
8060
8061        m_driverManager.publishJob(cms, dbc, pubList, report);
8062    }
8063
8064    /**
8065     * Reads a folder from the VFS, using the specified resource filter.<p>
8066     *
8067     * @param dbc the current database context
8068     * @param resourcename the name of the folder to read (full path)
8069     * @param filter the resource filter to use while reading
8070     *
8071     * @return the folder that was read
8072     *
8073     * @throws CmsException if something goes wrong
8074     */
8075    protected CmsFolder readFolder(CmsDbContext dbc, String resourcename, CmsResourceFilter filter)
8076    throws CmsException {
8077
8078        CmsResource resource = readResource(dbc, resourcename, filter);
8079        return m_driverManager.convertResourceToFolder(resource);
8080    }
8081
8082    /**
8083     * Reads a resource from the OpenCms VFS, using the specified resource filter.<p>
8084     *
8085     * @param dbc the current database context
8086     * @param structureID the ID of the structure to read
8087     * @param filter the resource filter to use while reading
8088     *
8089     * @return the resource that was read
8090     *
8091     * @throws CmsException if something goes wrong
8092     *
8093     * @see CmsObject#readResource(CmsUUID, CmsResourceFilter)
8094     * @see CmsObject#readResource(CmsUUID)
8095     * @see CmsObject#readFile(CmsResource)
8096     */
8097    protected CmsResource readResource(CmsDbContext dbc, CmsUUID structureID, CmsResourceFilter filter)
8098    throws CmsException {
8099
8100        // read the resource from the VFS
8101        CmsResource resource = m_driverManager.readResource(dbc, structureID, filter);
8102
8103        // check if the user has read access to the resource
8104        checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, true, filter);
8105
8106        // access was granted - return the resource
8107        return resource;
8108    }
8109
8110    /**
8111     * Reads a resource from the OpenCms VFS, using the specified resource filter.<p>
8112     *
8113     * @param dbc the current database context
8114     * @param resourcePath the name of the resource to read (full path)
8115     * @param filter the resource filter to use while reading
8116     *
8117     * @return the resource that was read
8118     *
8119     * @throws CmsException if something goes wrong
8120     *
8121     * @see CmsObject#readResource(String, CmsResourceFilter)
8122     * @see CmsObject#readResource(String)
8123     * @see CmsObject#readFile(CmsResource)
8124     */
8125    protected CmsResource readResource(CmsDbContext dbc, String resourcePath, CmsResourceFilter filter)
8126    throws CmsException {
8127
8128        // read the resource from the VFS
8129        CmsResource resource = m_driverManager.readResource(dbc, resourcePath, filter);
8130
8131        // check if the user has read access to the resource
8132        checkPermissions(dbc, resource, CmsPermissionSet.ACCESS_READ, true, filter);
8133
8134        // access was granted - return the resource
8135        return resource;
8136    }
8137
8138    /**
8139     * Determines a project where the deletion of a principal can be executed and sets it in the returned db context.<p>
8140     *
8141     * @param context the current request context
8142     *
8143     * @return the db context to use when deleting the principal.
8144     *
8145     * @throws CmsDataAccessException if determining a project that is suitable to delete the prinicipal fails.
8146     */
8147    private CmsDbContext getDbContextForDeletePrincipal(CmsRequestContext context) throws CmsDataAccessException {
8148
8149        CmsProject currentProject = context.getCurrentProject();
8150        CmsDbContext dbc = m_dbContextFactory.getDbContext(context);
8151        CmsUUID projectId = dbc.getProjectId();
8152        // principal modifications are allowed if the current project is not the online project
8153        if (currentProject.isOnlineProject()) {
8154            // this is needed because
8155            // I_CmsUserDriver#removeAccessControlEntriesForPrincipal(CmsDbContext, CmsProject, CmsProject, CmsUUID)
8156            // expects an offline project, if not, data will become inconsistent
8157
8158            // if the current project is the online project, check if there is a valid offline project at all
8159            List<CmsProject> projects = m_driverManager.getProjectDriver(dbc).readProjects(dbc, "");
8160            for (CmsProject project : projects) {
8161                if (!project.isOnlineProject()) {
8162                    try {
8163                        dbc.setProjectId(project.getUuid());
8164                        if (null != m_driverManager.readResource(dbc, "/", CmsResourceFilter.ALL)) {
8165                            // shallow clone the context with project adjusted
8166                            context = new CmsRequestContext(
8167                                context.getCurrentUser(),
8168                                project,
8169                                context.getUri(),
8170                                context.getRequestMatcher(),
8171                                context.getSiteRoot(),
8172                                context.isSecureRequest(),
8173                                context.getLocale(),
8174                                context.getEncoding(),
8175                                context.getRemoteAddress(),
8176                                context.getRequestTime(),
8177                                context.getDirectoryTranslator(),
8178                                context.getFileTranslator(),
8179                                context.getOuFqn(),
8180                                context.isForceAbsoluteLinks());
8181                            dbc = m_dbContextFactory.getDbContext(context);
8182                            projectId = dbc.getProjectId();
8183                            break;
8184                        }
8185                    } catch (Exception e) {
8186                        // ignore
8187                    }
8188                }
8189            }
8190        }
8191        dbc.setProjectId(projectId);
8192        return dbc;
8193    }
8194
8195    /**
8196     * Reports an exception which occurred while reading file content.<p>
8197     *
8198     * @param dbc the database context
8199     * @param context the request context
8200     * @param resource the resource being read
8201     * @param e the exception to report
8202     */
8203    private void reportReadFileException(CmsDbContext dbc, CmsRequestContext context, CmsResource resource, Exception e)
8204    throws CmsException {
8205
8206        if (resource instanceof I_CmsHistoryResource) {
8207            dbc.report(
8208                null,
8209                Messages.get().container(
8210                    Messages.ERR_READ_FILE_HISTORY_2,
8211                    context.getSitePath(resource),
8212                    Integer.valueOf(resource.getVersion())),
8213                e);
8214        } else {
8215            dbc.report(null, Messages.get().container(Messages.ERR_READ_FILE_1, context.getSitePath(resource)), e);
8216        }
8217    }
8218
8219}