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