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