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.publish.CmsTooManyPublishResourcesException; 031import org.opencms.configuration.CmsConfigurationManager; 032import org.opencms.configuration.CmsParameterConfiguration; 033import org.opencms.configuration.CmsSystemConfiguration; 034import org.opencms.db.generic.CmsPublishHistoryCleanupFilter; 035import org.opencms.db.generic.CmsUserDriver; 036import org.opencms.db.log.CmsLogEntry; 037import org.opencms.db.log.CmsLogEntryType; 038import org.opencms.db.log.CmsLogFilter; 039import org.opencms.db.storage.I_CmsStorageDelivery; 040import org.opencms.db.timing.CmsDefaultProfilingHandler; 041import org.opencms.db.timing.CmsProfilingInvocationHandler; 042import org.opencms.db.urlname.CmsUrlNameMappingEntry; 043import org.opencms.db.urlname.CmsUrlNameMappingFilter; 044import org.opencms.db.userpublishlist.A_CmsLogPublishListConverter; 045import org.opencms.db.userpublishlist.CmsLogPublishListConverterAllUsers; 046import org.opencms.db.userpublishlist.CmsLogPublishListConverterCurrentUser; 047import org.opencms.file.CmsDataAccessException; 048import org.opencms.file.CmsFile; 049import org.opencms.file.CmsFolder; 050import org.opencms.file.CmsGroup; 051import org.opencms.file.CmsObject; 052import org.opencms.file.CmsProject; 053import org.opencms.file.CmsProperty; 054import org.opencms.file.CmsPropertyDefinition; 055import org.opencms.file.CmsRequestContext; 056import org.opencms.file.CmsResource; 057import org.opencms.file.CmsResourceFilter; 058import org.opencms.file.CmsStoredContentInfo; 059import org.opencms.file.CmsUser; 060import org.opencms.file.CmsUserSearchParameters; 061import org.opencms.file.CmsVfsException; 062import org.opencms.file.CmsVfsResourceAlreadyExistsException; 063import org.opencms.file.CmsVfsResourceNotFoundException; 064import org.opencms.file.I_CmsFileContentStreamHandler; 065import org.opencms.file.I_CmsResource; 066import org.opencms.file.history.CmsHistoryFile; 067import org.opencms.file.history.CmsHistoryFolder; 068import org.opencms.file.history.CmsHistoryPrincipal; 069import org.opencms.file.history.CmsHistoryProject; 070import org.opencms.file.history.I_CmsHistoryResource; 071import org.opencms.file.quota.CmsFolderSizeEntry; 072import org.opencms.file.quota.CmsFolderSizeOptions; 073import org.opencms.file.types.CmsResourceTypeFolder; 074import org.opencms.file.types.CmsResourceTypeJsp; 075import org.opencms.file.types.I_CmsResourceType; 076import org.opencms.flex.CmsFlexRequestContextInfo; 077import org.opencms.gwt.shared.alias.CmsAliasImportResult; 078import org.opencms.gwt.shared.alias.CmsAliasImportStatus; 079import org.opencms.gwt.shared.alias.CmsAliasMode; 080import org.opencms.i18n.CmsLocaleManager; 081import org.opencms.i18n.CmsMessageContainer; 082import org.opencms.jsp.CmsJspNavBuilder; 083import org.opencms.lock.CmsLock; 084import org.opencms.lock.CmsLockException; 085import org.opencms.lock.CmsLockFilter; 086import org.opencms.lock.CmsLockManager; 087import org.opencms.lock.CmsLockType; 088import org.opencms.main.CmsEvent; 089import org.opencms.main.CmsException; 090import org.opencms.main.CmsIllegalArgumentException; 091import org.opencms.main.CmsIllegalStateException; 092import org.opencms.main.CmsInitException; 093import org.opencms.main.CmsLog; 094import org.opencms.main.CmsMultiException; 095import org.opencms.main.I_CmsEventListener; 096import org.opencms.main.OpenCms; 097import org.opencms.module.CmsModule; 098import org.opencms.monitor.CmsMemoryMonitor; 099import org.opencms.monitor.CmsMemoryMonitor.CacheType; 100import org.opencms.publish.CmsPublishEngine; 101import org.opencms.publish.CmsPublishJobInfoBean; 102import org.opencms.publish.CmsPublishReport; 103import org.opencms.relations.CmsCategoryService; 104import org.opencms.relations.CmsLink; 105import org.opencms.relations.CmsRelation; 106import org.opencms.relations.CmsRelationFilter; 107import org.opencms.relations.CmsRelationSystemValidator; 108import org.opencms.relations.CmsRelationType; 109import org.opencms.relations.CmsRelationType.CopyBehavior; 110import org.opencms.relations.I_CmsLinkParseable; 111import org.opencms.report.CmsLogReport; 112import org.opencms.report.I_CmsReport; 113import org.opencms.security.CmsAccessControlEntry; 114import org.opencms.security.CmsAccessControlList; 115import org.opencms.security.CmsAuthentificationException; 116import org.opencms.security.CmsOrganizationalUnit; 117import org.opencms.security.CmsPasswordEncryptionException; 118import org.opencms.security.CmsPermissionSet; 119import org.opencms.security.CmsPermissionSetCustom; 120import org.opencms.security.CmsPrincipal; 121import org.opencms.security.CmsRole; 122import org.opencms.security.CmsSecurityException; 123import org.opencms.security.I_CmsPermissionHandler; 124import org.opencms.security.I_CmsPermissionHandler.LockCheck; 125import org.opencms.security.I_CmsPrincipal; 126import org.opencms.security.twofactor.CmsSecondFactorInfo; 127import org.opencms.security.twofactor.CmsSecondFactorSetupException; 128import org.opencms.security.twofactor.CmsTwoFactorAuthenticationHandler; 129import org.opencms.site.CmsSiteMatcher; 130import org.opencms.util.CmsFileUtil; 131import org.opencms.util.CmsPath; 132import org.opencms.util.CmsStringUtil; 133import org.opencms.util.CmsUUID; 134import org.opencms.util.PrintfFormat; 135import org.opencms.workflow.CmsDefaultWorkflowManager; 136import org.opencms.workplace.threads.A_CmsProgressThread; 137 138import java.io.ByteArrayInputStream; 139import java.io.IOException; 140import java.io.OutputStream; 141import java.lang.reflect.Proxy; 142import java.util.ArrayList; 143import java.util.Arrays; 144import java.util.Collection; 145import java.util.Collections; 146import java.util.Comparator; 147import java.util.Date; 148import java.util.HashMap; 149import java.util.HashSet; 150import java.util.Iterator; 151import java.util.List; 152import java.util.ListIterator; 153import java.util.Locale; 154import java.util.Map; 155import java.util.Map.Entry; 156import java.util.Set; 157import java.util.TreeSet; 158import java.util.concurrent.ConcurrentMap; 159import java.util.concurrent.ExecutionException; 160import java.util.concurrent.TimeUnit; 161import java.util.function.Predicate; 162import java.util.function.Supplier; 163import java.util.regex.Pattern; 164import java.util.regex.PatternSyntaxException; 165import java.util.stream.Collectors; 166 167import org.apache.commons.logging.Log; 168 169import com.google.common.cache.Cache; 170import com.google.common.cache.CacheBuilder; 171import com.google.common.collect.ArrayListMultimap; 172import com.google.common.collect.Maps; 173import com.google.common.collect.Multimap; 174 175/** 176 * The OpenCms driver manager.<p> 177 * 178 * @since 6.0.0 179 */ 180public final class CmsDriverManager implements I_CmsEventListener { 181 182 /** 183 * Enum for distinguishing between login modes. 184 */ 185 public static enum LoginUserMode { 186 /** Check mode, where the user is not logged in, but the password check and other checks are still done (however not the second factor check for 2FA). */ 187 checkOnly, 188 189 /** Normal login process. */ 190 standard 191 } 192 193 /** 194 * Resource list which additionally knows whether it should be cacheable in the resource list cache or not. 195 */ 196 public static class ResourceListWithCacheability extends ArrayList<CmsResource> { 197 198 /** Serial version id. */ 199 private static final long serialVersionUID = 1L; 200 201 /** True if the list should be cacheable. */ 202 private boolean m_cacheable = true; 203 204 /** 205 * Creates a new instance. 206 */ 207 public ResourceListWithCacheability() { 208 209 super(); 210 } 211 212 /** 213 * Creates a new instance. 214 * @param initialCapacity the initial capacity 215 */ 216 public ResourceListWithCacheability(int initialCapacity) { 217 218 super(initialCapacity); 219 } 220 221 /** 222 * Returns true if the resource list is cacheable. 223 * 224 * @return true if the list is cacheable 225 */ 226 public boolean isCacheable() { 227 228 return m_cacheable; 229 } 230 231 /** 232 * Enables/disables cacheability for the resource list. 233 * @param cacheable true if the list should be cacheable 234 */ 235 public void setCacheable(boolean cacheable) { 236 237 m_cacheable = cacheable; 238 } 239 240 } 241 242 /** 243 * Special key class for caching the resource OU data with a Guava LoadingCache.<p> 244 * 245 * In principle, the actual cache key is just the current project, but because of how cache loaders work, 246 * the key must contain everything that varies between calls and is required to load the value. So we also store the DB context 247 * for use by the cache loader. The project (offline/online) must still be stored, because the DB context gets invalidated 248 * eventually, i.e. its project id gets nulled. 249 */ 250 public static class ResourceOUCacheKey { 251 252 /** The actual cache key. */ 253 private String m_actualKey; 254 255 /** The DB context. */ 256 private CmsDbContext m_dbc; 257 258 /** The driver manager to use. */ 259 private CmsDriverManager m_driverManager; 260 261 /** 262 * Creates a new instance. 263 * 264 * @param driverManager the driver manager to use 265 * @param dbc the current DB context 266 */ 267 public ResourceOUCacheKey(CmsDriverManager driverManager, CmsDbContext dbc) { 268 269 m_dbc = dbc; 270 m_driverManager = driverManager; 271 m_actualKey = CmsProject.ONLINE_PROJECT_ID.equals(dbc.currentProject().getId()) ? "ONLINE" : "OFFLINE"; 272 } 273 274 /** 275 * @see java.lang.Object#equals(java.lang.Object) 276 */ 277 @Override 278 public boolean equals(Object obj) { 279 280 return (obj instanceof ResourceOUCacheKey) 281 && ((ResourceOUCacheKey)obj).getActualKey().equals(getActualKey()); 282 } 283 284 /** 285 * Gets the stored DB context.<p> 286 * 287 * Note that the DB contex returned by this may have been invalidated! 288 * 289 * @return the stored DB context 290 */ 291 public CmsDbContext getDbContext() { 292 293 return m_dbc; 294 } 295 296 /** 297 * Gets the current driver manager. 298 * 299 * @return the driver manager to use 300 */ 301 public CmsDriverManager getDriverManager() { 302 303 return m_driverManager; 304 } 305 306 /** 307 * @see java.lang.Object#hashCode() 308 */ 309 @Override 310 public int hashCode() { 311 312 return getActualKey().hashCode(); 313 } 314 315 /** 316 * Gets the actual key data. 317 * 318 * @return the actual key data 319 */ 320 private String getActualKey() { 321 322 return m_actualKey; 323 } 324 325 } 326 327 /** 328 * Helper class used to store information about resources assigned to OUs in a cache. 329 */ 330 public static class ResourceOUMap { 331 332 /** Multimap from the paths of resources to the OUs to which they are assigned as OU resources. */ 333 private Multimap<CmsPath, CmsOrganizationalUnit> m_ousByAssignedResourcePaths = ArrayListMultimap.create(); 334 335 /** The organizational units, with their UUIDs as keys. */ 336 private Map<CmsUUID, CmsOrganizationalUnit> m_ousById = new HashMap<>(); 337 338 /** 339 * Gets the list of organizational units to which a given root path belongs, according to the cached 340 * OU resource assignments. 341 * 342 * @param rootPath the root path 343 * @return the organizational units to which the path belongs 344 */ 345 public List<CmsOrganizationalUnit> getResourceOrgUnits(String rootPath) { 346 347 Set<CmsOrganizationalUnit> result = new HashSet<>(); 348 String currentPath = rootPath; 349 while (currentPath != null) { 350 result.addAll(m_ousByAssignedResourcePaths.get(new CmsPath(currentPath))); 351 currentPath = CmsResource.getParentFolder(currentPath); 352 } 353 return new ArrayList<>(result); 354 } 355 356 /** 357 * Reads the OU resource data from the VFS and initializes this instance with it. 358 * 359 * @param driverManager the driver manager to use 360 * @param dbc the current DB context 361 * @throws CmsException if something goes wrong 362 */ 363 public void init(CmsDriverManager driverManager, CmsDbContext dbc) throws CmsException { 364 365 List<CmsRelation> relations = driverManager.getRelationsForResource( 366 dbc, 367 null, 368 CmsRelationFilter.ALL.filterType(CmsRelationType.OU_RESOURCE)); 369 CmsOrganizationalUnit root = driverManager.readOrganizationalUnit(dbc, ""); 370 List<CmsOrganizationalUnit> children = driverManager.getOrganizationalUnits(dbc, root, true); 371 372 Set<CmsOrganizationalUnit> ous = new HashSet<>(); 373 ous.add(root); 374 ous.addAll(children); 375 init(relations, ous); 376 377 } 378 379 /** 380 * Initializes the OU resource data. 381 * 382 * @param ouRelations the current list of OU relations 383 * @param ous the current list of OUs 384 */ 385 public void init(Collection<CmsRelation> ouRelations, Collection<CmsOrganizationalUnit> ous) { 386 387 m_ousById.clear(); 388 m_ousByAssignedResourcePaths.clear(); 389 for (CmsOrganizationalUnit ou : ous) { 390 m_ousById.put(ou.getId(), ou); 391 } 392 for (CmsRelation rel : ouRelations) { 393 CmsOrganizationalUnit ou = m_ousById.get(rel.getSourceId()); 394 if (ou != null) { 395 m_ousByAssignedResourcePaths.put(new CmsPath(rel.getTargetPath()), ou); 396 } 397 } 398 } 399 } 400 401 /** 402 * The comparator used for comparing url name mapping entries by date.<p> 403 */ 404 class UrlNameMappingComparator implements Comparator<CmsUrlNameMappingEntry> { 405 406 /** 407 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 408 */ 409 public int compare(CmsUrlNameMappingEntry o1, CmsUrlNameMappingEntry o2) { 410 411 long date1 = o1.getDateChanged(); 412 long date2 = o2.getDateChanged(); 413 if (date1 < date2) { 414 return -1; 415 } 416 if (date1 > date2) { 417 return +1; 418 } 419 return 0; 420 } 421 } 422 423 /** 424 * Enumeration class for the mode parameter in the 425 * {@link CmsDriverManager#readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)} 426 * method.<p> 427 */ 428 private static class CmsReadChangedProjectResourceMode { 429 430 /** 431 * Default constructor.<p> 432 */ 433 protected CmsReadChangedProjectResourceMode() { 434 435 // noop 436 } 437 } 438 439 /** Request context attribute used to override the time used for time-based exclusive access checks. */ 440 public static final String ATTR_EXCLUSIVE_ACCESS_CLOCK = "ATTR_EXCLUSIVE_ACCESS_CLOCK"; 441 442 /** Attribute for signaling to the user driver that a specific OU should be initialized by fillDefaults. */ 443 public static final String ATTR_INIT_OU = "INIT_OU"; 444 445 /** DB context attribute used to communicate information about resource cacheability between various methods. */ 446 public static final String ATTR_PERMISSION_NOCACHE = "ATTR_PERMISSION_NOCACHE"; 447 448 /** Attribute login. */ 449 public static final String ATTRIBUTE_LOGIN = "A_LOGIN"; 450 451 /** Cache key for all properties. */ 452 public static final String CACHE_ALL_PROPERTIES = "_CAP_"; 453 454 /** 455 * Values indicating changes of a resource, 456 * ordered according to the scope of the change. 457 */ 458 /** Value to indicate a change in access control entries of a resource. */ 459 public static final int CHANGED_ACCESSCONTROL = 1; 460 461 /** Value to indicate a content change. */ 462 public static final int CHANGED_CONTENT = 16; 463 464 /** Value to indicate a change in the lastmodified settings of a resource. */ 465 public static final int CHANGED_LASTMODIFIED = 4; 466 467 /** Value to indicate a project change. */ 468 public static final int CHANGED_PROJECT = 32; 469 470 /** Value to indicate a change in the resource data. */ 471 public static final int CHANGED_RESOURCE = 8; 472 473 /** Value to indicate a change in the availability timeframe. */ 474 public static final int CHANGED_TIMEFRAME = 2; 475 476 /** "cache" string in the configuration-file. */ 477 public static final String CONFIGURATION_CACHE = "cache"; 478 479 /** "db" string in the configuration-file. */ 480 public static final String CONFIGURATION_DB = "db"; 481 482 /** "driver.history" string in the configuration-file. */ 483 public static final String CONFIGURATION_HISTORY = "driver.history"; 484 485 /** "driver.project" string in the configuration-file. */ 486 public static final String CONFIGURATION_PROJECT = "driver.project"; 487 488 /** "subscription.vfs" string in the configuration file. */ 489 public static final String CONFIGURATION_SUBSCRIPTION = "driver.subscription"; 490 491 /** "driver.user" string in the configuration-file. */ 492 public static final String CONFIGURATION_USER = "driver.user"; 493 494 /** "driver.vfs" string in the configuration-file. */ 495 public static final String CONFIGURATION_VFS = "driver.vfs"; 496 497 /** DBC attribute key needed to fix publishing behavior involving siblings. */ 498 public static final String KEY_CHANGED_AND_DELETED = "changedAndDeleted"; 499 500 /** The vfs path of the loast and found folder. */ 501 public static final String LOST_AND_FOUND_FOLDER = "/system/lost-found"; 502 503 /** The maximum length of a VFS resource path. */ 504 public static final int MAX_VFS_RESOURCE_PATH_LENGTH = 512; 505 506 /** Key for indicating no changes. */ 507 public static final int NOTHING_CHANGED = 0; 508 509 /** Name of the configuration parameter to enable/disable logging to the CMS_LOG table. */ 510 public static final String PARAM_LOG_TABLE_ENABLED = "log.table.enabled"; 511 512 /** Indicates to ignore the resource path when matching resources. */ 513 public static final String READ_IGNORE_PARENT = null; 514 515 /** Indicates to ignore the time value. */ 516 public static final long READ_IGNORE_TIME = 0L; 517 518 /** Indicates to ignore the resource type when matching resources. */ 519 public static final int READ_IGNORE_TYPE = -1; 520 521 /** Indicates to match resources NOT having the given state. */ 522 public static final int READMODE_EXCLUDE_STATE = 8; 523 524 /** Indicates to match immediate children only. */ 525 public static final int READMODE_EXCLUDE_TREE = 1; 526 527 /** Indicates to match resources NOT having the given type. */ 528 public static final int READMODE_EXCLUDE_TYPE = 4; 529 530 /** Mode for reading project resources from the db. */ 531 public static final int READMODE_IGNORESTATE = 0; 532 533 /** Indicates to match resources in given project only. */ 534 public static final int READMODE_INCLUDE_PROJECT = 2; 535 536 /** Indicates to match all successors. */ 537 public static final int READMODE_INCLUDE_TREE = 0; 538 539 /** Mode for reading project resources from the db. */ 540 public static final int READMODE_MATCHSTATE = 1; 541 542 /** Indicates if only file resources should be read. */ 543 public static final int READMODE_ONLY_FILES = 128; 544 545 /** Indicates if only folder resources should be read. */ 546 public static final int READMODE_ONLY_FOLDERS = 64; 547 548 /** Mode for reading project resources from the db. */ 549 public static final int READMODE_UNMATCHSTATE = 2; 550 551 /** Flag that can be used to disable the resource OU caching if necessary. */ 552 public static boolean resourceOrgUnitCachingEnabled = true; 553 554 /** Prefix char for temporary files in the VFS. */ 555 public static final String TEMP_FILE_PREFIX = "~"; 556 557 /** Key to indicate complete update. */ 558 public static final int UPDATE_ALL = 3; 559 560 /** Key to indicate update of resource record. */ 561 public static final int UPDATE_RESOURCE = 4; 562 563 /** Key to indicate update of last modified project reference. */ 564 public static final int UPDATE_RESOURCE_PROJECT = 6; 565 566 /** Key to indicate update of resource state. */ 567 public static final int UPDATE_RESOURCE_STATE = 1; 568 569 /** Key to indicate update of resource state including the content date. */ 570 public static final int UPDATE_RESOURCE_STATE_CONTENT = 7; 571 572 /** Key to indicate update of structure record. */ 573 public static final int UPDATE_STRUCTURE = 5; 574 575 /** Key to indicate update of structure state. */ 576 public static final int UPDATE_STRUCTURE_STATE = 2; 577 578 /** Map of pools defined in opencms.properties. */ 579 protected static ConcurrentMap<String, CmsDbPoolV11> m_pools = Maps.newConcurrentMap(); 580 581 /** The log object for this class. */ 582 private static final Log LOG = CmsLog.getLog(CmsDriverManager.class); 583 584 /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */ 585 private static final CmsReadChangedProjectResourceMode RCPRM_FILES_AND_FOLDERS_MODE = new CmsReadChangedProjectResourceMode(); 586 587 /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */ 588 private static final CmsReadChangedProjectResourceMode RCPRM_FILES_ONLY_MODE = new CmsReadChangedProjectResourceMode(); 589 590 /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */ 591 private static final CmsReadChangedProjectResourceMode RCPRM_FOLDERS_ONLY_MODE = new CmsReadChangedProjectResourceMode(); 592 593 private final Object m_publishTagLock = new Object(); 594 595 /** The history driver. */ 596 private I_CmsHistoryDriver m_historyDriver; 597 598 /** The HTML link validator. */ 599 private CmsRelationSystemValidator m_htmlLinkValidator; 600 601 /** The class used for cache key generation. */ 602 private I_CmsCacheKey m_keyGenerator; 603 604 /** The lock manager. */ 605 private CmsLockManager m_lockManager; 606 607 /** The log entry cache. */ 608 private List<CmsLogEntry> m_log = new ArrayList<CmsLogEntry>(); 609 610 /** Local reference to the memory monitor to avoid multiple lookups through the OpenCms singleton. */ 611 private CmsMemoryMonitor m_monitor; 612 613 /** The project driver. */ 614 private I_CmsProjectDriver m_projectDriver; 615 616 /** The the configuration read from the <code>opencms.properties</code> file. */ 617 private CmsParameterConfiguration m_propertyConfiguration; 618 619 /** the publish engine. */ 620 private CmsPublishEngine m_publishEngine; 621 622 /** Object used for synchronizing updates to the user publish list. */ 623 private Object m_publishListUpdateLock = new Object(); 624 625 /** The security manager (for access checks). */ 626 private CmsSecurityManager m_securityManager; 627 628 /** The sql manager. */ 629 private CmsSqlManager m_sqlManager; 630 631 /** The subscription driver. */ 632 private I_CmsSubscriptionDriver m_subscriptionDriver; 633 634 /** The user driver. */ 635 private I_CmsUserDriver m_userDriver; 636 637 /** The VFS driver. */ 638 private I_CmsVfsDriver m_vfsDriver; 639 640 private volatile Integer m_lastPublishTag = null; 641 642 /** Cache for which OUs can be skipped for principal transfer when deleting a user. */ 643 private Cache<String, Boolean> m_skipTransferPrincipalResourceCache; 644 645 /** 646 * Private constructor, initializes some required member variables.<p> 647 */ 648 private CmsDriverManager() { 649 650 // intentionally left blank 651 } 652 653 /** 654 * Reads the required configurations from the opencms.properties file and creates 655 * the various drivers to access the cms resources.<p> 656 * 657 * The initialization process of the driver manager and its drivers is split into 658 * the following phases: 659 * <ul> 660 * <li>the database pool configuration is read</li> 661 * <li>a plain and empty driver manager instance is created</li> 662 * <li>an instance of each driver is created</li> 663 * <li>the driver manager is passed to each driver during initialization</li> 664 * <li>finally, the driver instances are passed to the driver manager during initialization</li> 665 * </ul> 666 * 667 * @param configurationManager the configuration manager 668 * @param securityManager the security manager 669 * @param runtimeInfoFactory the initialized OpenCms runtime info factory 670 * @param publishEngine the publish engine 671 * 672 * @return CmsDriverManager the instantiated driver manager 673 * @throws CmsInitException if the driver manager couldn't be instantiated 674 */ 675 public static CmsDriverManager newInstance( 676 CmsConfigurationManager configurationManager, 677 CmsSecurityManager securityManager, 678 I_CmsDbContextFactory runtimeInfoFactory, 679 CmsPublishEngine publishEngine) 680 throws CmsInitException { 681 682 // read the opencms.properties from the configuration 683 CmsParameterConfiguration config = configurationManager.getConfiguration(); 684 685 CmsDriverManager driverManager = null; 686 try { 687 // create a driver manager instance 688 driverManager = new CmsDriverManager(); 689 if (CmsLog.INIT.isInfoEnabled()) { 690 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE1_0)); 691 } 692 if (runtimeInfoFactory == null) { 693 throw new CmsInitException( 694 org.opencms.main.Messages.get().container(org.opencms.main.Messages.ERR_CRITICAL_NO_DB_CONTEXT_0)); 695 } 696 } catch (Exception exc) { 697 CmsMessageContainer message = Messages.get().container(Messages.LOG_ERR_DRIVER_MANAGER_START_0); 698 if (LOG.isFatalEnabled()) { 699 LOG.fatal(message.key(), exc); 700 } 701 throw new CmsInitException(message, exc); 702 } 703 704 // store the configuration 705 driverManager.m_propertyConfiguration = config; 706 707 // set the security manager 708 driverManager.m_securityManager = securityManager; 709 710 // set the lock manager 711 driverManager.m_lockManager = new CmsLockManager(driverManager); 712 713 // create and set the sql manager 714 driverManager.m_sqlManager = new CmsSqlManager(driverManager); 715 716 // set the publish engine 717 driverManager.m_publishEngine = publishEngine; 718 719 if (CmsLog.INIT.isInfoEnabled()) { 720 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE2_0)); 721 } 722 723 // read the pool names to initialize 724 List<String> driverPoolNames = config.getList(CmsDriverManager.CONFIGURATION_DB + ".pools"); 725 if (CmsLog.INIT.isInfoEnabled()) { 726 String names = ""; 727 for (String name : driverPoolNames) { 728 names += name + " "; 729 } 730 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_POOLS_1, names)); 731 } 732 733 // initialize each pool 734 for (String name : driverPoolNames) { 735 driverManager.newPoolInstance(config, name); 736 } 737 738 // initialize the runtime info factory with the generated driver manager 739 runtimeInfoFactory.initialize(driverManager); 740 741 if (CmsLog.INIT.isInfoEnabled()) { 742 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE3_0)); 743 } 744 745 // store the access objects 746 CmsDbContext dbc = runtimeInfoFactory.getDbContext(); 747 driverManager.m_vfsDriver = (I_CmsVfsDriver)driverManager.createDriver( 748 dbc, 749 configurationManager, 750 config, 751 CONFIGURATION_VFS, 752 ".vfs.driver"); 753 dbc.clear(); 754 755 dbc = runtimeInfoFactory.getDbContext(); 756 driverManager.m_userDriver = (I_CmsUserDriver)driverManager.createDriver( 757 dbc, 758 configurationManager, 759 config, 760 CONFIGURATION_USER, 761 ".user.driver"); 762 dbc.clear(); 763 764 dbc = runtimeInfoFactory.getDbContext(); 765 driverManager.m_projectDriver = (I_CmsProjectDriver)driverManager.createDriver( 766 dbc, 767 configurationManager, 768 config, 769 CONFIGURATION_PROJECT, 770 ".project.driver"); 771 dbc.clear(); 772 773 dbc = runtimeInfoFactory.getDbContext(); 774 driverManager.m_historyDriver = (I_CmsHistoryDriver)driverManager.createDriver( 775 dbc, 776 configurationManager, 777 config, 778 CONFIGURATION_HISTORY, 779 ".history.driver"); 780 dbc.clear(); 781 782 dbc = runtimeInfoFactory.getDbContext(); 783 try { 784 // we wrap this in a try-catch because otherwise it would fail during the update 785 // process, since the subscription driver configuration does not exist at that point. 786 driverManager.m_subscriptionDriver = (I_CmsSubscriptionDriver)driverManager.createDriver( 787 dbc, 788 configurationManager, 789 config, 790 CONFIGURATION_SUBSCRIPTION, 791 ".subscription.driver"); 792 } catch (IndexOutOfBoundsException npe) { 793 LOG.warn("Could not instantiate subscription driver!"); 794 LOG.warn(npe.getLocalizedMessage(), npe); 795 } 796 dbc.clear(); 797 798 // register the driver manager for required events 799 org.opencms.main.OpenCms.addCmsEventListener( 800 driverManager, 801 new int[] { 802 I_CmsEventListener.EVENT_UPDATE_EXPORTS, 803 I_CmsEventListener.EVENT_CLEAR_CACHES, 804 I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES, 805 I_CmsEventListener.EVENT_USER_MODIFIED, 806 I_CmsEventListener.EVENT_RESOURCE_MODIFIED, 807 I_CmsEventListener.EVENT_OU_MODIFIED, 808 I_CmsEventListener.EVENT_PUBLISH_PROJECT}); 809 810 // not sure the manual cache flushing based on events is sufficient in all cases for the 'is principal transfer skippable?' cache, 811 // so we limit it to a few minutes 812 Cache<String, Boolean> skipTransferPrincipalResourceCache = CacheBuilder.newBuilder().concurrencyLevel( 813 4).expireAfterWrite(5, TimeUnit.MINUTES).build(); 814 driverManager.m_skipTransferPrincipalResourceCache = skipTransferPrincipalResourceCache; 815 816 // return the configured driver manager 817 return driverManager; 818 } 819 820 /** 821 * Adds an alias entry.<p> 822 * 823 * @param dbc the database context 824 * @param project the current project 825 * @param alias the alias to add 826 * 827 * @throws CmsException if something goes wrong 828 */ 829 public void addAlias(CmsDbContext dbc, CmsProject project, CmsAlias alias) throws CmsException { 830 831 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 832 vfsDriver.insertAlias(dbc, project, alias); 833 } 834 835 /** 836 * Adds a new relation to the given resource.<p> 837 * 838 * @param dbc the database context 839 * @param resource the resource to add the relation to 840 * @param target the target of the relation 841 * @param type the type of the relation 842 * @param importCase if importing relations 843 * 844 * @throws CmsException if something goes wrong 845 */ 846 public void addRelationToResource( 847 CmsDbContext dbc, 848 CmsResource resource, 849 CmsResource target, 850 CmsRelationType type, 851 boolean importCase) 852 throws CmsException { 853 854 if (type.isDefinedInContent()) { 855 throw new CmsIllegalArgumentException( 856 Messages.get().container( 857 Messages.ERR_ADD_RELATION_IN_CONTENT_3, 858 dbc.removeSiteRoot(resource.getRootPath()), 859 dbc.removeSiteRoot(target.getRootPath()), 860 type.getLocalizedName(dbc.getRequestContext().getLocale()))); 861 } 862 CmsRelation relation = new CmsRelation(resource, target, type); 863 getVfsDriver(dbc).createRelation(dbc, dbc.currentProject().getUuid(), relation); 864 // IMPORTANT: In the import case, the importer has to ensure that the 865 // resource is reindexed offline after the relation has been added. 866 // We do not trigger reindexing here for each added relation due to performance issues. 867 // in the the non-import case reindexing is triggered by the update of the last modification date. 868 if (!importCase) { 869 // log it 870 log( 871 dbc, 872 new CmsLogEntry( 873 dbc, 874 resource.getStructureId(), 875 CmsLogEntryType.RESOURCE_ADD_RELATION, 876 new String[] {relation.getSourcePath(), relation.getTargetPath()}), 877 false); 878 // touch the resource 879 setDateLastModified(dbc, resource, System.currentTimeMillis()); 880 } 881 } 882 883 /** 884 * Adds a resource to the given organizational unit.<p> 885 * 886 * @param dbc the current db context 887 * @param orgUnit the organizational unit to add the resource to 888 * @param resource the resource that is to be added to the organizational unit 889 * 890 * @throws CmsException if something goes wrong 891 * 892 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 893 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 894 */ 895 public void addResourceToOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource) 896 throws CmsException { 897 898 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 899 getUserDriver(dbc).addResourceToOrganizationalUnit(dbc, orgUnit, resource); 900 } 901 902 /** 903 * Adds a user to a group.<p> 904 * 905 * @param dbc the current database context 906 * @param username the name of the user that is to be added to the group 907 * @param groupname the name of the group 908 * @param readRoles if reading roles or groups 909 * 910 * @throws CmsException if operation was not successful 911 * @throws CmsDbEntryNotFoundException if the given user or the given group was not found 912 * 913 * @see #removeUserFromGroup(CmsDbContext, String, String, boolean) 914 */ 915 public void addUserToGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 916 throws CmsException, CmsDbEntryNotFoundException { 917 918 //check if group exists 919 CmsGroup group = readGroup(dbc, groupname); 920 if (group == null) { 921 // the group does not exists 922 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 923 } 924 if (group.isVirtual() && !readRoles) { 925 String roleName = CmsRole.valueOf(group).getGroupName(); 926 if (!userInGroup(dbc, username, roleName, true)) { 927 addUserToGroup(dbc, username, roleName, true); 928 return; 929 } 930 } 931 if (group.isVirtual()) { 932 // this is an hack to prevent unlimited recursive calls 933 readRoles = false; 934 } 935 if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) { 936 // we want a role but we got a group, or the other way 937 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 938 } 939 if (userInGroup(dbc, username, groupname, readRoles)) { 940 // the user is already member of the group 941 return; 942 } 943 //check if the user exists 944 CmsUser user = readUser(dbc, username); 945 if (user == null) { 946 // the user does not exists 947 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, username)); 948 } 949 950 // if adding an user to a role 951 if (readRoles) { 952 CmsRole role = CmsRole.valueOf(group); 953 // a role can only be set if the user has the given role 954 m_securityManager.checkRole(dbc, role); 955 // now we check if we already have the role 956 if (m_securityManager.hasRole(dbc, user, role)) { 957 // do nothing 958 return; 959 } 960 // and now we need to remove all possible child-roles 961 List<CmsRole> children = role.getChildren(true); 962 Iterator<CmsGroup> itUserGroups = getGroupsOfUser( 963 dbc, 964 username, 965 group.getOuFqn(), 966 true, 967 true, 968 true, 969 dbc.getRequestContext().getRemoteAddress()).iterator(); 970 while (itUserGroups.hasNext()) { 971 CmsGroup roleGroup = itUserGroups.next(); 972 if (children.contains(CmsRole.valueOf(roleGroup))) { 973 // remove only child roles 974 removeUserFromGroup(dbc, username, roleGroup.getName(), true); 975 } 976 } 977 // update virtual groups 978 Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator(); 979 while (it.hasNext()) { 980 CmsGroup virtualGroup = it.next(); 981 // here we say readroles = true, to prevent an unlimited recursive calls 982 addUserToGroup(dbc, username, virtualGroup.getName(), true); 983 } 984 } 985 986 //add this user to the group 987 getUserDriver(dbc).createUserInGroup(dbc, user.getId(), group.getId()); 988 989 // flush the cache 990 if (readRoles) { 991 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 992 } 993 m_monitor.flushUserGroups(user.getId()); 994 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 995 996 if (!dbc.getProjectId().isNullUUID() && !CmsProject.ONLINE_PROJECT_ID.equals(dbc.getProjectId())) { 997 // user modified event is not needed 998 return; 999 } 1000 // fire user modified event 1001 Map<String, Object> eventData = new HashMap<String, Object>(); 1002 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 1003 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 1004 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 1005 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 1006 eventData.put( 1007 I_CmsEventListener.KEY_USER_ACTION, 1008 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP); 1009 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 1010 } 1011 1012 /** 1013 * Changes the lock of a resource to the current user, 1014 * that is "steals" the lock from another user.<p> 1015 * 1016 * @param dbc the current database context 1017 * @param resource the resource to change the lock for 1018 * @param lockType the new lock type to set 1019 * 1020 * @throws CmsException if something goes wrong 1021 * @throws CmsSecurityException if something goes wrong 1022 * 1023 * 1024 * @see CmsObject#changeLock(String) 1025 * @see I_CmsResourceType#changeLock(CmsObject, CmsSecurityManager, CmsResource) 1026 * 1027 * @see CmsSecurityManager#hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter) 1028 */ 1029 public void changeLock(CmsDbContext dbc, CmsResource resource, CmsLockType lockType) 1030 throws CmsException, CmsSecurityException { 1031 1032 // get the current lock 1033 CmsLock currentLock = getLock(dbc, resource); 1034 // check if the resource is locked at all 1035 if (currentLock.getEditionLock().isUnlocked() && currentLock.getSystemLock().isUnlocked()) { 1036 throw new CmsLockException( 1037 Messages.get().container( 1038 Messages.ERR_CHANGE_LOCK_UNLOCKED_RESOURCE_1, 1039 dbc.getRequestContext().getSitePath(resource))); 1040 } else if ((lockType == CmsLockType.EXCLUSIVE) 1041 && currentLock.isExclusiveOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) { 1042 // the current lock requires no change 1043 return; 1044 } 1045 1046 // duplicate logic from CmsSecurityManager#hasPermissions() because lock state can't be ignored 1047 // if another user has locked the file, the current user can never get WRITE permissions with the default check 1048 int denied = 0; 1049 1050 // check if the current user is vfs manager 1051 boolean canIgnorePermissions = m_securityManager.hasRoleForResource( 1052 dbc, 1053 dbc.currentUser(), 1054 CmsRole.VFS_MANAGER, 1055 resource); 1056 // if the resource type is jsp 1057 // write is only allowed for developers 1058 if (!canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) { 1059 if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource)) { 1060 denied |= CmsPermissionSet.PERMISSION_WRITE; 1061 } 1062 } 1063 CmsPermissionSetCustom permissions; 1064 if (canIgnorePermissions) { 1065 // if the current user is administrator, anything is allowed 1066 permissions = new CmsPermissionSetCustom(~0); 1067 } else { 1068 // otherwise, get the permissions from the access control list 1069 permissions = getPermissions(dbc, resource, dbc.currentUser()); 1070 } 1071 // revoke the denied permissions 1072 permissions.denyPermissions(denied); 1073 // now check if write permission is granted 1074 if ((CmsPermissionSet.ACCESS_WRITE.getPermissions() 1075 & permissions.getPermissions()) != CmsPermissionSet.ACCESS_WRITE.getPermissions()) { 1076 // check failed, throw exception 1077 m_securityManager.checkPermissions( 1078 dbc.getRequestContext(), 1079 resource, 1080 CmsPermissionSet.ACCESS_WRITE, 1081 I_CmsPermissionHandler.PERM_DENIED); 1082 } 1083 // if we got here write permission is granted on the target 1084 1085 // remove the old lock 1086 m_lockManager.removeResource(dbc, resource, true, lockType.isSystem()); 1087 // apply the new lock 1088 lockResource(dbc, resource, lockType); 1089 } 1090 1091 /** 1092 * Returns a list with all sub resources of a given folder that have set the given property, 1093 * matching the current property's value with the given old value and replacing it by a given new value.<p> 1094 * 1095 * @param dbc the current database context 1096 * @param resource the resource on which property definition values are changed 1097 * @param propertyDefinition the name of the propertydefinition to change the value 1098 * @param oldValue the old value of the propertydefinition 1099 * @param newValue the new value of the propertydefinition 1100 * @param recursive if true, change the property value on the resource and recursively all property values on 1101 * sub-resources (only for folders) 1102 * @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed 1103 * 1104 * @throws CmsVfsException for now only when the search for the oldvalue failed. 1105 * @throws CmsException if operation was not successful 1106 */ 1107 public List<CmsResource> changeResourcesInFolderWithProperty( 1108 CmsDbContext dbc, 1109 CmsResource resource, 1110 String propertyDefinition, 1111 String oldValue, 1112 String newValue, 1113 boolean recursive) 1114 throws CmsVfsException, CmsException { 1115 1116 CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION; 1117 // collect the resources to look up 1118 List<CmsResource> resources = new ArrayList<CmsResource>(); 1119 if (recursive) { 1120 // read the files in the folder 1121 resources = readResourcesWithProperty(dbc, resource, propertyDefinition, null, filter); 1122 // add the folder itself 1123 resources.add(resource); 1124 } else { 1125 resources.add(resource); 1126 } 1127 1128 Pattern oldPattern; 1129 try { 1130 // remove the place holder if available 1131 String tmpOldValue = oldValue; 1132 if (tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_START) 1133 && tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_END)) { 1134 tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_START, ""); 1135 tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_END, ""); 1136 } 1137 // compile regular expression pattern 1138 oldPattern = Pattern.compile(tmpOldValue); 1139 } catch (PatternSyntaxException e) { 1140 throw new CmsVfsException( 1141 Messages.get().container( 1142 Messages.ERR_CHANGE_RESOURCES_IN_FOLDER_WITH_PROP_4, 1143 new Object[] {propertyDefinition, oldValue, newValue, resource.getRootPath()}), 1144 e); 1145 } 1146 1147 List<CmsResource> changedResources = new ArrayList<CmsResource>(resources.size()); 1148 // create permission set and filter to check each resource 1149 CmsPermissionSet perm = CmsPermissionSet.ACCESS_WRITE; 1150 for (int i = 0; i < resources.size(); i++) { 1151 // loop through found resources and check property values 1152 CmsResource res = resources.get(i); 1153 // check resource state and permissions 1154 try { 1155 m_securityManager.checkPermissions(dbc, res, perm, true, filter); 1156 } catch (Exception e) { 1157 // resource is deleted or not writable for current user 1158 continue; 1159 } 1160 CmsProperty property = readPropertyObject(dbc, res, propertyDefinition, false); 1161 String propertyValue = property.getValue(); 1162 boolean changed = false; 1163 if ((propertyValue != null) && oldPattern.matcher(propertyValue).matches()) { 1164 // apply the place holder content 1165 String tmpNewValue = CmsStringUtil.transformValues(oldValue, newValue, propertyValue); 1166 // change structure value 1167 property.setStructureValue(tmpNewValue); 1168 changed = true; 1169 } 1170 if (changed) { 1171 // write property object if something has changed 1172 writePropertyObject(dbc, res, property); 1173 changedResources.add(res); 1174 } 1175 } 1176 return changedResources; 1177 } 1178 1179 /** 1180 * Changes the resource flags of a resource.<p> 1181 * 1182 * The resource flags are used to indicate various "special" conditions 1183 * for a resource. Most notably, the "internal only" setting which signals 1184 * that a resource can not be directly requested with it's URL.<p> 1185 * 1186 * @param dbc the current database context 1187 * @param resource the resource to change the flags for 1188 * @param flags the new resource flags for this resource 1189 * 1190 * @throws CmsException if something goes wrong 1191 * 1192 * @see CmsObject#chflags(String, int) 1193 * @see I_CmsResourceType#chflags(CmsObject, CmsSecurityManager, CmsResource, int) 1194 */ 1195 public void chflags(CmsDbContext dbc, CmsResource resource, int flags) throws CmsException { 1196 1197 // must operate on a clone to ensure resource is not modified in case permissions are not granted 1198 CmsResource clone = (CmsResource)resource.clone(); 1199 clone.setFlags(flags); 1200 // log it 1201 log( 1202 dbc, 1203 new CmsLogEntry( 1204 dbc, 1205 resource.getStructureId(), 1206 CmsLogEntryType.RESOURCE_FLAGS, 1207 new String[] {resource.getRootPath()}), 1208 false); 1209 // write it 1210 writeResource(dbc, clone); 1211 } 1212 1213 /** 1214 * Changes the resource type of a resource.<p> 1215 * 1216 * OpenCms handles resources according to the resource type, 1217 * not the file suffix. This is e.g. why a JSP in OpenCms can have the 1218 * suffix ".html" instead of ".jsp" only. Changing the resource type 1219 * makes sense e.g. if you want to make a plain text file a JSP resource, 1220 * or a binary file an image, etc.<p> 1221 * 1222 * @param dbc the current database context 1223 * @param resource the resource to change the type for 1224 * @param type the new resource type for this resource 1225 * 1226 * @throws CmsException if something goes wrong 1227 * 1228 * @see CmsObject#chtype(String, int) 1229 * @see I_CmsResourceType#chtype(CmsObject, CmsSecurityManager, CmsResource, int) 1230 */ 1231 @SuppressWarnings({"javadoc", "deprecation"}) 1232 public void chtype(CmsDbContext dbc, CmsResource resource, int type) throws CmsException { 1233 1234 // must operate on a clone to ensure resource is not modified in case permissions are not granted 1235 CmsResource clone = (CmsResource)resource.clone(); 1236 I_CmsResourceType newType = OpenCms.getResourceManager().getResourceType(type); 1237 clone.setType(newType.getTypeId()); 1238 // log it 1239 log( 1240 dbc, 1241 new CmsLogEntry( 1242 dbc, 1243 resource.getStructureId(), 1244 CmsLogEntryType.RESOURCE_TYPE, 1245 new String[] {resource.getRootPath()}), 1246 false); 1247 // write it 1248 writeResource(dbc, clone); 1249 } 1250 1251 /** 1252 * Cleans up the publish history entries according to the given filter. 1253 * 1254 * @param dbc the database context 1255 * @param filter the filter 1256 * @return the number of cleaned up rows 1257 * @throws CmsDataAccessException if something goes wrong 1258 */ 1259 public int cleanupPublishHistory(CmsDbContext dbc, CmsPublishHistoryCleanupFilter filter) 1260 throws CmsDataAccessException { 1261 1262 int result = m_projectDriver.cleanupPublishHistory(dbc, filter); 1263 if (filter.getMode() == CmsPublishHistoryCleanupFilter.Mode.single) { 1264 OpenCms.getMemoryMonitor().cachePublishedResources(filter.getHistoryId().toString(), null); 1265 } else { 1266 OpenCms.getMemoryMonitor().flushCache(CmsMemoryMonitor.CacheType.PUBLISHED_RESOURCES); 1267 } 1268 return result; 1269 } 1270 1271 /** 1272 * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) 1273 */ 1274 public void cmsEvent(CmsEvent event) { 1275 1276 if (LOG.isDebugEnabled()) { 1277 LOG.debug(Messages.get().getBundle().key(Messages.LOG_CMS_EVENT_1, Integer.valueOf(event.getType()))); 1278 } 1279 1280 I_CmsReport report; 1281 CmsDbContext dbc; 1282 1283 switch (event.getType()) { 1284 1285 case I_CmsEventListener.EVENT_UPDATE_EXPORTS: 1286 dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT); 1287 updateExportPoints(dbc); 1288 break; 1289 1290 case I_CmsEventListener.EVENT_PUBLISH_PROJECT: 1291 CmsUUID publishHistoryId = new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID)); 1292 report = (I_CmsReport)event.getData().get(I_CmsEventListener.KEY_REPORT); 1293 dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT); 1294 m_monitor.clearCacheForPublishing(); 1295 writeExportPoints(dbc, report, publishHistoryId); 1296 break; 1297 1298 case I_CmsEventListener.EVENT_CLEAR_CACHES: 1299 m_monitor.clearCache(); 1300 m_skipTransferPrincipalResourceCache.invalidateAll(); 1301 break; 1302 case I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES: 1303 m_monitor.clearPrincipalsCache(); 1304 break; 1305 case I_CmsEventListener.EVENT_USER_MODIFIED: 1306 String action = (String)event.getData().get(I_CmsEventListener.KEY_USER_ACTION); 1307 1308 if (I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER.equals(action)) { 1309 1310 // users are written *all the time* during normal editing operations, but we don't usually need to clear 1311 // caches that don't contain any user objects or depend on the user data itself. 1312 // so we handle this case separately. 1313 1314 List<CacheType> toClear = new ArrayList<>(); 1315 toClear.addAll(Arrays.asList(CacheType.USER, CacheType.USER_LIST)); 1316 Object changesObj = event.getData().get(I_CmsEventListener.KEY_USER_CHANGES); 1317 if (changesObj instanceof Integer) { 1318 int changes = ((Integer)changesObj).intValue(); 1319 if ((changes & CmsUser.FLAG_CORE_DATA) > 0) { 1320 // core data changes include flags, which may conceivably be used by permission checks, and happen rarely. 1321 toClear.add(CacheType.PERMISSION); 1322 } 1323 } 1324 m_monitor.flushCache(toClear.toArray(new CacheType[] {})); 1325 } else { 1326 m_monitor.flushCache( 1327 CacheType.USER, 1328 CacheType.GROUP, 1329 CacheType.ORG_UNIT, 1330 CacheType.ACL, 1331 CacheType.PERMISSION, 1332 CacheType.USER_LIST); 1333 } 1334 1335 if (I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP.equals(action) 1336 || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP.equals(action) 1337 || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU.equals(action)) { 1338 1339 Object userIdObj = event.getData().get(I_CmsEventListener.KEY_USER_ID); 1340 if (userIdObj != null) { 1341 CmsUUID userId = null; 1342 if (userIdObj instanceof CmsUUID) { 1343 userId = (CmsUUID)userIdObj; 1344 } else if (userIdObj instanceof String) { 1345 try { 1346 userId = new CmsUUID(userIdObj.toString()); 1347 } catch (Exception e) { 1348 LOG.error(e.getLocalizedMessage(), e); 1349 } 1350 } 1351 if (userId != null) { 1352 m_monitor.flushUserGroups(userId); 1353 } 1354 } else { 1355 m_monitor.flushCache(CacheType.USERGROUPS); 1356 } 1357 m_monitor.flushCache(CacheType.HAS_ROLE, CacheType.ROLE_LIST); 1358 } 1359 break; 1360 case I_CmsEventListener.EVENT_RESOURCE_MODIFIED: 1361 1362 Object resObj = event.getData().get(I_CmsEventListener.KEY_RESOURCE); 1363 if ((resObj != null) && (resObj instanceof CmsResource)) { 1364 CmsResource resource = (CmsResource)resObj; 1365 if (resource.getRootPath().startsWith(CmsUserDriver.ORGUNIT_BASE_FOLDER)) { 1366 m_monitor.flushCache(CacheType.ORG_UNIT_RESOURCES); 1367 m_skipTransferPrincipalResourceCache.invalidateAll(); 1368 } 1369 } 1370 break; 1371 case I_CmsEventListener.EVENT_OU_MODIFIED: 1372 m_monitor.flushCache(CacheType.ORG_UNIT_RESOURCES); 1373 m_skipTransferPrincipalResourceCache.invalidateAll(); 1374 break; 1375 default: 1376 // noop 1377 } 1378 } 1379 1380 /** 1381 * Copies the access control entries of a given resource to a destination resource.<p> 1382 * 1383 * Already existing access control entries of the destination resource are removed.<p> 1384 * 1385 * @param dbc the current database context 1386 * @param source the resource to copy the access control entries from 1387 * @param destination the resource to which the access control entries are copied 1388 * @param updateLastModifiedInfo if true, user and date "last modified" information on the target resource will be updated 1389 * 1390 * @throws CmsException if something goes wrong 1391 */ 1392 public void copyAccessControlEntries( 1393 CmsDbContext dbc, 1394 CmsResource source, 1395 CmsResource destination, 1396 boolean updateLastModifiedInfo) 1397 throws CmsException { 1398 1399 // get the entries to copy 1400 ListIterator<CmsAccessControlEntry> aceList = getUserDriver( 1401 dbc).readAccessControlEntries(dbc, dbc.currentProject(), source.getResourceId(), false).listIterator(); 1402 1403 // remove the current entries from the destination 1404 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), destination.getResourceId()); 1405 1406 // now write the new entries 1407 while (aceList.hasNext()) { 1408 CmsAccessControlEntry ace = aceList.next(); 1409 getUserDriver(dbc).createAccessControlEntry( 1410 dbc, 1411 dbc.currentProject(), 1412 destination.getResourceId(), 1413 ace.getPrincipal(), 1414 ace.getPermissions().getAllowedPermissions(), 1415 ace.getPermissions().getDeniedPermissions(), 1416 ace.getFlags()); 1417 } 1418 1419 // log it 1420 log( 1421 dbc, 1422 new CmsLogEntry( 1423 dbc, 1424 destination.getStructureId(), 1425 CmsLogEntryType.RESOURCE_PERMISSIONS, 1426 new String[] {destination.getRootPath()}), 1427 false); 1428 1429 // update the "last modified" information 1430 if (updateLastModifiedInfo) { 1431 setDateLastModified(dbc, destination, destination.getDateLastModified()); 1432 } 1433 1434 // clear the cache 1435 m_monitor.clearAccessControlListCache(); 1436 1437 // fire a resource modification event 1438 Map<String, Object> data = new HashMap<String, Object>(2); 1439 data.put(I_CmsEventListener.KEY_RESOURCE, destination); 1440 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 1441 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 1442 } 1443 1444 /** 1445 * Copies a resource.<p> 1446 * 1447 * You must ensure that the destination path is an absolute, valid and 1448 * existing VFS path. Relative paths from the source are currently not supported.<p> 1449 * 1450 * In case the target resource already exists, it is overwritten with the 1451 * source resource.<p> 1452 * 1453 * The <code>siblingMode</code> parameter controls how to handle siblings 1454 * during the copy operation. 1455 * Possible values for this parameter are: 1456 * <ul> 1457 * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_NEW}</code></li> 1458 * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_SIBLING}</code></li> 1459 * <li><code>{@link org.opencms.file.CmsResource#COPY_PRESERVE_SIBLING}</code></li> 1460 * </ul><p> 1461 * 1462 * @param dbc the current database context 1463 * @param source the resource to copy 1464 * @param destination the name of the copy destination with complete path 1465 * @param siblingMode indicates how to handle siblings during copy 1466 * 1467 * @throws CmsException if something goes wrong 1468 * @throws CmsIllegalArgumentException if the <code>source</code> argument is <code>null</code> 1469 * 1470 * @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode) 1471 * @see I_CmsResourceType#copyResource(CmsObject, CmsSecurityManager, CmsResource, String, CmsResource.CmsResourceCopyMode) 1472 */ 1473 public CmsResource copyResource( 1474 CmsDbContext dbc, 1475 CmsResource source, 1476 String destination, 1477 CmsResource.CmsResourceCopyMode siblingMode) 1478 throws CmsException, CmsIllegalArgumentException { 1479 1480 // check the sibling mode to see if this resource has to be copied as a sibling 1481 boolean copyAsSibling = false; 1482 1483 // siblings of folders are not supported 1484 if (!source.isFolder()) { 1485 // if the "copy as sibling" mode is used, set the flag to true 1486 if (siblingMode == CmsResource.COPY_AS_SIBLING) { 1487 copyAsSibling = true; 1488 } 1489 // if the mode is "preserve siblings", we have to check the sibling counter 1490 if (siblingMode == CmsResource.COPY_PRESERVE_SIBLING) { 1491 if (source.getSiblingCount() > 1) { 1492 copyAsSibling = true; 1493 } 1494 } 1495 } 1496 1497 // read the source properties 1498 List<CmsProperty> properties = readPropertyObjects(dbc, source, false); 1499 1500 if (copyAsSibling) { 1501 // create a sibling of the source file at the destination 1502 return createSibling(dbc, source, destination, properties); 1503 } 1504 1505 // prepare the content if required 1506 byte[] content = null; 1507 if (source.isFile()) { 1508 if (source instanceof CmsFile) { 1509 // resource already is a file 1510 content = ((CmsFile)source).getContents(); 1511 } 1512 if ((content == null) || (content.length < 1)) { 1513 // no known content yet - read from database 1514 content = getVfsDriver(dbc).readContent(dbc, dbc.currentProject().getUuid(), source.getResourceId()); 1515 } 1516 } 1517 1518 // determine destination folder 1519 String destinationFoldername = CmsResource.getParentFolder(destination); 1520 1521 // read the destination folder (will also check read permissions) 1522 CmsFolder destinationFolder = m_securityManager.readFolder( 1523 dbc, 1524 destinationFoldername, 1525 CmsResourceFilter.IGNORE_EXPIRATION); 1526 1527 // no further permission check required here, will be done in createResource() 1528 1529 // set user and creation time stamps 1530 long currentTime = System.currentTimeMillis(); 1531 long dateLastModified; 1532 CmsUUID userLastModified; 1533 if (source.isFolder()) { 1534 // folders always get a new date and user when they are copied 1535 dateLastModified = currentTime; 1536 userLastModified = dbc.currentUser().getId(); 1537 } else { 1538 // files keep the date and user last modified from the source 1539 dateLastModified = source.getDateLastModified(); 1540 userLastModified = source.getUserLastModified(); 1541 } 1542 1543 // check the resource flags 1544 int flags = source.getFlags(); 1545 if (source.isLabeled()) { 1546 // reset "labeled" link flag for new resource 1547 flags &= ~CmsResource.FLAG_LABELED; 1548 } 1549 1550 // create the new resource 1551 CmsResource newResource = new CmsResource( 1552 new CmsUUID(), 1553 new CmsUUID(), 1554 destination, 1555 source.getTypeId(), 1556 source.isFolder(), 1557 flags, 1558 dbc.currentProject().getUuid(), 1559 CmsResource.STATE_NEW, 1560 currentTime, 1561 dbc.currentUser().getId(), 1562 dateLastModified, 1563 userLastModified, 1564 source.getDateReleased(), 1565 source.getDateExpired(), 1566 1, 1567 source.getLength(), 1568 source.getDateContent(), 1569 source.getVersion()); // version number does not matter since it will be computed later 1570 1571 // trigger "is touched" state on resource (will ensure modification date is kept unchanged) 1572 newResource.setDateLastModified(dateLastModified); 1573 1574 // log it 1575 log( 1576 dbc, 1577 new CmsLogEntry( 1578 dbc, 1579 newResource.getStructureId(), 1580 CmsLogEntryType.RESOURCE_COPIED, 1581 new String[] {newResource.getRootPath()}), 1582 false); 1583 1584 // create the resource 1585 newResource = createResource(dbc, destination, newResource, content, properties, false); 1586 // copy relations 1587 copyRelations(dbc, source, newResource); 1588 1589 // copy the access control entries to the created resource 1590 copyAccessControlEntries(dbc, source, newResource, false); 1591 1592 // clear the cache 1593 m_monitor.clearAccessControlListCache(); 1594 1595 List<CmsResource> modifiedResources = new ArrayList<CmsResource>(); 1596 modifiedResources.add(source); 1597 modifiedResources.add(newResource); 1598 modifiedResources.add(destinationFolder); 1599 OpenCms.fireCmsEvent( 1600 new CmsEvent( 1601 I_CmsEventListener.EVENT_RESOURCE_COPIED, 1602 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, modifiedResources))); 1603 return newResource; 1604 } 1605 1606 /** 1607 * Copies a resource to the current project of the user.<p> 1608 * 1609 * @param dbc the current database context 1610 * @param resource the resource to apply this operation to 1611 * 1612 * @throws CmsException if something goes wrong 1613 * 1614 * @see CmsObject#copyResourceToProject(String) 1615 * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource) 1616 */ 1617 public void copyResourceToProject(CmsDbContext dbc, CmsResource resource) throws CmsException { 1618 1619 // copy the resource to the project only if the resource is not already in the project 1620 if (!isInsideCurrentProject(dbc, resource.getRootPath())) { 1621 // check if there are already any subfolders of this resource 1622 I_CmsProjectDriver projectDriver = getProjectDriver(dbc); 1623 if (resource.isFolder()) { 1624 List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject()); 1625 for (int i = 0; i < projectResources.size(); i++) { 1626 String resname = projectResources.get(i); 1627 if (resname.startsWith(resource.getRootPath())) { 1628 // delete the existing project resource first 1629 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname); 1630 } 1631 } 1632 } 1633 try { 1634 projectDriver.createProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath()); 1635 } catch (CmsException exc) { 1636 // if the subfolder exists already - all is ok 1637 } finally { 1638 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 1639 1640 OpenCms.fireCmsEvent( 1641 new CmsEvent( 1642 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 1643 Collections.<String, Object> singletonMap("project", dbc.currentProject()))); 1644 } 1645 } 1646 } 1647 1648 /** 1649 * Counts the locked resources in this project.<p> 1650 * 1651 * @param project the project to count the locked resources in 1652 * 1653 * @return the amount of locked resources in this project 1654 */ 1655 public int countLockedResources(CmsProject project) { 1656 1657 // count locks 1658 return m_lockManager.countExclusiveLocksInProject(project); 1659 } 1660 1661 /** 1662 * Add a new group to the Cms.<p> 1663 * 1664 * Only the admin can do this. 1665 * Only users, which are in the group "administrators" are granted.<p> 1666 * 1667 * @param dbc the current database context 1668 * @param id the id of the new group 1669 * @param name the name of the new group 1670 * @param description the description for the new group 1671 * @param flags the flags for the new group 1672 * @param parent the name of the parent group (or <code>null</code>) 1673 * 1674 * @return new created group 1675 * 1676 * @throws CmsException if the creation of the group failed 1677 * @throws CmsIllegalArgumentException if the length of the given name was below 1 1678 */ 1679 public CmsGroup createGroup(CmsDbContext dbc, CmsUUID id, String name, String description, int flags, String parent) 1680 throws CmsIllegalArgumentException, CmsException { 1681 1682 // check the group name 1683 OpenCms.getValidationHandler().checkGroupName(CmsOrganizationalUnit.getSimpleName(name)); 1684 // trim the name 1685 name = name.trim(); 1686 1687 // check the OU 1688 readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 1689 1690 // get the id of the parent group if necessary 1691 if (CmsStringUtil.isNotEmpty(parent)) { 1692 CmsGroup parentGroup = readGroup(dbc, parent); 1693 if (!parentGroup.isRole() 1694 && !CmsOrganizationalUnit.getParentFqn(parent).equals(CmsOrganizationalUnit.getParentFqn(name))) { 1695 throw new CmsDataAccessException( 1696 Messages.get().container( 1697 Messages.ERR_PARENT_GROUP_MUST_BE_IN_SAME_OU_3, 1698 CmsOrganizationalUnit.getSimpleName(name), 1699 CmsOrganizationalUnit.getParentFqn(name), 1700 parent)); 1701 } 1702 } 1703 1704 // create the group 1705 CmsGroup group = getUserDriver(dbc).createGroup(dbc, id, name, description, flags, parent); 1706 1707 // if the group is in fact a role, initialize it 1708 if (group.isVirtual()) { 1709 // get all users that have the given role 1710 String groupname = CmsRole.valueOf(group).getGroupName(); 1711 Iterator<CmsUser> it = getUsersOfGroup(dbc, groupname, true, false, true).iterator(); 1712 while (it.hasNext()) { 1713 CmsUser user = it.next(); 1714 // put them in the new group 1715 addUserToGroup(dbc, user.getName(), group.getName(), true); 1716 } 1717 } 1718 1719 // put it into the cache 1720 m_monitor.cacheGroup(group); 1721 1722 if (!dbc.getProjectId().isNullUUID()) { 1723 // group modified event is not needed 1724 return group; 1725 } 1726 // fire group modified event 1727 Map<String, Object> eventData = new HashMap<String, Object>(); 1728 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 1729 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 1730 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_CREATE); 1731 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 1732 1733 // return it 1734 return group; 1735 } 1736 1737 /** 1738 * Creates a new organizational unit.<p> 1739 * 1740 * @param dbc the current db context 1741 * @param ouFqn the fully qualified name of the new organizational unit 1742 * @param description the description of the new organizational unit 1743 * @param flags the flags for the new organizational unit 1744 * @param resource the first associated resource 1745 * 1746 * @return a <code>{@link CmsOrganizationalUnit}</code> object representing 1747 * the newly created organizational unit 1748 * 1749 * @throws CmsException if operation was not successful 1750 * 1751 * @see org.opencms.security.CmsOrgUnitManager#createOrganizationalUnit(CmsObject, String, String, int, String) 1752 */ 1753 public CmsOrganizationalUnit createOrganizationalUnit( 1754 CmsDbContext dbc, 1755 String ouFqn, 1756 String description, 1757 int flags, 1758 CmsResource resource) 1759 throws CmsException { 1760 1761 // normal case 1762 CmsOrganizationalUnit parent = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(ouFqn)); 1763 String name = CmsOrganizationalUnit.getSimpleName(ouFqn); 1764 if (name.endsWith(CmsOrganizationalUnit.SEPARATOR)) { 1765 name = name.substring(0, name.length() - 1); 1766 } 1767 1768 // check the name 1769 CmsResource.checkResourceName(name); 1770 1771 // trim the name 1772 name = name.trim(); 1773 1774 // check the description 1775 if (CmsStringUtil.isEmptyOrWhitespaceOnly(description)) { 1776 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_OU_DESCRIPTION_EMPTY_0)); 1777 } 1778 1779 // create the organizational unit 1780 CmsOrganizationalUnit orgUnit = getUserDriver(dbc).createOrganizationalUnit( 1781 dbc, 1782 name, 1783 description, 1784 flags, 1785 parent, 1786 resource != null ? resource.getRootPath() : null); 1787 // put the new created org unit into the cache 1788 m_monitor.cacheOrgUnit(orgUnit); 1789 1790 // flush relevant caches 1791 m_monitor.clearPrincipalsCache(); 1792 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 1793 1794 // create a publish list for the 'virtual' publish event 1795 CmsResource ouRes = readResource( 1796 dbc, 1797 CmsUserDriver.ORGUNIT_BASE_FOLDER + orgUnit.getName(), 1798 CmsResourceFilter.DEFAULT); 1799 CmsPublishList pl = new CmsPublishList(ouRes, false); 1800 pl.add(ouRes, false); 1801 1802 getProjectDriver(dbc).writePublishHistory( 1803 dbc, 1804 pl.getPublishHistoryId(), 1805 new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW)); 1806 1807 // fire the 'virtual' publish event 1808 Map<String, Object> eventData = new HashMap<String, Object>(); 1809 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 1810 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 1811 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 1812 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 1813 OpenCms.fireCmsEvent(afterPublishEvent); 1814 1815 if (!dbc.getProjectId().isNullUUID()) { 1816 // OU modified event is not needed 1817 return orgUnit; 1818 } 1819 1820 // fire OU modified event 1821 Map<String, Object> event2Data = new HashMap<String, Object>(); 1822 event2Data.put(I_CmsEventListener.KEY_OU_NAME, orgUnit.getName()); 1823 event2Data.put(I_CmsEventListener.KEY_OU_ID, orgUnit.getId().toString()); 1824 event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_CREATE); 1825 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data)); 1826 1827 // return it 1828 return orgUnit; 1829 } 1830 1831 /** 1832 * Creates a project.<p> 1833 * 1834 * @param dbc the current database context 1835 * @param name the name of the project to create 1836 * @param description the description of the project 1837 * @param groupname the project user group to be set 1838 * @param managergroupname the project manager group to be set 1839 * @param projecttype the type of the project 1840 * 1841 * @return the created project 1842 * 1843 * @throws CmsIllegalArgumentException if the chosen <code>name</code> is already used 1844 * by the online project, or if the name is not valid 1845 * @throws CmsException if something goes wrong 1846 */ 1847 public CmsProject createProject( 1848 CmsDbContext dbc, 1849 String name, 1850 String description, 1851 String groupname, 1852 String managergroupname, 1853 CmsProject.CmsProjectType projecttype) 1854 throws CmsIllegalArgumentException, CmsException { 1855 1856 if (CmsProject.ONLINE_PROJECT_NAME.equals(name)) { 1857 throw new CmsIllegalArgumentException( 1858 Messages.get().container( 1859 Messages.ERR_CREATE_PROJECT_ONLINE_PROJECT_NAME_1, 1860 CmsProject.ONLINE_PROJECT_NAME)); 1861 } 1862 // check the name 1863 CmsProject.checkProjectName(CmsOrganizationalUnit.getSimpleName(name)); 1864 // check the ou 1865 readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 1866 // read the needed groups from the cms 1867 CmsGroup group = readGroup(dbc, groupname); 1868 CmsGroup managergroup = readGroup(dbc, managergroupname); 1869 1870 return getProjectDriver(dbc).createProject( 1871 dbc, 1872 new CmsUUID(), 1873 dbc.currentUser(), 1874 group, 1875 managergroup, 1876 name, 1877 description, 1878 projecttype.getDefaultFlags(), 1879 projecttype); 1880 } 1881 1882 /** 1883 * Creates a property definition.<p> 1884 * 1885 * Property definitions are valid for all resource types.<p> 1886 * 1887 * @param dbc the current database context 1888 * @param name the name of the property definition to create 1889 * 1890 * @return the created property definition 1891 * 1892 * @throws CmsException if something goes wrong 1893 */ 1894 public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 1895 1896 CmsPropertyDefinition propertyDefinition = null; 1897 1898 name = name.trim(); 1899 // validate the property name 1900 CmsPropertyDefinition.checkPropertyName(name); 1901 // TODO: make the type a parameter 1902 try { 1903 try { 1904 propertyDefinition = getVfsDriver(dbc).readPropertyDefinition( 1905 dbc, 1906 name, 1907 dbc.currentProject().getUuid()); 1908 } catch (CmsException e) { 1909 propertyDefinition = getVfsDriver(dbc).createPropertyDefinition( 1910 dbc, 1911 dbc.currentProject().getUuid(), 1912 name, 1913 CmsPropertyDefinition.TYPE_NORMAL); 1914 } 1915 1916 try { 1917 getVfsDriver(dbc).readPropertyDefinition(dbc, name, CmsProject.ONLINE_PROJECT_ID); 1918 } catch (CmsException e) { 1919 getVfsDriver(dbc).createPropertyDefinition( 1920 dbc, 1921 CmsProject.ONLINE_PROJECT_ID, 1922 name, 1923 CmsPropertyDefinition.TYPE_NORMAL); 1924 } 1925 1926 try { 1927 getHistoryDriver(dbc).readPropertyDefinition(dbc, name); 1928 } catch (CmsException e) { 1929 getHistoryDriver(dbc).createPropertyDefinition(dbc, name, CmsPropertyDefinition.TYPE_NORMAL); 1930 } 1931 } finally { 1932 1933 // fire an event that a property of a resource has been deleted 1934 OpenCms.fireCmsEvent( 1935 new CmsEvent( 1936 I_CmsEventListener.EVENT_PROPERTY_DEFINITION_CREATED, 1937 Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition))); 1938 1939 } 1940 1941 return propertyDefinition; 1942 } 1943 1944 /** 1945 * Creates a new publish job.<p> 1946 * 1947 * @param dbc the current database context 1948 * @param publishJob the publish job to create 1949 * 1950 * @throws CmsException if something goes wrong 1951 */ 1952 public void createPublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 1953 1954 getProjectDriver(dbc).createPublishJob(dbc, publishJob); 1955 } 1956 1957 /** 1958 * Creates a new resource with the provided content and properties.<p> 1959 * 1960 * The <code>content</code> parameter may be <code>null</code> if the resource id 1961 * already exists. If so, the created resource will be a sibling of the existing 1962 * resource, the existing content will remain unchanged.<p> 1963 * 1964 * This is used during file import for import of siblings as the 1965 * <code>manifest.xml</code> only contains one binary copy per file.<p> 1966 * 1967 * If the resource id exists but the <code>content</code> is not <code>null</code>, 1968 * the created resource will be made a sibling of the existing resource, 1969 * and both will share the new content.<p> 1970 * 1971 * @param dbc the current database context 1972 * @param resourcePath the name of the resource to create (full path) 1973 * @param resource the new resource to create 1974 * @param content the content for the new resource 1975 * @param properties the properties for the new resource 1976 * @param importCase if <code>true</code>, signals that this operation is done while 1977 * importing resource, causing different lock behavior and 1978 * potential "lost and found" usage 1979 * 1980 * @return the created resource 1981 * 1982 * @throws CmsException if something goes wrong 1983 */ 1984 public CmsResource createResource( 1985 CmsDbContext dbc, 1986 String resourcePath, 1987 CmsResource resource, 1988 byte[] content, 1989 List<CmsProperty> properties, 1990 boolean importCase) 1991 throws CmsException { 1992 1993 CmsResource newResource = null; 1994 if (resource.isFolder()) { 1995 resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath); 1996 } 1997 1998 try { 1999 synchronized (this) { 2000 // need to provide the parent folder id for resource creation 2001 String parentFolderName = CmsResource.getParentFolder(resourcePath); 2002 CmsResource parentFolder = readFolder(dbc, parentFolderName, CmsResourceFilter.IGNORE_EXPIRATION); 2003 2004 CmsLock parentLock = getLock(dbc, parentFolder); 2005 // it is not allowed to create a resource in a folder locked by other user 2006 if (!parentLock.isUnlocked() && !parentLock.isOwnedBy(dbc.currentUser())) { 2007 // one exception is if the admin user tries to create a temporary resource 2008 if (!CmsResource.getName(resourcePath).startsWith(TEMP_FILE_PREFIX) 2009 || !m_securityManager.hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) { 2010 throw new CmsLockException( 2011 Messages.get().container( 2012 Messages.ERR_CREATE_RESOURCE_PARENT_LOCK_1, 2013 dbc.removeSiteRoot(resourcePath))); 2014 } 2015 } 2016 if (CmsResourceTypeJsp.isJsp(resource)) { 2017 // security check when trying to create a new jsp file 2018 m_securityManager.checkRoleForResource(dbc, CmsRole.VFS_MANAGER, parentFolder); 2019 } 2020 2021 // check import configuration of "lost and found" folder 2022 boolean useLostAndFound = importCase && !OpenCms.getImportExportManager().overwriteCollidingResources(); 2023 2024 // check if the resource already exists by name 2025 CmsResource currentResourceByName = null; 2026 try { 2027 currentResourceByName = readResource(dbc, resourcePath, CmsResourceFilter.ALL); 2028 } catch (CmsVfsResourceNotFoundException e) { 2029 // if the resource does exist, we have to check the id later to decide what to do 2030 } 2031 2032 // check if the resource already exists by id 2033 try { 2034 CmsResource currentResourceById = readResource( 2035 dbc, 2036 resource.getStructureId(), 2037 CmsResourceFilter.ALL); 2038 // it is not allowed to import resources when there is already a resource with the same id but different path 2039 if (!currentResourceById.getRootPath().equals(resourcePath)) { 2040 throw new CmsVfsResourceAlreadyExistsException( 2041 Messages.get().container( 2042 Messages.ERR_RESOURCE_WITH_ID_ALREADY_EXISTS_3, 2043 dbc.removeSiteRoot(resourcePath), 2044 dbc.removeSiteRoot(currentResourceById.getRootPath()), 2045 currentResourceById.getStructureId())); 2046 } 2047 } catch (CmsVfsResourceNotFoundException e) { 2048 // if the resource does exist, we have to check the id later to decide what to do 2049 } 2050 2051 // check the permissions 2052 if (currentResourceByName == null) { 2053 // resource does not exist - check parent folder 2054 m_securityManager.checkPermissions( 2055 dbc, 2056 parentFolder, 2057 CmsPermissionSet.ACCESS_WRITE, 2058 false, 2059 CmsResourceFilter.IGNORE_EXPIRATION); 2060 } else { 2061 // resource already exists - check existing resource 2062 m_securityManager.checkPermissions( 2063 dbc, 2064 currentResourceByName, 2065 CmsPermissionSet.ACCESS_WRITE, 2066 !importCase, 2067 CmsResourceFilter.ALL); 2068 } 2069 2070 // now look for the resource by name 2071 if (currentResourceByName != null) { 2072 boolean overwrite = true; 2073 if (currentResourceByName.getState().isDeleted()) { 2074 if (!currentResourceByName.isFolder()) { 2075 // if a non-folder resource was deleted it's treated like a new resource 2076 overwrite = false; 2077 } 2078 } else { 2079 if (!importCase) { 2080 // direct "overwrite" of a resource is possible only during import, 2081 // or if the resource has been deleted 2082 throw new CmsVfsResourceAlreadyExistsException( 2083 org.opencms.db.generic.Messages.get().container( 2084 org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1, 2085 dbc.removeSiteRoot(resource.getRootPath()))); 2086 } 2087 // the resource already exists 2088 if (!resource.isFolder() 2089 && useLostAndFound 2090 && (!currentResourceByName.getResourceId().equals(resource.getResourceId()))) { 2091 // semantic change: the current resource is moved to L&F and the imported resource will overwrite the old one 2092 // will leave the resource with state deleted, 2093 // but it does not matter, since the state will be set later again 2094 moveToLostAndFound(dbc, currentResourceByName, false); 2095 } 2096 } 2097 if (!overwrite) { 2098 // lock the resource, will throw an exception if not lockable 2099 lockResource(dbc, currentResourceByName, CmsLockType.EXCLUSIVE); 2100 2101 // trigger createResource instead of writeResource 2102 currentResourceByName = null; 2103 } 2104 } 2105 // if null, create new resource, if not null write resource 2106 CmsResource overwrittenResource = currentResourceByName; 2107 2108 // extract the name (without path) 2109 String targetName = CmsResource.getName(resourcePath); 2110 2111 int contentLength; 2112 2113 // modify target name and content length in case of folder creation 2114 if (resource.isFolder()) { 2115 // folders never have any content 2116 contentLength = -1; 2117 // must cut of trailing '/' for folder creation (or name check fails) 2118 if (CmsResource.isFolder(targetName)) { 2119 targetName = targetName.substring(0, targetName.length() - 1); 2120 } 2121 } else { 2122 // otherwise ensure content and content length are set correctly 2123 if (content != null) { 2124 // if a content is provided, in each case the length is the length of this content 2125 contentLength = content.length; 2126 } else if (overwrittenResource != null) { 2127 // we have no content, but an already existing resource - length remains unchanged 2128 contentLength = overwrittenResource.getLength(); 2129 } else { 2130 // we have no content - length is used as set in the resource 2131 contentLength = resource.getLength(); 2132 } 2133 } 2134 2135 // check if the target name is valid (forbidden chars etc.), 2136 // if not throw an exception 2137 // must do this here since targetName is modified in folder case (see above) 2138 CmsResource.checkResourceName(targetName); 2139 2140 // set structure and resource ids as given 2141 CmsUUID structureId = resource.getStructureId(); 2142 CmsUUID resourceId = resource.getResourceId(); 2143 2144 // decide which structure id to use 2145 if (overwrittenResource != null) { 2146 // resource exists, re-use existing ids 2147 structureId = overwrittenResource.getStructureId(); 2148 } 2149 if (structureId.isNullUUID()) { 2150 // need a new structure id 2151 structureId = new CmsUUID(); 2152 } 2153 2154 // decide which resource id to use 2155 if (overwrittenResource != null) { 2156 // if we are overwriting we have to assure the resource id is the same 2157 resourceId = overwrittenResource.getResourceId(); 2158 } 2159 if (resourceId.isNullUUID()) { 2160 // need a new resource id 2161 resourceId = new CmsUUID(); 2162 } 2163 2164 try { 2165 // check online resource 2166 CmsResource onlineResource = getVfsDriver( 2167 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resourcePath, true); 2168 // only allow to overwrite with different id if importing (createResource will set the right id) 2169 try { 2170 CmsResource offlineResource = getVfsDriver(dbc).readResource( 2171 dbc, 2172 dbc.currentProject().getUuid(), 2173 onlineResource.getStructureId(), 2174 true); 2175 if (!offlineResource.getRootPath().equals(onlineResource.getRootPath())) { 2176 throw new CmsVfsOnlineResourceAlreadyExistsException( 2177 Messages.get().container( 2178 Messages.ERR_ONLINE_RESOURCE_EXISTS_2, 2179 dbc.removeSiteRoot(resourcePath), 2180 dbc.removeSiteRoot(offlineResource.getRootPath()))); 2181 } 2182 } catch (CmsVfsResourceNotFoundException e) { 2183 // there is no problem for now 2184 // but should never happen 2185 if (LOG.isErrorEnabled()) { 2186 LOG.error(e.getLocalizedMessage(), e); 2187 } 2188 } 2189 } catch (CmsVfsResourceNotFoundException e) { 2190 // ok, there is no online entry to worry about 2191 } 2192 2193 // now create a resource object with all informations 2194 newResource = new CmsResource( 2195 structureId, 2196 resourceId, 2197 resourcePath, 2198 resource.getTypeId(), 2199 resource.isFolder(), 2200 resource.getFlags(), 2201 dbc.currentProject().getUuid(), 2202 resource.getState(), 2203 resource.getDateCreated(), 2204 resource.getUserCreated(), 2205 resource.getDateLastModified(), 2206 resource.getUserLastModified(), 2207 resource.getDateReleased(), 2208 resource.getDateExpired(), 2209 1, 2210 contentLength, 2211 resource.getDateContent(), 2212 resource.getVersion()); // version number does not matter since it will be computed later 2213 2214 // ensure date is updated only if required 2215 if (resource.isTouched()) { 2216 // this will trigger the internal "is touched" state on the new resource 2217 newResource.setDateLastModified(resource.getDateLastModified()); 2218 } 2219 2220 if (resource.isFile()) { 2221 // check if a sibling to the imported resource lies in a marked site 2222 if (labelResource(dbc, resource, resourcePath, 2)) { 2223 int flags = resource.getFlags(); 2224 flags |= CmsResource.FLAG_LABELED; 2225 resource.setFlags(flags); 2226 } 2227 // ensure siblings don't overwrite existing resource records 2228 if (content == null) { 2229 newResource.setState(CmsResource.STATE_KEEP); 2230 } 2231 } 2232 2233 // delete all relations for the resource, before writing the content 2234 getVfsDriver( 2235 dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), newResource, CmsRelationFilter.TARGETS); 2236 if (overwrittenResource == null) { 2237 CmsLock lock = getLock(dbc, newResource); 2238 if (lock.getEditionLock().isExclusive()) { 2239 unlockResource(dbc, newResource, true, false); 2240 } 2241 // resource does not exist. 2242 newResource = getVfsDriver( 2243 dbc).createResource(dbc, dbc.currentProject().getUuid(), newResource, content); 2244 } else { 2245 // resource already exists. 2246 // probably the resource is a merged page file that gets overwritten during import, or it gets 2247 // overwritten by a copy operation. if so, the structure & resource state are not modified to changed. 2248 int updateStates = (overwrittenResource.getState().isNew() 2249 ? CmsDriverManager.NOTHING_CHANGED 2250 : CmsDriverManager.UPDATE_ALL); 2251 getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), newResource, updateStates); 2252 2253 if ((content != null) && resource.isFile()) { 2254 // also update file content if required 2255 getVfsDriver(dbc).writeContent(dbc, newResource, content); 2256 } 2257 } 2258 2259 // write the properties (internal operation, no events or duplicate permission checks) 2260 writePropertyObjects(dbc, newResource, properties, false); 2261 2262 // lock the created resource 2263 try { 2264 // if it is locked by another user (copied or moved resource) this lock should be preserved and 2265 // the exception is OK: locks on created resources are a slave feature to original locks 2266 lockResource(dbc, newResource, CmsLockType.EXCLUSIVE); 2267 } catch (CmsLockException cle) { 2268 if (LOG.isDebugEnabled()) { 2269 LOG.debug( 2270 Messages.get().getBundle().key( 2271 Messages.ERR_CREATE_RESOURCE_LOCK_1, 2272 new Object[] {dbc.removeSiteRoot(newResource.getRootPath())})); 2273 } 2274 } 2275 2276 if (!importCase) { 2277 log( 2278 dbc, 2279 new CmsLogEntry( 2280 dbc, 2281 newResource.getStructureId(), 2282 CmsLogEntryType.RESOURCE_CREATED, 2283 new String[] {resource.getRootPath()}), 2284 false); 2285 } else { 2286 log( 2287 dbc, 2288 new CmsLogEntry( 2289 dbc, 2290 newResource.getStructureId(), 2291 CmsLogEntryType.RESOURCE_IMPORTED, 2292 new String[] {resource.getRootPath()}), 2293 false); 2294 } 2295 } 2296 } finally { 2297 // clear the internal caches 2298 m_monitor.clearAccessControlListCache(); 2299 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2300 2301 if (newResource != null) { 2302 // fire an event that a new resource has been created 2303 OpenCms.fireCmsEvent( 2304 new CmsEvent( 2305 I_CmsEventListener.EVENT_RESOURCE_CREATED, 2306 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, newResource))); 2307 } 2308 } 2309 return newResource; 2310 } 2311 2312 /** 2313 * Creates a new resource of the given resource type 2314 * with the provided content and properties.<p> 2315 * 2316 * If the provided content is null and the resource is not a folder, 2317 * the content will be set to an empty byte array.<p> 2318 * 2319 * @param dbc the current database context 2320 * @param resourcename the name of the resource to create (full path) 2321 * @param type the type of the resource to create 2322 * @param content the content for the new resource 2323 * @param properties the properties for the new resource 2324 * 2325 * @return the created resource 2326 * 2327 * @throws CmsException if something goes wrong 2328 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 2329 * 2330 * @see CmsObject#createResource(String, int, byte[], List) 2331 * @see CmsObject#createResource(String, int) 2332 * @see I_CmsResourceType#createResource(CmsObject, CmsSecurityManager, String, byte[], List) 2333 */ 2334 @SuppressWarnings("javadoc") 2335 public CmsResource createResource( 2336 CmsDbContext dbc, 2337 String resourcename, 2338 int type, 2339 byte[] content, 2340 List<CmsProperty> properties) 2341 throws CmsException, CmsIllegalArgumentException { 2342 2343 String targetName = resourcename; 2344 2345 if (content == null) { 2346 // name based resource creation MUST have a content 2347 content = new byte[0]; 2348 } 2349 int size; 2350 2351 if (CmsFolder.isFolderType(type)) { 2352 // must cut of trailing '/' for folder creation 2353 if (CmsResource.isFolder(targetName)) { 2354 targetName = targetName.substring(0, targetName.length() - 1); 2355 } 2356 size = -1; 2357 } else { 2358 size = content.length; 2359 } 2360 2361 // create a new resource 2362 CmsResource newResource = new CmsResource( 2363 CmsUUID.getNullUUID(), // uuids will be "corrected" later 2364 CmsUUID.getNullUUID(), 2365 targetName, 2366 type, 2367 CmsFolder.isFolderType(type), 2368 0, 2369 dbc.currentProject().getUuid(), 2370 CmsResource.STATE_NEW, 2371 0, 2372 dbc.currentUser().getId(), 2373 0, 2374 dbc.currentUser().getId(), 2375 CmsResource.DATE_RELEASED_DEFAULT, 2376 CmsResource.DATE_EXPIRED_DEFAULT, 2377 1, 2378 size, 2379 0, // version number does not matter since it will be computed later 2380 0); // content time will be corrected later 2381 2382 return createResource(dbc, targetName, newResource, content, properties, false); 2383 } 2384 2385 /** 2386 * Creates a new sibling of the source resource.<p> 2387 * 2388 * @param dbc the current database context 2389 * @param source the resource to create a sibling for 2390 * @param destination the name of the sibling to create with complete path 2391 * @param properties the individual properties for the new sibling 2392 * 2393 * @return the new created sibling 2394 * 2395 * @throws CmsException if something goes wrong 2396 * 2397 * @see CmsObject#createSibling(String, String, List) 2398 * @see I_CmsResourceType#createSibling(CmsObject, CmsSecurityManager, CmsResource, String, List) 2399 */ 2400 public CmsResource createSibling( 2401 CmsDbContext dbc, 2402 CmsResource source, 2403 String destination, 2404 List<CmsProperty> properties) 2405 throws CmsException { 2406 2407 if (source.isFolder()) { 2408 throw new CmsVfsException(Messages.get().container(Messages.ERR_VFS_FOLDERS_DONT_SUPPORT_SIBLINGS_0)); 2409 } 2410 2411 // determine destination folder and resource name 2412 String destinationFoldername = CmsResource.getParentFolder(destination); 2413 2414 // read the destination folder (will also check read permissions) 2415 CmsFolder destinationFolder = readFolder(dbc, destinationFoldername, CmsResourceFilter.IGNORE_EXPIRATION); 2416 2417 // no further permission check required here, will be done in createResource() 2418 2419 // check the resource flags 2420 int flags = source.getFlags(); 2421 if (labelResource(dbc, source, destination, 1)) { 2422 // set "labeled" link flag for new resource 2423 flags |= CmsResource.FLAG_LABELED; 2424 } 2425 2426 // create the new resource 2427 CmsResource newResource = new CmsResource( 2428 new CmsUUID(), 2429 source.getResourceId(), 2430 destination, 2431 source.getTypeId(), 2432 source.isFolder(), 2433 flags, 2434 dbc.currentProject().getUuid(), 2435 CmsResource.STATE_KEEP, 2436 source.getDateCreated(), // ensures current resource record remains untouched 2437 source.getUserCreated(), 2438 source.getDateLastModified(), 2439 source.getUserLastModified(), 2440 source.getDateReleased(), 2441 source.getDateExpired(), 2442 source.getSiblingCount() + 1, 2443 source.getLength(), 2444 source.getDateContent(), 2445 source.getVersion()); // version number does not matter since it will be computed later 2446 2447 // trigger "is touched" state on resource (will ensure modification date is kept unchanged) 2448 newResource.setDateLastModified(newResource.getDateLastModified()); 2449 2450 log( 2451 dbc, 2452 new CmsLogEntry( 2453 dbc, 2454 newResource.getStructureId(), 2455 CmsLogEntryType.RESOURCE_CLONED, 2456 new String[] {newResource.getRootPath()}), 2457 false); 2458 // create the resource (null content signals creation of sibling) 2459 newResource = createResource(dbc, destination, newResource, null, properties, false); 2460 2461 // copy relations 2462 copyRelations(dbc, source, newResource); 2463 2464 // clear the caches 2465 m_monitor.clearAccessControlListCache(); 2466 2467 List<CmsResource> modifiedResources = new ArrayList<CmsResource>(); 2468 modifiedResources.add(source); 2469 modifiedResources.add(newResource); 2470 modifiedResources.add(destinationFolder); 2471 Map<String, Object> eventData = new HashMap<>(); 2472 eventData.put(I_CmsEventListener.KEY_RESOURCES, modifiedResources); 2473 eventData.put(I_CmsEventListener.KEY_CHANGE, I_CmsEventListener.VALUE_CREATE_SIBLING); 2474 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, eventData)); 2475 2476 return newResource; 2477 } 2478 2479 /** 2480 * Creates the project for the temporary workplace files.<p> 2481 * 2482 * @param dbc the current database context 2483 * 2484 * @return the created project for the temporary workplace files 2485 * 2486 * @throws CmsException if something goes wrong 2487 */ 2488 public CmsProject createTempfileProject(CmsDbContext dbc) throws CmsException { 2489 2490 // read the needed groups from the cms 2491 CmsGroup projectUserGroup = readGroup(dbc, dbc.currentProject().getGroupId()); 2492 CmsGroup projectManagerGroup = readGroup(dbc, dbc.currentProject().getManagerGroupId()); 2493 2494 CmsProject tempProject = getProjectDriver(dbc).createProject( 2495 dbc, 2496 new CmsUUID(), 2497 dbc.currentUser(), 2498 projectUserGroup, 2499 projectManagerGroup, 2500 I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME, 2501 Messages.get().getBundle(dbc.getRequestContext().getLocale()).key( 2502 Messages.GUI_WORKPLACE_TEMPFILE_PROJECT_DESC_0), 2503 CmsProject.PROJECT_FLAG_HIDDEN, 2504 CmsProject.PROJECT_TYPE_NORMAL); 2505 getProjectDriver(dbc).createProjectResource(dbc, tempProject.getUuid(), "/"); 2506 2507 OpenCms.fireCmsEvent( 2508 new CmsEvent( 2509 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 2510 Collections.<String, Object> singletonMap("project", tempProject))); 2511 2512 return tempProject; 2513 } 2514 2515 /** 2516 * Creates a new user.<p> 2517 * 2518 * @param dbc the current database context 2519 * @param name the name for the new user 2520 * @param password the password for the new user 2521 * @param description the description for the new user 2522 * @param additionalInfos the additional infos for the user 2523 * 2524 * @return the created user 2525 * 2526 * @see CmsObject#createUser(String, String, String, Map) 2527 * 2528 * @throws CmsException if something goes wrong 2529 * @throws CmsIllegalArgumentException if the name for the user is not valid 2530 */ 2531 public CmsUser createUser( 2532 CmsDbContext dbc, 2533 String name, 2534 String password, 2535 String description, 2536 Map<String, Object> additionalInfos) 2537 throws CmsException, CmsIllegalArgumentException { 2538 2539 // no space before or after the name 2540 name = name.trim(); 2541 // check the user name 2542 String userName = CmsOrganizationalUnit.getSimpleName(name); 2543 OpenCms.getValidationHandler().checkUserName(userName); 2544 if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) { 2545 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName)); 2546 } 2547 // check the ou 2548 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 2549 // check the password 2550 validatePassword(password); 2551 2552 Map<String, Object> info = new HashMap<String, Object>(); 2553 if (additionalInfos != null) { 2554 info.putAll(additionalInfos); 2555 } 2556 if (description != null) { 2557 info.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description); 2558 } 2559 int flags = 0; 2560 if (ou.hasFlagWebuser()) { 2561 flags += I_CmsPrincipal.FLAG_USER_WEBUSER; 2562 } 2563 CmsUser user = getUserDriver(dbc).createUser( 2564 dbc, 2565 new CmsUUID(), 2566 name, 2567 OpenCms.getPasswordHandler().digest(password), 2568 " ", 2569 " ", 2570 " ", 2571 0, 2572 I_CmsPrincipal.FLAG_ENABLED + flags, 2573 0, 2574 info); 2575 2576 if (!dbc.getProjectId().isNullUUID()) { 2577 // user modified event is not needed 2578 return user; 2579 } 2580 // fire user modified event 2581 Map<String, Object> eventData = new HashMap<String, Object>(); 2582 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 2583 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_CREATE_USER); 2584 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 2585 return user; 2586 } 2587 2588 /** 2589 * Deletes aliases indicated by a filter.<p> 2590 * 2591 * @param dbc the current database context 2592 * @param project the current project 2593 * @param filter the filter which describes which aliases to delete 2594 * 2595 * @throws CmsException if something goes wrong 2596 */ 2597 public void deleteAliases(CmsDbContext dbc, CmsProject project, CmsAliasFilter filter) throws CmsException { 2598 2599 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 2600 vfsDriver.deleteAliases(dbc, project, filter); 2601 } 2602 2603 /** 2604 * Deletes all property values of a file or folder.<p> 2605 * 2606 * If there are no other siblings than the specified resource, 2607 * both the structure and resource property values get deleted. 2608 * If the specified resource has siblings, only the structure 2609 * property values get deleted.<p> 2610 * 2611 * @param dbc the current database context 2612 * @param resourcename the name of the resource for which all properties should be deleted 2613 * 2614 * @throws CmsException if operation was not successful 2615 */ 2616 public void deleteAllProperties(CmsDbContext dbc, String resourcename) throws CmsException { 2617 2618 CmsResource resource = null; 2619 List<CmsResource> resources = new ArrayList<CmsResource>(); 2620 2621 try { 2622 // read the resource 2623 resource = readResource(dbc, resourcename, CmsResourceFilter.IGNORE_EXPIRATION); 2624 2625 // check the security 2626 m_securityManager.checkPermissions( 2627 dbc, 2628 resource, 2629 CmsPermissionSet.ACCESS_WRITE, 2630 false, 2631 CmsResourceFilter.ALL); 2632 2633 // delete the property values 2634 if (resource.getSiblingCount() > 1) { 2635 // the resource has siblings- delete only the (structure) properties of this sibling 2636 getVfsDriver(dbc).deletePropertyObjects( 2637 dbc, 2638 dbc.currentProject().getUuid(), 2639 resource, 2640 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES); 2641 resources.addAll(readSiblings(dbc, resource, CmsResourceFilter.ALL)); 2642 2643 } else { 2644 // the resource has no other siblings- delete all (structure+resource) properties 2645 getVfsDriver(dbc).deletePropertyObjects( 2646 dbc, 2647 dbc.currentProject().getUuid(), 2648 resource, 2649 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 2650 resources.add(resource); 2651 } 2652 } finally { 2653 // clear the driver manager cache 2654 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2655 2656 // fire an event that all properties of a resource have been deleted 2657 OpenCms.fireCmsEvent( 2658 new CmsEvent( 2659 I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, 2660 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources))); 2661 } 2662 } 2663 2664 /** 2665 * Deletes all entries in the published resource table.<p> 2666 * 2667 * @param dbc the current database context 2668 * @param linkType the type of resource deleted (0= non-paramter, 1=parameter) 2669 * 2670 * @throws CmsException if something goes wrong 2671 */ 2672 public void deleteAllStaticExportPublishedResources(CmsDbContext dbc, int linkType) throws CmsException { 2673 2674 getProjectDriver(dbc).deleteAllStaticExportPublishedResources(dbc, linkType); 2675 } 2676 2677 /** 2678 * Deletes a group, where all permissions, users and children of the group 2679 * are transfered to a replacement group.<p> 2680 * 2681 * @param dbc the current request context 2682 * @param group the id of the group to be deleted 2683 * @param replacementId the id of the group to be transfered, can be <code>null</code> 2684 * 2685 * @throws CmsException if operation was not successful 2686 * @throws CmsDataAccessException if group to be deleted contains user 2687 */ 2688 public void deleteGroup(CmsDbContext dbc, CmsGroup group, CmsUUID replacementId) 2689 throws CmsDataAccessException, CmsException { 2690 2691 CmsGroup replacementGroup = null; 2692 if (replacementId != null) { 2693 replacementGroup = readGroup(dbc, replacementId); 2694 } 2695 // get all child groups of the group 2696 List<CmsGroup> children = getChildren(dbc, group, false); 2697 // get all users in this group 2698 List<CmsUser> users = getUsersOfGroup(dbc, group.getName(), true, true, group.isRole()); 2699 // get online project 2700 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 2701 if (replacementGroup == null) { 2702 // remove users 2703 Iterator<CmsUser> itUsers = users.iterator(); 2704 while (itUsers.hasNext()) { 2705 CmsUser user = itUsers.next(); 2706 if (userInGroup(dbc, user.getName(), group.getName(), group.isRole())) { 2707 removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole()); 2708 } 2709 } 2710 // transfer children to grandfather if possible 2711 CmsUUID parentId = group.getParentId(); 2712 if (parentId == null) { 2713 parentId = CmsUUID.getNullUUID(); 2714 } 2715 Iterator<CmsGroup> itChildren = children.iterator(); 2716 while (itChildren.hasNext()) { 2717 CmsGroup child = itChildren.next(); 2718 child.setParentId(parentId); 2719 writeGroup(dbc, child); 2720 } 2721 } else { 2722 // move children 2723 Iterator<CmsGroup> itChildren = children.iterator(); 2724 while (itChildren.hasNext()) { 2725 CmsGroup child = itChildren.next(); 2726 child.setParentId(replacementId); 2727 writeGroup(dbc, child); 2728 } 2729 // move users 2730 Iterator<CmsUser> itUsers = users.iterator(); 2731 while (itUsers.hasNext()) { 2732 CmsUser user = itUsers.next(); 2733 addUserToGroup(dbc, user.getName(), replacementGroup.getName(), group.isRole()); 2734 removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole()); 2735 } 2736 // transfer for offline 2737 transferPrincipalResources(dbc, dbc.currentProject(), group.getId(), replacementId, true); 2738 // transfer for online 2739 transferPrincipalResources(dbc, onlineProject, group.getId(), replacementId, true); 2740 } 2741 // remove the group 2742 getUserDriver( 2743 dbc).removeAccessControlEntriesForPrincipal(dbc, dbc.currentProject(), onlineProject, group.getId()); 2744 getUserDriver(dbc).deleteGroup(dbc, group.getName()); 2745 // backup the group 2746 getHistoryDriver(dbc).writePrincipal(dbc, group); 2747 if (OpenCms.getSubscriptionManager().isEnabled()) { 2748 // delete all subscribed resources for group 2749 unsubscribeAllResourcesFor(dbc, OpenCms.getSubscriptionManager().getPoolName(), group); 2750 } 2751 2752 // clear the relevant caches 2753 m_monitor.uncacheGroup(group); 2754 m_monitor.flushCache( 2755 CmsMemoryMonitor.CacheType.USERGROUPS, 2756 CmsMemoryMonitor.CacheType.USER_LIST, 2757 CmsMemoryMonitor.CacheType.ACL); 2758 2759 if (!dbc.getProjectId().isNullUUID()) { 2760 // group modified event is not needed 2761 return; 2762 } 2763 // fire group modified event 2764 Map<String, Object> eventData = new HashMap<String, Object>(); 2765 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 2766 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 2767 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_DELETE); 2768 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 2769 } 2770 2771 /** 2772 * Deletes the versions from the history tables, keeping the given number of versions per resource.<p> 2773 * 2774 * if the <code>cleanUp</code> option is set, additionally versions of deleted resources will be removed.<p> 2775 * 2776 * @param dbc the current database context 2777 * @param versionsToKeep number of versions to keep, is ignored if negative 2778 * @param versionsDeleted number of versions to keep for deleted resources, is ignored if negative 2779 * @param timeDeleted deleted resources older than this will also be deleted, is ignored if negative 2780 * @param clearDeletedFilter filter to evaluate whether the deleted resources should be cleared 2781 * @param report the report for output logging 2782 * 2783 * @throws CmsException if operation was not successful 2784 */ 2785 public void deleteHistoricalVersions( 2786 CmsDbContext dbc, 2787 int versionsToKeep, 2788 int versionsDeleted, 2789 long timeDeleted, 2790 Predicate<I_CmsHistoryResource> clearDeletedFilter, 2791 I_CmsReport report) 2792 throws CmsException { 2793 2794 if (clearDeletedFilter == null) { 2795 clearDeletedFilter = res -> true; 2796 } 2797 2798 report.println(Messages.get().container(Messages.RPT_START_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE); 2799 if (versionsToKeep >= 0) { 2800 report.println( 2801 Messages.get().container(Messages.RPT_START_DELETE_ACT_VERSIONS_1, Integer.valueOf(versionsToKeep)), 2802 I_CmsReport.FORMAT_HEADLINE); 2803 2804 List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllNotDeletedEntries(dbc); 2805 if (resources.isEmpty()) { 2806 report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK); 2807 } 2808 int n = resources.size(); 2809 int m = 1; 2810 Iterator<I_CmsHistoryResource> itResources = resources.iterator(); 2811 while (itResources.hasNext()) { 2812 I_CmsHistoryResource histResource = itResources.next(); 2813 2814 report.print( 2815 org.opencms.report.Messages.get().container( 2816 org.opencms.report.Messages.RPT_SUCCESSION_2, 2817 String.valueOf(m), 2818 String.valueOf(n)), 2819 I_CmsReport.FORMAT_NOTE); 2820 report.print( 2821 org.opencms.report.Messages.get().container( 2822 org.opencms.report.Messages.RPT_ARGUMENT_1, 2823 dbc.removeSiteRoot(histResource.getRootPath()))); 2824 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2825 2826 try { 2827 int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsToKeep, -1); 2828 2829 report.print( 2830 Messages.get().container(Messages.RPT_VERSION_DELETING_1, Integer.valueOf(deleted)), 2831 I_CmsReport.FORMAT_NOTE); 2832 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2833 report.println( 2834 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 2835 I_CmsReport.FORMAT_OK); 2836 } catch (CmsDataAccessException e) { 2837 report.println( 2838 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0), 2839 I_CmsReport.FORMAT_ERROR); 2840 2841 if (LOG.isDebugEnabled()) { 2842 LOG.debug(e.getLocalizedMessage(), e); 2843 } 2844 } 2845 2846 m++; 2847 } 2848 2849 report.println( 2850 Messages.get().container(Messages.RPT_END_DELETE_ACT_VERSIONS_0), 2851 I_CmsReport.FORMAT_HEADLINE); 2852 } 2853 if ((versionsDeleted >= 0) || (timeDeleted >= 0)) { 2854 if (timeDeleted >= 0) { 2855 report.println( 2856 Messages.get().container( 2857 Messages.RPT_START_DELETE_DEL_VERSIONS_2, 2858 Integer.valueOf(versionsDeleted), 2859 new Date(timeDeleted)), 2860 I_CmsReport.FORMAT_HEADLINE); 2861 } else { 2862 report.println( 2863 Messages.get().container( 2864 Messages.RPT_START_DELETE_DEL_VERSIONS_1, 2865 Integer.valueOf(versionsDeleted)), 2866 I_CmsReport.FORMAT_HEADLINE); 2867 } 2868 List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllDeletedEntries(dbc); 2869 resources = resources.stream().filter(clearDeletedFilter).collect(Collectors.toList()); 2870 if (resources.isEmpty()) { 2871 report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK); 2872 } 2873 int n = resources.size(); 2874 int m = 1; 2875 Iterator<I_CmsHistoryResource> itResources = resources.iterator(); 2876 while (itResources.hasNext()) { 2877 I_CmsHistoryResource histResource = itResources.next(); 2878 2879 report.print( 2880 org.opencms.report.Messages.get().container( 2881 org.opencms.report.Messages.RPT_SUCCESSION_2, 2882 String.valueOf(m), 2883 String.valueOf(n)), 2884 I_CmsReport.FORMAT_NOTE); 2885 report.print( 2886 org.opencms.report.Messages.get().container( 2887 org.opencms.report.Messages.RPT_ARGUMENT_1, 2888 dbc.removeSiteRoot(histResource.getRootPath()))); 2889 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2890 2891 try { 2892 int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsDeleted, timeDeleted); 2893 2894 report.print( 2895 Messages.get().container(Messages.RPT_VERSION_DELETING_1, Integer.valueOf(deleted)), 2896 I_CmsReport.FORMAT_NOTE); 2897 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2898 report.println( 2899 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 2900 I_CmsReport.FORMAT_OK); 2901 } catch (CmsDataAccessException e) { 2902 report.println( 2903 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0), 2904 I_CmsReport.FORMAT_ERROR); 2905 2906 if (LOG.isDebugEnabled()) { 2907 LOG.debug(e.getLocalizedMessage(), e); 2908 } 2909 } 2910 2911 m++; 2912 } 2913 report.println( 2914 Messages.get().container(Messages.RPT_END_DELETE_DEL_VERSIONS_0), 2915 I_CmsReport.FORMAT_HEADLINE); 2916 } 2917 report.println(Messages.get().container(Messages.RPT_END_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE); 2918 } 2919 2920 /** 2921 * Deletes all log entries matching the given filter.<p> 2922 * 2923 * @param dbc the current db context 2924 * @param filter the filter to use for deletion 2925 * 2926 * @throws CmsException if something goes wrong 2927 * 2928 * @see CmsSecurityManager#deleteLogEntries(CmsRequestContext, CmsLogFilter) 2929 */ 2930 public void deleteLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException { 2931 2932 updateLog(dbc); 2933 m_projectDriver.deleteLog(dbc, filter); 2934 } 2935 2936 /** 2937 * Deletes an organizational unit.<p> 2938 * 2939 * Only organizational units that contain no suborganizational unit can be deleted.<p> 2940 * 2941 * The organizational unit can not be delete if it is used in the request context, 2942 * or if the current user belongs to it.<p> 2943 * 2944 * All users and groups in the given organizational unit will be deleted.<p> 2945 * 2946 * @param dbc the current db context 2947 * @param organizationalUnit the organizational unit to delete 2948 * 2949 * @throws CmsException if operation was not successful 2950 * 2951 * @see org.opencms.security.CmsOrgUnitManager#deleteOrganizationalUnit(CmsObject, String) 2952 */ 2953 public void deleteOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) 2954 throws CmsException { 2955 2956 // check organizational unit in context 2957 if (dbc.getRequestContext().getOuFqn().equals(organizationalUnit.getName())) { 2958 throw new CmsDbConsistencyException( 2959 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_IN_CONTEXT_1, organizationalUnit.getName())); 2960 } 2961 // check organizational unit for user 2962 if (dbc.currentUser().getOuFqn().equals(organizationalUnit.getName())) { 2963 throw new CmsDbConsistencyException( 2964 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_CURRENT_USER_1, organizationalUnit.getName())); 2965 } 2966 // check sub organizational units 2967 if (!getOrganizationalUnits(dbc, organizationalUnit, true).isEmpty()) { 2968 throw new CmsDbConsistencyException( 2969 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_SUB_ORGUNITS_1, organizationalUnit.getName())); 2970 } 2971 // check groups 2972 List<CmsGroup> groups = getGroups(dbc, organizationalUnit, true, false); 2973 Iterator<CmsGroup> itGroups = groups.iterator(); 2974 while (itGroups.hasNext()) { 2975 CmsGroup group = itGroups.next(); 2976 if (!OpenCms.getDefaultUsers().isDefaultGroup(group.getName())) { 2977 throw new CmsDbConsistencyException( 2978 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_GROUPS_1, organizationalUnit.getName())); 2979 } 2980 } 2981 // check users 2982 if (!getUsers(dbc, organizationalUnit, true).isEmpty()) { 2983 throw new CmsDbConsistencyException( 2984 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_USERS_1, organizationalUnit.getName())); 2985 } 2986 2987 // delete default groups if needed 2988 itGroups = groups.iterator(); 2989 while (itGroups.hasNext()) { 2990 CmsGroup group = itGroups.next(); 2991 deleteGroup(dbc, group, null); 2992 } 2993 2994 // delete projects 2995 Iterator<CmsProject> itProjects = getProjectDriver(dbc).readProjects( 2996 dbc, 2997 organizationalUnit.getName()).iterator(); 2998 while (itProjects.hasNext()) { 2999 CmsProject project = itProjects.next(); 3000 deleteProject(dbc, project, false); 3001 } 3002 3003 // delete roles 3004 Iterator<CmsGroup> itRoles = getGroups(dbc, organizationalUnit, true, true).iterator(); 3005 while (itRoles.hasNext()) { 3006 CmsGroup role = itRoles.next(); 3007 deleteGroup(dbc, role, null); 3008 } 3009 3010 // create a publish list for the 'virtual' publish event 3011 CmsResource resource = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT); 3012 CmsPublishList pl = new CmsPublishList(resource, false); 3013 pl.add(resource, false); 3014 3015 // remove the organizational unit itself 3016 getUserDriver(dbc).deleteOrganizationalUnit(dbc, organizationalUnit); 3017 3018 // write the publish history entry 3019 getProjectDriver(dbc).writePublishHistory( 3020 dbc, 3021 pl.getPublishHistoryId(), 3022 new CmsPublishedResource(resource, -1, CmsResourceState.STATE_DELETED)); 3023 3024 // flush relevant caches 3025 m_monitor.clearPrincipalsCache(); 3026 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 3027 3028 // fire the 'virtual' publish event 3029 Map<String, Object> eventData = new HashMap<String, Object>(); 3030 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 3031 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 3032 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 3033 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 3034 OpenCms.fireCmsEvent(afterPublishEvent); 3035 3036 m_lockManager.removeDeletedResource(dbc, resource.getRootPath()); 3037 3038 if (!dbc.getProjectId().isNullUUID()) { 3039 // OU modified event is not needed 3040 return; 3041 } 3042 // fire OU modified event 3043 Map<String, Object> event2Data = new HashMap<String, Object>(); 3044 event2Data.put(I_CmsEventListener.KEY_OU_NAME, organizationalUnit.getName()); 3045 event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_DELETE); 3046 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data)); 3047 3048 } 3049 3050 /** 3051 * Deletes a project.<p> 3052 * 3053 * Only the admin or the owner of the project can do this. 3054 * 3055 * @param dbc the current database context 3056 * @param deleteProject the project to be deleted 3057 * 3058 * @throws CmsException if something goes wrong 3059 */ 3060 public void deleteProject(CmsDbContext dbc, CmsProject deleteProject) throws CmsException { 3061 3062 deleteProject(dbc, deleteProject, true); 3063 } 3064 3065 /** 3066 * Deletes a project.<p> 3067 * 3068 * Only the admin or the owner of the project can do this. 3069 * 3070 * @param dbc the current database context 3071 * @param deleteProject the project to be deleted 3072 * @param resetResources if true, the resources of the project to delete will be reset to their online state, or deleted if they have no online state 3073 * 3074 * @throws CmsException if something goes wrong 3075 */ 3076 public void deleteProject(CmsDbContext dbc, CmsProject deleteProject, boolean resetResources) throws CmsException { 3077 3078 CmsUUID projectId = deleteProject.getUuid(); 3079 3080 if (resetResources) { 3081 // changed/new/deleted files in the specified project 3082 List<CmsResource> modifiedFiles = readChangedResourcesInsideProject(dbc, projectId, RCPRM_FILES_ONLY_MODE); 3083 // changed/new/deleted folders in the specified project 3084 List<CmsResource> modifiedFolders = readChangedResourcesInsideProject( 3085 dbc, 3086 projectId, 3087 RCPRM_FOLDERS_ONLY_MODE); 3088 resetResourcesInProject(dbc, projectId, modifiedFiles, modifiedFolders); 3089 } 3090 3091 // unlock all resources in the project 3092 m_lockManager.removeResourcesInProject(deleteProject.getUuid(), true); 3093 m_monitor.clearAccessControlListCache(); 3094 m_monitor.clearResourceCache(); 3095 3096 // set project to online project if current project is the one which will be deleted 3097 if (projectId.equals(dbc.currentProject().getUuid())) { 3098 dbc.getRequestContext().setCurrentProject(readProject(dbc, CmsProject.ONLINE_PROJECT_ID)); 3099 } 3100 3101 // delete the project itself 3102 getProjectDriver(dbc).deleteProject(dbc, deleteProject); 3103 m_monitor.uncacheProject(deleteProject); 3104 3105 // fire the corresponding event 3106 OpenCms.fireCmsEvent( 3107 new CmsEvent( 3108 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 3109 Collections.<String, Object> singletonMap("project", deleteProject))); 3110 3111 } 3112 3113 /** 3114 * Deletes a property definition.<p> 3115 * 3116 * @param dbc the current database context 3117 * @param name the name of the property definition to delete 3118 * 3119 * @throws CmsException if something goes wrong 3120 */ 3121 public void deletePropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 3122 3123 CmsPropertyDefinition propertyDefinition = null; 3124 3125 try { 3126 // first read and then delete the metadefinition. 3127 propertyDefinition = readPropertyDefinition(dbc, name); 3128 getVfsDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition); 3129 getHistoryDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition); 3130 } finally { 3131 3132 // fire an event that a property of a resource has been deleted 3133 OpenCms.fireCmsEvent( 3134 new CmsEvent( 3135 I_CmsEventListener.EVENT_PROPERTY_DEFINITION_MODIFIED, 3136 Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition))); 3137 } 3138 } 3139 3140 /** 3141 * Deletes a publish job identified by its history id.<p> 3142 * 3143 * @param dbc the current database context 3144 * @param publishHistoryId the history id identifying the publish job 3145 * 3146 * @throws CmsException if something goes wrong 3147 */ 3148 public void deletePublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 3149 3150 getProjectDriver(dbc).deletePublishJob(dbc, publishHistoryId); 3151 } 3152 3153 /** 3154 * Deletes the publish list assigned to a publish job.<p> 3155 * 3156 * @param dbc the current database context 3157 * @param publishHistoryId the history id identifying the publish job 3158 * @throws CmsException if something goes wrong 3159 */ 3160 public void deletePublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 3161 3162 getProjectDriver(dbc).deletePublishList(dbc, publishHistoryId); 3163 } 3164 3165 /** 3166 * Deletes all relations for the given resource matching the given filter.<p> 3167 * 3168 * @param dbc the current db context 3169 * @param resource the resource to delete the relations for 3170 * @param filter the filter to use for deletion 3171 * 3172 * @throws CmsException if something goes wrong 3173 * 3174 * @see CmsSecurityManager#deleteRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter) 3175 */ 3176 public void deleteRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter) 3177 throws CmsException { 3178 3179 if (filter.includesDefinedInContent()) { 3180 throw new CmsIllegalArgumentException( 3181 Messages.get().container( 3182 Messages.ERR_DELETE_RELATION_IN_CONTENT_2, 3183 dbc.removeSiteRoot(resource.getRootPath()), 3184 filter.getTypes())); 3185 } 3186 getVfsDriver(dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), resource, filter); 3187 setDateLastModified(dbc, resource, System.currentTimeMillis()); 3188 log( 3189 dbc, 3190 new CmsLogEntry( 3191 dbc, 3192 resource.getStructureId(), 3193 CmsLogEntryType.RESOURCE_REMOVE_RELATION, 3194 new String[] {resource.getRootPath(), filter.toString()}), 3195 false); 3196 } 3197 3198 /** 3199 * Deletes a resource.<p> 3200 * 3201 * The <code>siblingMode</code> parameter controls how to handle siblings 3202 * during the delete operation. 3203 * Possible values for this parameter are: 3204 * <ul> 3205 * <li><code>{@link CmsResource#DELETE_REMOVE_SIBLINGS}</code></li> 3206 * <li><code>{@link CmsResource#DELETE_PRESERVE_SIBLINGS}</code></li> 3207 * </ul><p> 3208 * 3209 * @param dbc the current database context 3210 * @param resource the name of the resource to delete (full path) 3211 * @param siblingMode indicates how to handle siblings of the deleted resource 3212 * 3213 * @throws CmsException if something goes wrong 3214 * 3215 * @see CmsObject#deleteResource(String, CmsResource.CmsResourceDeleteMode) 3216 * @see I_CmsResourceType#deleteResource(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode) 3217 */ 3218 public void deleteResource(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceDeleteMode siblingMode) 3219 throws CmsException { 3220 3221 // upgrade a potential inherited, non-shared lock into a common lock 3222 CmsLock currentLock = getLock(dbc, resource); 3223 if (currentLock.getEditionLock().isDirectlyInherited()) { 3224 // upgrade the lock status if required 3225 lockResource(dbc, resource, CmsLockType.EXCLUSIVE); 3226 } 3227 3228 // check if siblings of the resource exist and must be deleted as well 3229 if (resource.isFolder()) { 3230 // folder can have no siblings 3231 siblingMode = CmsResource.DELETE_PRESERVE_SIBLINGS; 3232 } 3233 3234 // if selected, add all siblings of this resource to the list of resources to be deleted 3235 boolean allSiblingsRemoved; 3236 List<CmsResource> resources; 3237 if (siblingMode == CmsResource.DELETE_REMOVE_SIBLINGS) { 3238 resources = new ArrayList<CmsResource>(readSiblings(dbc, resource, CmsResourceFilter.ALL)); 3239 allSiblingsRemoved = true; 3240 3241 // ensure that the resource requested to be deleted is the last resource that gets actually deleted 3242 // to keep the shared locks of the siblings while those get deleted. 3243 resources.remove(resource); 3244 resources.add(resource); 3245 } else { 3246 // only delete the resource, no siblings 3247 resources = Collections.singletonList(resource); 3248 allSiblingsRemoved = false; 3249 } 3250 3251 int size = resources.size(); 3252 // if we have only one resource no further check is required 3253 if (size > 1) { 3254 CmsMultiException me = new CmsMultiException(); 3255 // ensure that each sibling is unlocked or locked by the current user 3256 for (int i = 0; i < size; i++) { 3257 CmsResource currentResource = resources.get(i); 3258 currentLock = getLock(dbc, currentResource); 3259 if (!currentLock.getEditionLock().isUnlocked() && !currentLock.isOwnedBy(dbc.currentUser())) { 3260 // the resource is locked by a user different from the current user 3261 CmsRequestContext context = dbc.getRequestContext(); 3262 me.addException( 3263 new CmsLockException( 3264 org.opencms.lock.Messages.get().container( 3265 org.opencms.lock.Messages.ERR_SIBLING_LOCKED_2, 3266 context.getSitePath(currentResource), 3267 context.getSitePath(resource)))); 3268 } 3269 } 3270 if (!me.getExceptions().isEmpty()) { 3271 throw me; 3272 } 3273 } 3274 3275 boolean removeAce = true; 3276 3277 if (resource.isFolder()) { 3278 // check if the folder has any resources in it 3279 Iterator<CmsResource> childResources = getVfsDriver( 3280 dbc).readChildResources(dbc, dbc.currentProject(), resource, true, true).iterator(); 3281 3282 CmsUUID projectId = CmsProject.ONLINE_PROJECT_ID; 3283 if (dbc.currentProject().isOnlineProject()) { 3284 projectId = CmsUUID.getOpenCmsUUID(); // HACK: to get an offline project id 3285 } 3286 3287 // collect the names of the resources inside the folder, excluding the moved resources 3288 StringBuffer errorResNames = new StringBuffer(128); 3289 while (childResources.hasNext()) { 3290 CmsResource errorRes = childResources.next(); 3291 if (errorRes.getState().isDeleted()) { 3292 continue; 3293 } 3294 // if deleting offline, or not moved, or just renamed inside the deleted folder 3295 // so, it may remain some orphan online entries for moved resources 3296 // which will be fixed during the publishing of the moved resources 3297 boolean error = !dbc.currentProject().isOnlineProject(); 3298 if (!error) { 3299 try { 3300 String originalPath = getVfsDriver( 3301 dbc).readResource(dbc, projectId, errorRes.getRootPath(), true).getRootPath(); 3302 error = originalPath.equals(errorRes.getRootPath()) 3303 || originalPath.startsWith(resource.getRootPath()); 3304 } catch (CmsVfsResourceNotFoundException e) { 3305 // ignore 3306 } 3307 } 3308 if (error) { 3309 if (errorResNames.length() != 0) { 3310 errorResNames.append(", "); 3311 } 3312 errorResNames.append("[" + dbc.removeSiteRoot(errorRes.getRootPath()) + "]"); 3313 } 3314 } 3315 3316 // the current implementation only deletes empty folders 3317 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(errorResNames.toString())) { 3318 throw new CmsVfsException( 3319 org.opencms.db.generic.Messages.get().container( 3320 org.opencms.db.generic.Messages.ERR_DELETE_NONEMTY_FOLDER_2, 3321 dbc.removeSiteRoot(resource.getRootPath()), 3322 errorResNames.toString())); 3323 } 3324 } 3325 3326 // delete all collected resources 3327 for (int i = 0; i < size; i++) { 3328 CmsResource currentResource = resources.get(i); 3329 3330 // try to delete/remove the resource only if the user has write access to the resource 3331 // check permissions only for the sibling, the resource it self was already checked or 3332 // is to be removed without write permissions, ie. while deleting a folder 3333 if (!currentResource.equals(resource) 3334 && (I_CmsPermissionHandler.PERM_ALLOWED != m_securityManager.hasPermissions( 3335 dbc, 3336 currentResource, 3337 CmsPermissionSet.ACCESS_WRITE, 3338 LockCheck.yes, 3339 CmsResourceFilter.ALL))) { 3340 3341 // no write access to sibling - must keep ACE (see below) 3342 allSiblingsRemoved = false; 3343 } else { 3344 // write access to sibling granted 3345 boolean existsOnline = (getVfsDriver(dbc).validateStructureIdExists( 3346 dbc, 3347 CmsProject.ONLINE_PROJECT_ID, 3348 currentResource.getStructureId()) || !(currentResource.getState().equals(CmsResource.STATE_NEW))); 3349 if (!existsOnline) { 3350 // the resource does not exist online => remove the resource 3351 // this means the resource is "new" (blue) in the offline project 3352 3353 // delete all properties of this resource 3354 deleteAllProperties(dbc, currentResource.getRootPath()); 3355 3356 if (currentResource.isFolder()) { 3357 getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentResource); 3358 } else { 3359 // check labels 3360 if (currentResource.isLabeled() && !labelResource(dbc, currentResource, null, 2)) { 3361 // update the resource flags to "un label" the other siblings 3362 int flags = currentResource.getFlags(); 3363 flags &= ~CmsResource.FLAG_LABELED; 3364 currentResource.setFlags(flags); 3365 } 3366 getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentResource); 3367 } 3368 3369 // ensure an exclusive lock is removed in the lock manager for a deleted new resource, 3370 // otherwise it would "stick" in the lock manager, preventing other users from creating 3371 // a file with the same name (issue with temp files in editor) 3372 m_lockManager.removeDeletedResource(dbc, currentResource.getRootPath()); 3373 // delete relations 3374 getVfsDriver(dbc).deleteRelations( 3375 dbc, 3376 dbc.currentProject().getUuid(), 3377 currentResource, 3378 CmsRelationFilter.TARGETS); 3379 getVfsDriver(dbc).deleteUrlNameMappingEntries( 3380 dbc, 3381 false, 3382 CmsUrlNameMappingFilter.ALL.filterStructureId(currentResource.getStructureId())); 3383 getVfsDriver(dbc).deleteAliases( 3384 dbc, 3385 dbc.currentProject(), 3386 new CmsAliasFilter(null, null, currentResource.getStructureId())); 3387 log( 3388 dbc, 3389 new CmsLogEntry( 3390 dbc, 3391 currentResource.getStructureId(), 3392 CmsLogEntryType.RESOURCE_NEW_DELETED, 3393 new String[] {currentResource.getRootPath()}), 3394 true); 3395 } else { 3396 // the resource exists online => mark the resource as deleted 3397 // structure record is removed during next publish 3398 // if one (or more) siblings are not removed, the ACE can not be removed 3399 removeAce = false; 3400 // set resource state to deleted 3401 currentResource.setState(CmsResource.STATE_DELETED); 3402 getVfsDriver( 3403 dbc).writeResourceState(dbc, dbc.currentProject(), currentResource, UPDATE_STRUCTURE, false); 3404 3405 // update the project ID 3406 getVfsDriver(dbc).writeLastModifiedProjectId( 3407 dbc, 3408 dbc.currentProject(), 3409 dbc.currentProject().getUuid(), 3410 currentResource); 3411 // log it 3412 3413 log( 3414 dbc, 3415 new CmsLogEntry( 3416 dbc, 3417 currentResource.getStructureId(), 3418 CmsLogEntryType.RESOURCE_DELETED, 3419 new String[] {currentResource.getRootPath()}), 3420 true); 3421 } 3422 } 3423 } 3424 3425 if ((resource.getSiblingCount() <= 1) || allSiblingsRemoved) { 3426 if (removeAce) { 3427 // remove the access control entries 3428 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId()); 3429 } 3430 } 3431 3432 // flush all caches 3433 m_monitor.clearAccessControlListCache(); 3434 m_monitor.flushCache( 3435 CmsMemoryMonitor.CacheType.PROPERTY, 3436 CmsMemoryMonitor.CacheType.PROPERTY_LIST, 3437 CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 3438 3439 Map<String, Object> eventData = new HashMap<String, Object>(); 3440 eventData.put(I_CmsEventListener.KEY_RESOURCES, resources); 3441 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 3442 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_DELETED, eventData)); 3443 } 3444 3445 /** 3446 * Deletes an entry in the published resource table.<p> 3447 * 3448 * @param dbc the current database context 3449 * @param resourceName The name of the resource to be deleted in the static export 3450 * @param linkType the type of resource deleted (0= non-parameter, 1=parameter) 3451 * @param linkParameter the parameters of the resource 3452 * 3453 * @throws CmsException if something goes wrong 3454 */ 3455 public void deleteStaticExportPublishedResource( 3456 CmsDbContext dbc, 3457 String resourceName, 3458 int linkType, 3459 String linkParameter) 3460 throws CmsException { 3461 3462 getProjectDriver(dbc).deleteStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter); 3463 } 3464 3465 /** 3466 * Deletes a user, where all permissions and resources attributes of the user 3467 * were transfered to a replacement user, if given.<p> 3468 * 3469 * Only users, which are in the group "administrators" are granted.<p> 3470 * 3471 * @param dbc the current database context 3472 * @param project the current project 3473 * @param username the name of the user to be deleted 3474 * @param replacementUsername the name of the user to be transfered, can be <code>null</code> 3475 * 3476 * @throws CmsException if operation was not successful 3477 */ 3478 public void deleteUser(CmsDbContext dbc, CmsProject project, String username, String replacementUsername) 3479 throws CmsException { 3480 3481 // Test if the users exists 3482 CmsUser user = readUser(dbc, username); 3483 CmsUser replacementUser = null; 3484 if (replacementUsername != null) { 3485 replacementUser = readUser(dbc, replacementUsername); 3486 } 3487 3488 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 3489 boolean withACEs = true; 3490 if (replacementUser == null) { 3491 withACEs = false; 3492 replacementUser = readUser(dbc, OpenCms.getDefaultUsers().getUserDeletedResource()); 3493 } 3494 3495 boolean isVfsManager = m_securityManager.hasRole(dbc, replacementUser, CmsRole.VFS_MANAGER); 3496 3497 // iterate groups and roles 3498 for (int i = 0; i < 2; i++) { 3499 boolean readRoles = i != 0; 3500 Iterator<CmsGroup> itGroups = getGroupsOfUser( 3501 dbc, 3502 username, 3503 "", 3504 true, 3505 readRoles, 3506 true, 3507 dbc.getRequestContext().getRemoteAddress()).iterator(); 3508 while (itGroups.hasNext()) { 3509 CmsGroup group = itGroups.next(); 3510 if (!isVfsManager) { 3511 // add replacement user to user groups 3512 if (!userInGroup(dbc, replacementUser.getName(), group.getName(), readRoles)) { 3513 addUserToGroup(dbc, replacementUser.getName(), group.getName(), readRoles); 3514 } 3515 } 3516 // remove user from groups 3517 if (userInGroup(dbc, username, group.getName(), readRoles)) { 3518 // we need this additional check because removing a user from a group 3519 // may also automatically remove him from other groups if the group was 3520 // associated with a role. 3521 removeUserFromGroup(dbc, username, group.getName(), readRoles); 3522 } 3523 } 3524 } 3525 // remove all locks set for the deleted user 3526 m_lockManager.removeLocks(user.getId()); 3527 3528 boolean skipTransfer = false; 3529 try { 3530 3531 // Looking up the resources for the principal transfer is pretty expensive, 3532 // but for webuser OUs without associated OU resources, it should be unnecessary. 3533 // So we can save ourselves the work. 3534 // We cache this "skippable" status on a per-OU basis, so we don't need to a do it for each 3535 // user if we delete a lot of users in a row. 3536 // 3537 // Note: Even if there *were* resources which would be affected by transferPrincipalResources, 3538 // skipping the transfer doesn't do any harm - the UI just displays the ID instead. 3539 3540 skipTransfer = m_skipTransferPrincipalResourceCache.get(user.getOuFqn(), () -> { 3541 3542 String ouName = CmsOrganizationalUnit.removeLeadingSeparator(user.getOuFqn()); 3543 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouName); 3544 if (!ou.hasFlagWebuser()) { 3545 return false; 3546 } 3547 List<CmsResource> ouResources = getResourcesForOrganizationalUnit(dbc, ou); 3548 return ouResources.size() == 0; 3549 }); 3550 } catch (ExecutionException e) { 3551 LOG.error(e.getLocalizedMessage(), e); 3552 } 3553 if (skipTransfer) { 3554 LOG.info( 3555 "Skipping principal transfer while deleting user " 3556 + user.getName() 3557 + " (" 3558 + user.getId() 3559 + ") because it is part of a webuser OU with no OU resources."); 3560 } else { 3561 // offline 3562 if (dbc.getProjectId().isNullUUID()) { 3563 // offline project available 3564 transferPrincipalResources(dbc, project, user.getId(), replacementUser.getId(), withACEs); 3565 } 3566 // online 3567 transferPrincipalResources(dbc, onlineProject, user.getId(), replacementUser.getId(), withACEs); 3568 } 3569 getUserDriver(dbc).removeAccessControlEntriesForPrincipal(dbc, project, onlineProject, user.getId()); 3570 getHistoryDriver(dbc).writePrincipal(dbc, user); 3571 getUserDriver(dbc).deleteUser(dbc, username); 3572 // delete user from cache 3573 m_monitor.clearUserCache(user); 3574 3575 if (!dbc.getProjectId().isNullUUID()) { 3576 // user modified event is not needed 3577 return; 3578 } 3579 // fire user modified event 3580 Map<String, Object> eventData = new HashMap<String, Object>(); 3581 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 3582 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 3583 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_DELETE_USER); 3584 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 3585 } 3586 3587 /** 3588 * Destroys this driver manager and releases all allocated resources.<p> 3589 */ 3590 public void destroy() { 3591 3592 try { 3593 if (m_projectDriver != null) { 3594 try { 3595 m_projectDriver.destroy(); 3596 } catch (Throwable t) { 3597 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_PROJECT_DRIVER_0), t); 3598 } 3599 m_projectDriver = null; 3600 } 3601 if (m_userDriver != null) { 3602 try { 3603 m_userDriver.destroy(); 3604 } catch (Throwable t) { 3605 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_USER_DRIVER_0), t); 3606 } 3607 m_userDriver = null; 3608 } 3609 if (m_vfsDriver != null) { 3610 try { 3611 m_vfsDriver.destroy(); 3612 } catch (Throwable t) { 3613 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_VFS_DRIVER_0), t); 3614 } 3615 m_vfsDriver = null; 3616 } 3617 if (m_historyDriver != null) { 3618 try { 3619 m_historyDriver.destroy(); 3620 } catch (Throwable t) { 3621 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_HISTORY_DRIVER_0), t); 3622 } 3623 m_historyDriver = null; 3624 } 3625 3626 if (m_pools != null) { 3627 for (CmsDbPoolV11 pool : m_pools.values()) { 3628 try { 3629 pool.close(); 3630 if (CmsLog.INIT.isDebugEnabled()) { 3631 CmsLog.INIT.debug(Messages.get().getBundle().key(Messages.INIT_CLOSE_CONN_POOL_1, pool)); 3632 } 3633 3634 } catch (Throwable t) { 3635 LOG.error(Messages.get().getBundle().key(Messages.LOG_CLOSE_CONN_POOL_ERROR_1, pool), t); 3636 } 3637 } 3638 m_pools.clear(); 3639 } 3640 3641 m_monitor.clearCache(); 3642 3643 m_lockManager = null; 3644 m_htmlLinkValidator = null; 3645 } catch (Throwable t) { 3646 // ignore 3647 } 3648 if (CmsLog.INIT.isInfoEnabled()) { 3649 CmsLog.INIT.info( 3650 Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_DESTROY_1, getClass().getName())); 3651 } 3652 } 3653 3654 /** 3655 * Tests if a resource with the given resourceId does already exist in the Database.<p> 3656 * 3657 * @param dbc the current database context 3658 * @param resourceId the resource id to test for 3659 * @return true if a resource with the given id was found, false otherweise 3660 * @throws CmsException if something goes wrong 3661 */ 3662 public boolean existsResourceId(CmsDbContext dbc, CmsUUID resourceId) throws CmsException { 3663 3664 return getVfsDriver(dbc).validateResourceIdExists(dbc, dbc.currentProject().getUuid(), resourceId); 3665 } 3666 3667 /** 3668 * Fills the given publish list with the the VFS resources that actually get published.<p> 3669 * 3670 * Please refer to the source code of this method for the rules on how to decide whether a 3671 * new/changed/deleted <code>{@link CmsResource}</code> object can be published or not.<p> 3672 * 3673 * @param dbc the current database context 3674 * @param publishList must be initialized with basic publish information (Project or direct publish operation), 3675 * the given publish list will be filled with all new/changed/deleted files from the current 3676 * (offline) project that will be actually published 3677 * 3678 * @throws CmsException if something goes wrong 3679 * 3680 * @see org.opencms.db.CmsPublishList 3681 */ 3682 public void fillPublishList(CmsDbContext dbc, CmsPublishList publishList) throws CmsException { 3683 3684 if (!publishList.isDirectPublish()) { 3685 // when publishing a project 3686 // all modified resources with the last change done in the current project are candidates if unlocked 3687 List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree( 3688 dbc, 3689 dbc.currentProject().getUuid(), 3690 CmsDriverManager.READ_IGNORE_PARENT, 3691 CmsDriverManager.READ_IGNORE_TYPE, 3692 CmsResource.STATE_UNCHANGED, 3693 CmsDriverManager.READ_IGNORE_TIME, 3694 CmsDriverManager.READ_IGNORE_TIME, 3695 CmsDriverManager.READ_IGNORE_TIME, 3696 CmsDriverManager.READ_IGNORE_TIME, 3697 CmsDriverManager.READ_IGNORE_TIME, 3698 CmsDriverManager.READ_IGNORE_TIME, 3699 CmsDriverManager.READMODE_INCLUDE_TREE 3700 | CmsDriverManager.READMODE_INCLUDE_PROJECT 3701 | CmsDriverManager.READMODE_EXCLUDE_STATE 3702 | CmsDriverManager.READMODE_ONLY_FOLDERS); 3703 3704 List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree( 3705 dbc, 3706 dbc.currentProject().getUuid(), 3707 CmsDriverManager.READ_IGNORE_PARENT, 3708 CmsDriverManager.READ_IGNORE_TYPE, 3709 CmsResource.STATE_UNCHANGED, 3710 CmsDriverManager.READ_IGNORE_TIME, 3711 CmsDriverManager.READ_IGNORE_TIME, 3712 CmsDriverManager.READ_IGNORE_TIME, 3713 CmsDriverManager.READ_IGNORE_TIME, 3714 CmsDriverManager.READ_IGNORE_TIME, 3715 CmsDriverManager.READ_IGNORE_TIME, 3716 CmsDriverManager.READMODE_INCLUDE_TREE 3717 | CmsDriverManager.READMODE_INCLUDE_PROJECT 3718 | CmsDriverManager.READMODE_EXCLUDE_STATE 3719 | CmsDriverManager.READMODE_ONLY_FILES); 3720 CmsRequestContext context = dbc.getRequestContext(); 3721 if ((context != null) 3722 && (context.getAttribute(CmsDefaultWorkflowManager.ATTR_CHECK_PUBLISH_RESOURCE_LIMIT) != null)) { 3723 3724 // check if total size and if it exceeds the resource limit and the request 3725 // context attribute is set, throw an exception. 3726 // we do it here since filterResources() can be very expensive on large resource lists 3727 3728 int limit = OpenCms.getWorkflowManager().getResourceLimit(); 3729 int total = fileList.size() + folderList.size(); 3730 if (total > limit) { 3731 throw new CmsTooManyPublishResourcesException(total); 3732 } 3733 } 3734 publishList.addAll(filterResources(dbc, null, folderList), true); 3735 publishList.addAll(filterResources(dbc, publishList, fileList), true); 3736 } else { 3737 // this is a direct publish 3738 Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator(); 3739 while (it.hasNext()) { 3740 // iterate all resources in the direct publish list 3741 CmsResource directPublishResource = it.next(); 3742 if (directPublishResource.isFolder()) { 3743 // when publishing a folder directly, 3744 // the folder and all modified resources within the tree below this folder 3745 // and with the last change done in the current project are candidates if lockable 3746 CmsLock lock = getLock(dbc, directPublishResource); 3747 if (!directPublishResource.getState().isUnchanged() && lock.isLockableBy(dbc.currentUser())) { 3748 3749 try { 3750 m_securityManager.checkPermissions( 3751 dbc, 3752 directPublishResource, 3753 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 3754 false, 3755 CmsResourceFilter.ALL); 3756 publishList.add(directPublishResource, true); 3757 } catch (CmsException e) { 3758 // skip if not enough permissions 3759 } 3760 } 3761 boolean shouldPublishDeletedSubResources = publishList.isUserPublishList() 3762 && directPublishResource.getState().isDeleted(); 3763 if (publishList.isPublishSubResources() || shouldPublishDeletedSubResources) { 3764 addSubResources(dbc, publishList, directPublishResource, resource -> true); 3765 } 3766 } else if (directPublishResource.isFile() && !directPublishResource.getState().isUnchanged()) { 3767 3768 // when publishing a file directly this file is the only candidate 3769 // if it is modified and lockable 3770 CmsLock lock = getLock(dbc, directPublishResource); 3771 if (lock.isLockableBy(dbc.currentUser())) { 3772 // check permissions 3773 try { 3774 m_securityManager.checkPermissions( 3775 dbc, 3776 directPublishResource, 3777 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 3778 false, 3779 CmsResourceFilter.ALL); 3780 publishList.add(directPublishResource, true); 3781 } catch (CmsException e) { 3782 // skip if not enough permissions 3783 } 3784 } 3785 } 3786 } 3787 } 3788 3789 // Step 2: if desired, extend the list of files to publish with related siblings 3790 if (publishList.isPublishSiblings()) { 3791 List<CmsResource> publishFiles = publishList.getFileList(); 3792 int size = publishFiles.size(); 3793 3794 // Improved: first calculate closure of all siblings, then filter and add them 3795 Set<CmsResource> siblingsClosure = new HashSet<CmsResource>(publishFiles); 3796 for (int i = 0; i < size; i++) { 3797 CmsResource currentFile = publishFiles.get(i); 3798 if (currentFile.getSiblingCount() > 1) { 3799 siblingsClosure.addAll(readSiblings(dbc, currentFile, CmsResourceFilter.ALL_MODIFIED)); 3800 } 3801 } 3802 publishList.addAll(filterSiblings(dbc, publishList, siblingsClosure), true); 3803 } 3804 publishList.initialize(); 3805 } 3806 3807 /** 3808 * Returns the list of access control entries of a resource given its name.<p> 3809 * 3810 * @param dbc the current database context 3811 * @param resource the resource to read the access control entries for 3812 * @param getInherited true if the result should include all access control entries inherited by parent folders 3813 * 3814 * @return a list of <code>{@link CmsAccessControlEntry}</code> objects defining all permissions for the given resource 3815 * 3816 * @throws CmsException if something goes wrong 3817 */ 3818 public List<CmsAccessControlEntry> getAccessControlEntries( 3819 CmsDbContext dbc, 3820 CmsResource resource, 3821 boolean getInherited) 3822 throws CmsException { 3823 3824 // get the ACE of the resource itself 3825 I_CmsUserDriver userDriver = getUserDriver(dbc); 3826 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 3827 List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries( 3828 dbc, 3829 dbc.currentProject(), 3830 resource.getResourceId(), 3831 false); 3832 3833 // sort and check if we got the 'overwrite all' ace to stop looking up 3834 boolean overwriteAll = sortAceList(ace); 3835 3836 // get the ACE of each parent folder 3837 // Note: for the immediate parent, get non-inherited access control entries too, 3838 // if the resource is not a folder 3839 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 3840 int d = (resource.isFolder()) ? 1 : 0; 3841 3842 while (!overwriteAll && getInherited && (parentPath != null)) { 3843 resource = vfsDriver.readFolder(dbc, dbc.currentProject().getUuid(), parentPath); 3844 List<CmsAccessControlEntry> entries = userDriver.readAccessControlEntries( 3845 dbc, 3846 dbc.currentProject(), 3847 resource.getResourceId(), 3848 d > 0); 3849 3850 // sort and check if we got the 'overwrite all' ace to stop looking up 3851 overwriteAll = sortAceList(entries); 3852 3853 for (CmsAccessControlEntry e : entries) { 3854 e.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED); 3855 } 3856 3857 ace.addAll(entries); 3858 parentPath = CmsResource.getParentFolder(resource.getRootPath()); 3859 d++; 3860 } 3861 3862 return ace; 3863 } 3864 3865 /** 3866 * Returns the full access control list of a given resource.<p> 3867 * 3868 * @param dbc the current database context 3869 * @param resource the resource 3870 * 3871 * @return the access control list of the resource 3872 * 3873 * @throws CmsException if something goes wrong 3874 */ 3875 public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource) throws CmsException { 3876 3877 return getAccessControlList(dbc, resource, false); 3878 } 3879 3880 /** 3881 * Returns the access control list of a given resource.<p> 3882 * 3883 * If <code>inheritedOnly</code> is set, only inherited access control entries 3884 * are returned.<p> 3885 * 3886 * Note: For file resources, *all* permissions set at the immediate parent folder are inherited, 3887 * not only these marked to inherit. 3888 * 3889 * @param dbc the current database context 3890 * @param resource the resource 3891 * @param inheritedOnly skip non-inherited entries if set 3892 * 3893 * @return the access control list of the resource 3894 * 3895 * @throws CmsException if something goes wrong 3896 */ 3897 public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource, boolean inheritedOnly) 3898 throws CmsException { 3899 3900 return getAccessControlList(dbc, resource, inheritedOnly, resource.isFolder(), 0); 3901 } 3902 3903 /** 3904 * Returns the number of active connections managed by a pool.<p> 3905 * 3906 * @param dbPoolUrl the url of a pool 3907 * @return the number of active connections 3908 * @throws CmsDbException if something goes wrong 3909 */ 3910 public int getActiveConnections(String dbPoolUrl) throws CmsDbException { 3911 3912 CmsDbPoolV11 pool = m_pools.get(dbPoolUrl); 3913 if (pool == null) { 3914 CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl); 3915 throw new CmsDbException(message); 3916 } 3917 try { 3918 return pool.getActiveConnections(); 3919 } catch (Exception exc) { 3920 CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl); 3921 throw new CmsDbException(message, exc); 3922 } 3923 3924 } 3925 3926 /** 3927 * Reads all access control entries.<p> 3928 * 3929 * @param dbc the current database context 3930 * @return all access control entries for the current project (offline/online) 3931 * 3932 * @throws CmsException if something goes wrong 3933 */ 3934 public List<CmsAccessControlEntry> getAllAccessControlEntries(CmsDbContext dbc) throws CmsException { 3935 3936 I_CmsUserDriver userDriver = getUserDriver(dbc); 3937 List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries( 3938 dbc, 3939 dbc.currentProject(), 3940 CmsAccessControlEntry.PRINCIPAL_READALL_ID, 3941 false); 3942 return ace; 3943 } 3944 3945 /** 3946 * Returns all projects which are owned by the current user or which are 3947 * accessible by the current user.<p> 3948 * 3949 * @param dbc the current database context 3950 * @param orgUnit the organizational unit to search project in 3951 * @param includeSubOus if to include sub organizational units 3952 * 3953 * @return a list of objects of type <code>{@link CmsProject}</code> 3954 * 3955 * @throws CmsException if something goes wrong 3956 */ 3957 public List<CmsProject> getAllAccessibleProjects( 3958 CmsDbContext dbc, 3959 CmsOrganizationalUnit orgUnit, 3960 boolean includeSubOus) 3961 throws CmsException { 3962 3963 Set<CmsProject> projects = new HashSet<CmsProject>(); 3964 3965 // get the ous where the user has the project manager role 3966 List<CmsOrganizationalUnit> ous = getOrgUnitsForRole( 3967 dbc, 3968 CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()), 3969 includeSubOus); 3970 3971 // get the groups of the user if needed 3972 Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>(); 3973 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 3974 while (itGroups.hasNext()) { 3975 CmsGroup group = itGroups.next(); 3976 userGroupIds.add(group.getId()); 3977 } 3978 3979 // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus' 3980 // get all projects that might come in question 3981 projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName())); 3982 3983 // filter hidden and not accessible projects 3984 Iterator<CmsProject> itProjects = projects.iterator(); 3985 while (itProjects.hasNext()) { 3986 CmsProject project = itProjects.next(); 3987 boolean accessible = true; 3988 // if hidden 3989 accessible = accessible && !project.isHidden(); 3990 3991 if (!includeSubOus) { 3992 // if not exact in the given ou 3993 accessible = accessible && project.getOuFqn().equals(orgUnit.getName()); 3994 } else { 3995 // if not in the given ou 3996 accessible = accessible && project.getOuFqn().startsWith(orgUnit.getName()); 3997 } 3998 3999 if (!accessible) { 4000 itProjects.remove(); 4001 continue; 4002 } 4003 4004 accessible = false; 4005 // online project 4006 accessible = accessible || project.isOnlineProject(); 4007 // if owner 4008 accessible = accessible || project.getOwnerId().equals(dbc.currentUser().getId()); 4009 4010 // project managers 4011 Iterator<CmsOrganizationalUnit> itOus = ous.iterator(); 4012 while (!accessible && itOus.hasNext()) { 4013 CmsOrganizationalUnit ou = itOus.next(); 4014 // for project managers check visibility 4015 accessible = accessible || project.getOuFqn().startsWith(ou.getName()); 4016 } 4017 4018 if (!accessible) { 4019 // if direct user or manager of project 4020 CmsUUID groupId = null; 4021 if (userGroupIds.contains(project.getGroupId())) { 4022 groupId = project.getGroupId(); 4023 } else if (userGroupIds.contains(project.getManagerGroupId())) { 4024 groupId = project.getManagerGroupId(); 4025 } 4026 if (groupId != null) { 4027 String oufqn = readGroup(dbc, groupId).getOuFqn(); 4028 accessible = accessible || (oufqn.startsWith(dbc.getRequestContext().getOuFqn())); 4029 } 4030 } 4031 if (!accessible) { 4032 // remove not accessible project 4033 itProjects.remove(); 4034 } 4035 } 4036 4037 List<CmsProject> accessibleProjects = new ArrayList<CmsProject>(projects); 4038 // sort the list of projects based on the project name 4039 Collections.sort(accessibleProjects); 4040 // ensure the online project is in first place 4041 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 4042 if (accessibleProjects.contains(onlineProject)) { 4043 accessibleProjects.remove(onlineProject); 4044 } 4045 accessibleProjects.add(0, onlineProject); 4046 4047 return accessibleProjects; 4048 } 4049 4050 /** 4051 * Returns a list with all projects from history.<p> 4052 * 4053 * @param dbc the current database context 4054 * 4055 * @return list of <code>{@link CmsHistoryProject}</code> objects 4056 * with all projects from history. 4057 * 4058 * @throws CmsException if operation was not successful 4059 */ 4060 public List<CmsHistoryProject> getAllHistoricalProjects(CmsDbContext dbc) throws CmsException { 4061 4062 // user is allowed to access all existing projects for the ous he has the project_manager role 4063 Set<CmsOrganizationalUnit> manOus = new HashSet<CmsOrganizationalUnit>( 4064 getOrgUnitsForRole(dbc, CmsRole.PROJECT_MANAGER, true)); 4065 4066 List<CmsHistoryProject> projects = getHistoryDriver(dbc).readProjects(dbc); 4067 Iterator<CmsHistoryProject> itProjects = projects.iterator(); 4068 while (itProjects.hasNext()) { 4069 CmsHistoryProject project = itProjects.next(); 4070 if (project.isHidden()) { 4071 // project is hidden 4072 itProjects.remove(); 4073 continue; 4074 } 4075 if (!project.getOuFqn().startsWith(dbc.currentUser().getOuFqn())) { 4076 // project is not visible from the users ou 4077 itProjects.remove(); 4078 continue; 4079 } 4080 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, project.getOuFqn()); 4081 if (manOus.contains(ou)) { 4082 // user is project manager for this project 4083 continue; 4084 } else if (project.getOwnerId().equals(dbc.currentUser().getId())) { 4085 // user is owner of the project 4086 continue; 4087 } else { 4088 boolean found = false; 4089 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 4090 while (itGroups.hasNext()) { 4091 CmsGroup group = itGroups.next(); 4092 if (project.getManagerGroupId().equals(group.getId())) { 4093 found = true; 4094 break; 4095 } 4096 } 4097 if (found) { 4098 // user is member of the manager group of the project 4099 continue; 4100 } 4101 } 4102 itProjects.remove(); 4103 } 4104 return projects; 4105 } 4106 4107 /** 4108 * Returns all projects which are owned by the current user or which are manageable 4109 * for the group of the user.<p> 4110 * 4111 * @param dbc the current database context 4112 * @param orgUnit the organizational unit to search project in 4113 * @param includeSubOus if to include sub organizational units 4114 * 4115 * @return a list of objects of type <code>{@link CmsProject}</code> 4116 * 4117 * @throws CmsException if operation was not successful 4118 */ 4119 public List<CmsProject> getAllManageableProjects( 4120 CmsDbContext dbc, 4121 CmsOrganizationalUnit orgUnit, 4122 boolean includeSubOus) 4123 throws CmsException { 4124 4125 Set<CmsProject> projects = new HashSet<CmsProject>(); 4126 4127 // get the ous where the user has the project manager role 4128 List<CmsOrganizationalUnit> ous = getOrgUnitsForRole( 4129 dbc, 4130 CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()), 4131 includeSubOus); 4132 4133 // get the groups of the user if needed 4134 Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>(); 4135 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 4136 while (itGroups.hasNext()) { 4137 CmsGroup group = itGroups.next(); 4138 userGroupIds.add(group.getId()); 4139 } 4140 4141 // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus' 4142 // get all projects that might come in question 4143 projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName())); 4144 4145 // filter hidden and not manageable projects 4146 Iterator<CmsProject> itProjects = projects.iterator(); 4147 while (itProjects.hasNext()) { 4148 CmsProject project = itProjects.next(); 4149 boolean manageable = true; 4150 // if online 4151 manageable = manageable && !project.isOnlineProject(); 4152 // if hidden 4153 manageable = manageable && !project.isHidden(); 4154 4155 if (!includeSubOus) { 4156 // if not exact in the given ou 4157 manageable = manageable && project.getOuFqn().equals(orgUnit.getName()); 4158 } else { 4159 // if not in the given ou 4160 manageable = manageable && project.getOuFqn().startsWith(orgUnit.getName()); 4161 } 4162 4163 if (!manageable) { 4164 itProjects.remove(); 4165 continue; 4166 } 4167 4168 manageable = false; 4169 // if owner 4170 manageable = manageable || project.getOwnerId().equals(dbc.currentUser().getId()); 4171 4172 // project managers 4173 Iterator<CmsOrganizationalUnit> itOus = ous.iterator(); 4174 while (!manageable && itOus.hasNext()) { 4175 CmsOrganizationalUnit ou = itOus.next(); 4176 // for project managers check visibility 4177 manageable = manageable || project.getOuFqn().startsWith(ou.getName()); 4178 } 4179 4180 if (!manageable) { 4181 // if manager of project 4182 if (userGroupIds.contains(project.getManagerGroupId())) { 4183 String oufqn = readGroup(dbc, project.getManagerGroupId()).getOuFqn(); 4184 manageable = manageable || (oufqn.startsWith(dbc.getRequestContext().getOuFqn())); 4185 } 4186 } 4187 if (!manageable) { 4188 // remove not accessible project 4189 itProjects.remove(); 4190 } 4191 } 4192 4193 List<CmsProject> manageableProjects = new ArrayList<CmsProject>(projects); 4194 // sort the list of projects based on the project name 4195 Collections.sort(manageableProjects); 4196 // ensure the online project is not in the list 4197 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 4198 if (manageableProjects.contains(onlineProject)) { 4199 manageableProjects.remove(onlineProject); 4200 } 4201 4202 return manageableProjects; 4203 } 4204 4205 /** 4206 * Returns all child groups of a group.<p> 4207 * 4208 * @param dbc the current database context 4209 * @param group the group to get the child for 4210 * @param includeSubChildren if set also returns all sub-child groups of the given group 4211 * 4212 * @return a list of all child <code>{@link CmsGroup}</code> objects 4213 * 4214 * @throws CmsException if operation was not successful 4215 */ 4216 public List<CmsGroup> getChildren(CmsDbContext dbc, CmsGroup group, boolean includeSubChildren) 4217 throws CmsException { 4218 4219 if (!includeSubChildren) { 4220 return getUserDriver(dbc).readChildGroups(dbc, group.getName()); 4221 } 4222 Set<CmsGroup> allChildren = new TreeSet<CmsGroup>(); 4223 // iterate all child groups 4224 Iterator<CmsGroup> it = getUserDriver(dbc).readChildGroups(dbc, group.getName()).iterator(); 4225 while (it.hasNext()) { 4226 CmsGroup child = it.next(); 4227 // add the group itself 4228 allChildren.add(child); 4229 // now get all sub-children for each group 4230 allChildren.addAll(getChildren(dbc, child, true)); 4231 } 4232 return new ArrayList<CmsGroup>(allChildren); 4233 } 4234 4235 /** 4236 * Returns the date when the resource was last visited by the user.<p> 4237 * 4238 * @param dbc the database context 4239 * @param poolName the name of the database pool to use 4240 * @param user the user to check the date 4241 * @param resource the resource to check the date 4242 * 4243 * @return the date when the resource was last visited by the user 4244 * 4245 * @throws CmsException if something goes wrong 4246 */ 4247 public long getDateLastVisitedBy(CmsDbContext dbc, String poolName, CmsUser user, CmsResource resource) 4248 throws CmsException { 4249 4250 return m_subscriptionDriver.getDateLastVisitedBy(dbc, poolName, user, resource); 4251 } 4252 4253 /** 4254 * Returns all groups of the given organizational unit.<p> 4255 * 4256 * @param dbc the current db context 4257 * @param orgUnit the organizational unit to get the groups for 4258 * @param includeSubOus if all groups of sub-organizational units should be retrieved too 4259 * @param readRoles if to read roles or groups 4260 * 4261 * @return all <code>{@link CmsGroup}</code> objects in the organizational unit 4262 * 4263 * @throws CmsException if operation was not successful 4264 * 4265 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 4266 * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean) 4267 */ 4268 public List<CmsGroup> getGroups( 4269 CmsDbContext dbc, 4270 CmsOrganizationalUnit orgUnit, 4271 boolean includeSubOus, 4272 boolean readRoles) 4273 throws CmsException { 4274 4275 return getUserDriver(dbc).getGroups(dbc, orgUnit, includeSubOus, readRoles); 4276 } 4277 4278 /** 4279 * Returns the groups of an user filtered by the specified IP address.<p> 4280 * 4281 * @param dbc the current database context 4282 * @param username the name of the user 4283 * @param readRoles if to read roles or groups 4284 * 4285 * @return the groups of the given user, as a list of {@link CmsGroup} objects 4286 * 4287 * @throws CmsException if something goes wrong 4288 */ 4289 public List<CmsGroup> getGroupsOfUser(CmsDbContext dbc, String username, boolean readRoles) throws CmsException { 4290 4291 return getGroupsOfUser(dbc, username, "", true, readRoles, false, dbc.getRequestContext().getRemoteAddress()); 4292 } 4293 4294 /** 4295 * Returns the groups of an user filtered by the specified IP address.<p> 4296 * 4297 * @param dbc the current database context 4298 * @param username the name of the user 4299 * @param ouFqn the fully qualified name of the organizational unit to restrict the result set for 4300 * @param includeChildOus include groups of child organizational units 4301 * @param readRoles if to read roles or groups 4302 * @param directGroupsOnly if set only the direct assigned groups will be returned, if not also indirect groups 4303 * @param remoteAddress the IP address to filter the groups in the result list 4304 * 4305 * @return a list of <code>{@link CmsGroup}</code> objects 4306 * 4307 * @throws CmsException if operation was not successful 4308 */ 4309 public List<CmsGroup> getGroupsOfUser( 4310 CmsDbContext dbc, 4311 String username, 4312 String ouFqn, 4313 boolean includeChildOus, 4314 boolean readRoles, 4315 boolean directGroupsOnly, 4316 String remoteAddress) 4317 throws CmsException { 4318 4319 CmsUser user = readUser(dbc, username); 4320 String prefix = ouFqn + "_" + includeChildOus + "_" + directGroupsOnly + "_" + readRoles + "_" + remoteAddress; 4321 String cacheKey = m_keyGenerator.getCacheKeyForUserGroups(prefix, dbc, user); 4322 List<CmsGroup> groups = m_monitor.getCachedUserGroups(user.getId(), cacheKey); 4323 if (groups == null) { 4324 // get all groups of the user 4325 List<CmsGroup> directGroups = getUserDriver(dbc).readGroupsOfUser( 4326 dbc, 4327 user.getId(), 4328 readRoles ? "" : ouFqn, 4329 readRoles ? true : includeChildOus, 4330 remoteAddress, 4331 readRoles); 4332 Set<CmsGroup> allGroups = new HashSet<CmsGroup>(); 4333 if (!readRoles) { 4334 allGroups.addAll(directGroups); 4335 } 4336 if (!directGroupsOnly) { 4337 if (!readRoles) { 4338 // now get all parents of the groups 4339 for (int i = 0; i < directGroups.size(); i++) { 4340 CmsGroup parent = getParent(dbc, directGroups.get(i).getName()); 4341 while ((parent != null) && (!allGroups.contains(parent))) { 4342 if (parent.getOuFqn().startsWith(ouFqn)) { 4343 allGroups.add(parent); 4344 } 4345 // read next parent group 4346 parent = getParent(dbc, parent.getName()); 4347 } 4348 } 4349 } 4350 } 4351 if (readRoles) { 4352 // for each for role 4353 for (int i = 0; i < directGroups.size(); i++) { 4354 CmsGroup group = directGroups.get(i); 4355 CmsRole role = CmsRole.valueOf(group); 4356 if (!includeChildOus && role.getOuFqn().equals(ouFqn)) { 4357 allGroups.add(group); 4358 } 4359 if (includeChildOus && role.getOuFqn().startsWith(ouFqn)) { 4360 allGroups.add(group); 4361 } 4362 if (directGroupsOnly || (!includeChildOus && !role.getOuFqn().equals(ouFqn))) { 4363 // if roles of child OUs are not requested and the role does not belong to the requested OU don't include the role children 4364 continue; 4365 } 4366 CmsOrganizationalUnit currentOu = readOrganizationalUnit(dbc, group.getOuFqn()); 4367 boolean readChildRoleGroups = true; 4368 if (currentOu.hasFlagWebuser() && role.forOrgUnit(null).equals(CmsRole.ACCOUNT_MANAGER)) { 4369 readChildRoleGroups = false; 4370 } 4371 if (readChildRoleGroups) { 4372 // get the child roles 4373 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 4374 while (itChildRoles.hasNext()) { 4375 CmsRole childRole = itChildRoles.next(); 4376 if (childRole.isSystemRole()) { 4377 if (canReadRoleInOu(currentOu, childRole)) { 4378 // include system roles only 4379 try { 4380 allGroups.add(readGroup(dbc, childRole.getGroupName())); 4381 } catch (CmsDataAccessException e) { 4382 // should not happen, log error if it does 4383 LOG.error(e.getLocalizedMessage(), e); 4384 } 4385 } 4386 } 4387 } 4388 } else { 4389 LOG.info("Skipping child role group check for web user OU " + currentOu.getName()); 4390 } 4391 if (includeChildOus) { 4392 // if needed include the roles of child ous 4393 Iterator<CmsOrganizationalUnit> itSubOus = getOrganizationalUnits( 4394 dbc, 4395 readOrganizationalUnit(dbc, group.getOuFqn()), 4396 true).iterator(); 4397 while (itSubOus.hasNext()) { 4398 CmsOrganizationalUnit subOu = itSubOus.next(); 4399 // add role in child ou 4400 try { 4401 if (canReadRoleInOu(subOu, role)) { 4402 allGroups.add(readGroup(dbc, role.forOrgUnit(subOu.getName()).getGroupName())); 4403 } 4404 } catch (CmsDbEntryNotFoundException e) { 4405 // ignore, this may happen while deleting an orgunit 4406 if (LOG.isDebugEnabled()) { 4407 LOG.debug(e.getLocalizedMessage(), e); 4408 } 4409 } 4410 // add child roles in child ous 4411 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 4412 while (itChildRoles.hasNext()) { 4413 CmsRole childRole = itChildRoles.next(); 4414 try { 4415 if (canReadRoleInOu(subOu, childRole)) { 4416 allGroups.add( 4417 readGroup(dbc, childRole.forOrgUnit(subOu.getName()).getGroupName())); 4418 } 4419 } catch (CmsDbEntryNotFoundException e) { 4420 // ignore, this may happen while deleting an orgunit 4421 if (LOG.isDebugEnabled()) { 4422 LOG.debug(e.getLocalizedMessage(), e); 4423 } 4424 } 4425 } 4426 } 4427 } 4428 } 4429 } 4430 // make group list unmodifiable for caching 4431 groups = Collections.unmodifiableList(new ArrayList<CmsGroup>(allGroups)); 4432 if (dbc.getProjectId().isNullUUID()) { 4433 m_monitor.getGroupListCache().setGroups(user, cacheKey, groups); 4434 } 4435 } 4436 4437 return groups; 4438 } 4439 4440 /** 4441 * Returns the history driver.<p> 4442 * 4443 * @return the history driver 4444 */ 4445 public I_CmsHistoryDriver getHistoryDriver() { 4446 4447 return m_historyDriver; 4448 } 4449 4450 /** 4451 * Returns the history driver for a given database context.<p> 4452 * 4453 * @param dbc the database context 4454 * @return the history driver for the database context 4455 */ 4456 public I_CmsHistoryDriver getHistoryDriver(CmsDbContext dbc) { 4457 4458 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4459 return m_historyDriver; 4460 } 4461 I_CmsHistoryDriver driver = dbc.getHistoryDriver(dbc.getProjectId()); 4462 return driver != null ? driver : m_historyDriver; 4463 4464 } 4465 4466 /** 4467 * Returns the number of idle connections managed by a pool.<p> 4468 * 4469 * @param dbPoolUrl the url of a pool 4470 * @return the number of idle connections 4471 * @throws CmsDbException if something goes wrong 4472 */ 4473 public int getIdleConnections(String dbPoolUrl) throws CmsDbException { 4474 4475 CmsDbPoolV11 pool = m_pools.get(dbPoolUrl); 4476 if (pool == null) { 4477 CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl); 4478 throw new CmsDbException(message); 4479 } 4480 try { 4481 return pool.getIdleConnections(); 4482 } catch (Exception exc) { 4483 CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl); 4484 throw new CmsDbException(message, exc); 4485 } 4486 4487 } 4488 4489 /** 4490 * Returns the lock state of a resource.<p> 4491 * 4492 * @param dbc the current database context 4493 * @param resource the resource to return the lock state for 4494 * 4495 * @return the lock state of the resource 4496 * 4497 * @throws CmsException if something goes wrong 4498 */ 4499 public CmsLock getLock(CmsDbContext dbc, CmsResource resource) throws CmsException { 4500 4501 return m_lockManager.getLock(dbc, resource); 4502 } 4503 4504 /** 4505 * Returns all locked resources in a given folder.<p> 4506 * 4507 * @param dbc the current database context 4508 * @param resource the folder to search in 4509 * @param filter the lock filter 4510 * 4511 * @return a list of locked resource paths (relative to current site) 4512 * 4513 * @throws CmsException if the current project is locked 4514 */ 4515 public List<String> getLockedResources(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter) 4516 throws CmsException { 4517 4518 List<String> lockedResources = new ArrayList<String>(); 4519 // get locked resources 4520 Iterator<CmsLock> it = m_lockManager.getLocks(dbc, resource.getRootPath(), filter).iterator(); 4521 while (it.hasNext()) { 4522 CmsLock lock = it.next(); 4523 lockedResources.add(dbc.removeSiteRoot(lock.getResourceName())); 4524 } 4525 Collections.sort(lockedResources); 4526 return lockedResources; 4527 } 4528 4529 /** 4530 * Returns all locked resources in a given folder.<p> 4531 * 4532 * @param dbc the current database context 4533 * @param resource the folder to search in 4534 * @param filter the lock filter 4535 * 4536 * @return a list of locked resources 4537 * 4538 * @throws CmsException if the current project is locked 4539 */ 4540 public List<CmsResource> getLockedResourcesObjects(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter) 4541 throws CmsException { 4542 4543 return m_lockManager.getLockedResources(dbc, resource, filter); 4544 } 4545 4546 /** 4547 * Returns all locked resources in a given folder, but uses a cache for resource lookups.<p> 4548 * 4549 * @param dbc the current database context 4550 * @param resource the folder to search in 4551 * @param filter the lock filter 4552 * @param cache the cache to use for resource lookups 4553 * 4554 * @return a list of locked resources 4555 * 4556 * @throws CmsException if the current project is locked 4557 */ 4558 public List<CmsResource> getLockedResourcesObjectsWithCache( 4559 CmsDbContext dbc, 4560 CmsResource resource, 4561 CmsLockFilter filter, 4562 Map<String, CmsResource> cache) 4563 throws CmsException { 4564 4565 return m_lockManager.getLockedResourcesWithCache(dbc, resource, filter, cache); 4566 } 4567 4568 /** 4569 * Returns all log entries matching the given filter.<p> 4570 * 4571 * @param dbc the current db context 4572 * @param filter the filter to match the log entries 4573 * 4574 * @return all log entries matching the given filter 4575 * 4576 * @throws CmsException if something goes wrong 4577 * 4578 * @see CmsSecurityManager#getLogEntries(CmsRequestContext, CmsLogFilter) 4579 */ 4580 public List<CmsLogEntry> getLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException { 4581 4582 updateLog(dbc); 4583 return m_projectDriver.readLog(dbc, filter); 4584 } 4585 4586 /** 4587 * Returns the next publish tag for the published historical resources.<p> 4588 * 4589 * @param dbc the current database context 4590 * 4591 * @return the next available publish tag 4592 */ 4593 public int getNextPublishTag(CmsDbContext dbc) { 4594 4595 if (dbc.getProjectId().isNullUUID()) { 4596 synchronized (m_publishTagLock) { 4597 int dbNextPublishTag = getHistoryDriver(dbc).readNextPublishTag(dbc); 4598 if (m_lastPublishTag == null) { 4599 m_lastPublishTag = Integer.valueOf(dbNextPublishTag); 4600 return dbNextPublishTag; 4601 } else { 4602 int newValue = Math.max(dbNextPublishTag, m_lastPublishTag.intValue() + 1); 4603 m_lastPublishTag = Integer.valueOf(newValue); 4604 return newValue; 4605 4606 } 4607 } 4608 } else { 4609 return getHistoryDriver(dbc).readNextPublishTag(dbc); 4610 } 4611 4612 } 4613 4614 /** 4615 * Returns all child organizational units of the given parent organizational unit including 4616 * hierarchical deeper organization units if needed.<p> 4617 * 4618 * @param dbc the current db context 4619 * @param parent the parent organizational unit, or <code>null</code> for the root 4620 * @param includeChildren if hierarchical deeper organization units should also be returned 4621 * 4622 * @return a list of <code>{@link CmsOrganizationalUnit}</code> objects 4623 * 4624 * @throws CmsException if operation was not successful 4625 * 4626 * @see org.opencms.security.CmsOrgUnitManager#getOrganizationalUnits(CmsObject, String, boolean) 4627 */ 4628 public List<CmsOrganizationalUnit> getOrganizationalUnits( 4629 CmsDbContext dbc, 4630 CmsOrganizationalUnit parent, 4631 boolean includeChildren) 4632 throws CmsException { 4633 4634 if (parent == null) { 4635 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PARENT_ORGUNIT_NULL_0)); 4636 } 4637 return getUserDriver(dbc).getOrganizationalUnits(dbc, parent, includeChildren); 4638 } 4639 4640 /** 4641 * Returns all the organizational units for which the current user has the given role.<p> 4642 * 4643 * @param dbc the current database context 4644 * @param role the role to check 4645 * @param includeSubOus if sub organizational units should be included in the search 4646 * 4647 * @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects 4648 * 4649 * @throws CmsException if something goes wrong 4650 */ 4651 public List<CmsOrganizationalUnit> getOrgUnitsForRole(CmsDbContext dbc, CmsRole role, boolean includeSubOus) 4652 throws CmsException { 4653 4654 String ouFqn = role.getOuFqn(); 4655 if (ouFqn == null) { 4656 ouFqn = ""; 4657 role = role.forOrgUnit(""); 4658 } 4659 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouFqn); 4660 List<CmsOrganizationalUnit> orgUnits = new ArrayList<CmsOrganizationalUnit>(); 4661 if (m_securityManager.hasRole(dbc, dbc.currentUser(), role)) { 4662 orgUnits.add(ou); 4663 } 4664 if (includeSubOus) { 4665 Iterator<CmsOrganizationalUnit> it = getOrganizationalUnits(dbc, ou, true).iterator(); 4666 while (it.hasNext()) { 4667 CmsOrganizationalUnit orgUnit = it.next(); 4668 if (m_securityManager.hasRole(dbc, dbc.currentUser(), role.forOrgUnit(orgUnit.getName()))) { 4669 orgUnits.add(orgUnit); 4670 } 4671 } 4672 } 4673 return orgUnits; 4674 } 4675 4676 /** 4677 * Returns the parent group of a group.<p> 4678 * 4679 * @param dbc the current database context 4680 * @param groupname the name of the group 4681 * 4682 * @return group the parent group or <code>null</code> 4683 * 4684 * @throws CmsException if operation was not successful 4685 */ 4686 public CmsGroup getParent(CmsDbContext dbc, String groupname) throws CmsException { 4687 4688 CmsGroup group = readGroup(dbc, groupname); 4689 if (group.getParentId().isNullUUID()) { 4690 return null; 4691 } 4692 4693 // try to read from cache 4694 CmsGroup parent = m_monitor.getCachedGroup(group.getParentId().toString()); 4695 if (parent == null) { 4696 parent = getUserDriver(dbc).readGroup(dbc, group.getParentId()); 4697 m_monitor.cacheGroup(parent); 4698 } 4699 return parent; 4700 } 4701 4702 /** 4703 * Returns the set of permissions of the current user for a given resource.<p> 4704 * 4705 * @param dbc the current database context 4706 * @param resource the resource 4707 * @param user the user 4708 * 4709 * @return bit set with allowed permissions 4710 * 4711 * @throws CmsException if something goes wrong 4712 */ 4713 public CmsPermissionSetCustom getPermissions(CmsDbContext dbc, CmsResource resource, CmsUser user) 4714 throws CmsException { 4715 4716 CmsAccessControlList acList = getAccessControlList(dbc, resource, false); 4717 List<CmsGroup> groups = getGroupsOfUser(dbc, user.getName(), false); 4718 List<CmsRole> roles = getRolesForUser(dbc, user); 4719 CmsPermissionSetCustom permissions = acList.getPermissions(user, groups, roles); 4720 4721 if (acList.getExclusiveAccessPrincipals().size() > 0) { 4722 long now; 4723 @SuppressWarnings("unchecked") 4724 Supplier<Long> alternativeClock = (Supplier<Long>)(dbc.getRequestContext().getAttribute( 4725 ATTR_EXCLUSIVE_ACCESS_CLOCK)); 4726 if (alternativeClock != null) { 4727 // used for testing 4728 now = alternativeClock.get().longValue(); 4729 } else { 4730 // *NOT* using dbc.getRequestContext().getRequestTime(), even though that value is used for normal resource availability checks, because 4731 // that value may be manipulated by some workplace classes, and we want the real time for permission checks 4732 now = System.currentTimeMillis(); 4733 } 4734 permissions.setCacheable(false); // resources going in/out of availability can change permissions - don't cache 4735 if (!resource.isReleasedAndNotExpired(now)) { 4736 boolean hasExclusiveAccess = false; 4737 for (CmsGroup group : groups) { 4738 if (acList.getExclusiveAccessPrincipals().contains(group.getId())) { 4739 hasExclusiveAccess = true; 4740 break; 4741 } 4742 } 4743 hasExclusiveAccess |= acList.getExclusiveAccessPrincipals().contains(user.getId()); 4744 if (!hasExclusiveAccess) { 4745 permissions.denyPermissions(CmsPermissionSet.PERMISSION_FULL); 4746 } 4747 } 4748 } 4749 return permissions; 4750 } 4751 4752 /** 4753 * Returns the project driver.<p> 4754 * 4755 * @return the project driver 4756 */ 4757 public I_CmsProjectDriver getProjectDriver() { 4758 4759 return m_projectDriver; 4760 } 4761 4762 /** 4763 * Returns the project driver for a given DB context.<p> 4764 * 4765 * @param dbc the database context 4766 * 4767 * @return the project driver for the database context 4768 */ 4769 public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc) { 4770 4771 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4772 return m_projectDriver; 4773 } 4774 I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId()); 4775 return driver != null ? driver : m_projectDriver; 4776 } 4777 4778 /** 4779 * Returns either the project driver for the DB context (if it has one) or a default project driver.<p> 4780 * 4781 * @param dbc the DB context 4782 * @param defaultDriver the driver which should be returned if there is no project driver for the DB context 4783 * 4784 * @return either the project driver for the DB context, or the default driver 4785 */ 4786 public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc, I_CmsProjectDriver defaultDriver) { 4787 4788 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4789 return defaultDriver; 4790 } 4791 I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId()); 4792 return driver != null ? driver : defaultDriver; 4793 } 4794 4795 /** 4796 * Returns the uuid id for the given id.<p> 4797 * 4798 * TODO: remove this method as soon as possible 4799 * 4800 * @param dbc the current database context 4801 * @param id the old project id 4802 * 4803 * @return the new uuid for the given id 4804 * 4805 * @throws CmsException if something goes wrong 4806 */ 4807 public CmsUUID getProjectId(CmsDbContext dbc, int id) throws CmsException { 4808 4809 Iterator<CmsProject> itProjects = getAllAccessibleProjects( 4810 dbc, 4811 readOrganizationalUnit(dbc, ""), 4812 true).iterator(); 4813 while (itProjects.hasNext()) { 4814 CmsProject project = itProjects.next(); 4815 if (project.getUuid().hashCode() == id) { 4816 return project.getUuid(); 4817 } 4818 } 4819 return null; 4820 } 4821 4822 /** 4823 * Returns the configuration read from the <code>opencms.properties</code> file.<p> 4824 * 4825 * @return the configuration read from the <code>opencms.properties</code> file 4826 */ 4827 public CmsParameterConfiguration getPropertyConfiguration() { 4828 4829 return m_propertyConfiguration; 4830 } 4831 4832 /** 4833 * Returns a new publish list that contains the unpublished resources related 4834 * to all resources in the given publish list, the related resources exclude 4835 * all resources in the given publish list and also locked (by other users) resources.<p> 4836 * 4837 * @param dbc the current database context 4838 * @param publishList the publish list to exclude from result 4839 * @param filter the relation filter to use to get the related resources 4840 * 4841 * @return a new publish list that contains the related resources 4842 * 4843 * @throws CmsException if something goes wrong 4844 * 4845 * @see org.opencms.publish.CmsPublishManager#getRelatedResourcesToPublish(CmsObject, CmsPublishList) 4846 */ 4847 public CmsPublishList getRelatedResourcesToPublish( 4848 CmsDbContext dbc, 4849 CmsPublishList publishList, 4850 CmsRelationFilter filter) 4851 throws CmsException { 4852 4853 Map<String, CmsResource> relations = new HashMap<String, CmsResource>(); 4854 4855 // check if progress should be set in the thread 4856 A_CmsProgressThread thread = null; 4857 if (Thread.currentThread() instanceof A_CmsProgressThread) { 4858 thread = (A_CmsProgressThread)Thread.currentThread(); 4859 } 4860 4861 // get all resources to publish 4862 List<CmsResource> publishResources = publishList.getAllResources(); 4863 Iterator<CmsResource> itCheckList = publishResources.iterator(); 4864 // iterate over them 4865 int count = 0; 4866 while (itCheckList.hasNext()) { 4867 4868 // set progress in thread 4869 count++; 4870 if (thread != null) { 4871 4872 if (thread.isInterrupted()) { 4873 throw new CmsIllegalStateException( 4874 org.opencms.workplace.commons.Messages.get().container( 4875 org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0)); 4876 } 4877 thread.setProgress((count * 20) / publishResources.size()); 4878 thread.setDescription( 4879 org.opencms.workplace.commons.Messages.get().getBundle().key( 4880 org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP1_2, 4881 Integer.valueOf(count), 4882 Integer.valueOf(publishResources.size()))); 4883 } 4884 4885 CmsResource checkResource = itCheckList.next(); 4886 // get and iterate over all related resources 4887 Iterator<CmsRelation> itRelations = getRelationsForResource(dbc, checkResource, filter).iterator(); 4888 while (itRelations.hasNext()) { 4889 CmsRelation relation = itRelations.next(); 4890 try { 4891 // get the target of the relation, see CmsRelation#getTarget(CmsObject, CmsResourceFilter) 4892 CmsResource target; 4893 try { 4894 // first look up by id 4895 target = readResource(dbc, relation.getTargetId(), CmsResourceFilter.ALL); 4896 } catch (CmsVfsResourceNotFoundException e) { 4897 // then look up by name, but from the root site 4898 String storedSiteRoot = dbc.getRequestContext().getSiteRoot(); 4899 try { 4900 dbc.getRequestContext().setSiteRoot(""); 4901 target = readResource(dbc, relation.getTargetPath(), CmsResourceFilter.ALL); 4902 } finally { 4903 dbc.getRequestContext().setSiteRoot(storedSiteRoot); 4904 } 4905 } 4906 CmsLock lock = getLock(dbc, target); 4907 // just add resources that may come in question 4908 if (!publishResources.contains(target) // is not in the original list 4909 && !relations.containsKey(target.getRootPath()) // has not been already added by another relation 4910 && !target.getState().isUnchanged() // has been changed 4911 && lock.isLockableBy(dbc.currentUser())) { // is lockable by current user 4912 4913 relations.put(target.getRootPath(), target); 4914 // now check the folder structure 4915 CmsResource parent = getVfsDriver(dbc).readParentFolder( 4916 dbc, 4917 dbc.currentProject().getUuid(), 4918 target.getStructureId()); 4919 while ((parent != null) && parent.getState().isNew()) { 4920 // just add resources that may come in question 4921 if (!publishResources.contains(parent) // is not in the original list 4922 && !relations.containsKey(parent.getRootPath())) { // has not been already added by another relation 4923 4924 relations.put(parent.getRootPath(), parent); 4925 } 4926 parent = getVfsDriver(dbc).readParentFolder( 4927 dbc, 4928 dbc.currentProject().getUuid(), 4929 parent.getStructureId()); 4930 } 4931 } 4932 } catch (CmsVfsResourceNotFoundException e) { 4933 // ignore broken links 4934 if (LOG.isDebugEnabled()) { 4935 LOG.debug(e.getLocalizedMessage(), e); 4936 } 4937 } 4938 } 4939 } 4940 4941 CmsPublishList ret = new CmsPublishList(publishList.getDirectPublishResources(), false, false); 4942 ret.addAll(relations.values(), false); 4943 ret.initialize(); 4944 return ret; 4945 } 4946 4947 /** 4948 * Returns all relations for the given resource matching the given filter.<p> 4949 * 4950 * @param dbc the current db context 4951 * @param resource the resource to retrieve the relations for 4952 * @param filter the filter to match the relation 4953 * 4954 * @return all relations for the given resource matching the given filter 4955 * 4956 * @throws CmsException if something goes wrong 4957 * 4958 * @see CmsSecurityManager#getRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter) 4959 */ 4960 public List<CmsRelation> getRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter) 4961 throws CmsException { 4962 4963 CmsUUID projectId = getProjectIdForContext(dbc); 4964 return getVfsDriver(dbc).readRelations(dbc, projectId, resource, filter); 4965 } 4966 4967 /** 4968 * Returns the list of organizational units the given resource belongs to.<p> 4969 * 4970 * @param dbc the current database context 4971 * @param resource the resource 4972 * 4973 * @return list of {@link CmsOrganizationalUnit} objects 4974 * 4975 * @throws CmsException if something goes wrong 4976 */ 4977 public List<CmsOrganizationalUnit> getResourceOrgUnits(CmsDbContext dbc, CmsResource resource) throws CmsException { 4978 4979 boolean nullDbcProjectId = (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID(); 4980 if (nullDbcProjectId && resourceOrgUnitCachingEnabled) { 4981 try { 4982 return m_monitor.getResourceOuCache().get(new ResourceOUCacheKey(this, dbc)).getResourceOrgUnits( 4983 resource.getRootPath()); 4984 } catch (ExecutionException e) { 4985 LOG.error(e.getLocalizedMessage(), e); 4986 } 4987 } 4988 List<CmsOrganizationalUnit> result = getVfsDriver(dbc).getResourceOus( 4989 dbc, 4990 dbc.currentProject().getUuid(), 4991 resource); 4992 4993 return result; 4994 } 4995 4996 /** 4997 * Returns all resources of the given organizational unit.<p> 4998 * 4999 * @param dbc the current db context 5000 * @param orgUnit the organizational unit to get all resources for 5001 * 5002 * @return all <code>{@link CmsResource}</code> objects in the organizational unit 5003 * 5004 * @throws CmsException if operation was not successful 5005 * 5006 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5007 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5008 * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean) 5009 */ 5010 public List<CmsResource> getResourcesForOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit) 5011 throws CmsException { 5012 5013 return getUserDriver(dbc).getResourcesForOrganizationalUnit(dbc, orgUnit); 5014 } 5015 5016 /** 5017 * Returns all resources associated to a given principal via an ACE with the given permissions.<p> 5018 * 5019 * If the <code>includeAttr</code> flag is set it returns also all resources associated to 5020 * a given principal through some of following attributes.<p> 5021 * 5022 * <ul> 5023 * <li>User Created</li> 5024 * <li>User Last Modified</li> 5025 * </ul><p> 5026 * 5027 * @param dbc the current database context 5028 * @param project the to read the entries from 5029 * @param principalId the id of the principal 5030 * @param permissions a set of permissions to match, can be <code>null</code> for all ACEs 5031 * @param includeAttr a flag to include resources associated by attributes 5032 * 5033 * @return a set of <code>{@link CmsResource}</code> objects 5034 * 5035 * @throws CmsException if something goes wrong 5036 */ 5037 public Set<CmsResource> getResourcesForPrincipal( 5038 CmsDbContext dbc, 5039 CmsProject project, 5040 CmsUUID principalId, 5041 CmsPermissionSet permissions, 5042 boolean includeAttr) 5043 throws CmsException { 5044 5045 Set<CmsResource> resources = new HashSet<CmsResource>( 5046 getVfsDriver(dbc).readResourcesForPrincipalACE(dbc, project, principalId)); 5047 if (permissions != null) { 5048 Iterator<CmsResource> itRes = resources.iterator(); 5049 while (itRes.hasNext()) { 5050 CmsAccessControlEntry ace = readAccessControlEntry(dbc, itRes.next(), principalId); 5051 if ((ace.getPermissions().getPermissions() 5052 & permissions.getPermissions()) != permissions.getPermissions()) { 5053 // remove if permissions does not match 5054 itRes.remove(); 5055 } 5056 } 5057 } 5058 if (includeAttr) { 5059 resources.addAll(getVfsDriver(dbc).readResourcesForPrincipalAttr(dbc, project, principalId)); 5060 } 5061 return resources; 5062 } 5063 5064 /** 5065 * Gets the rewrite aliases matching a given filter.<p> 5066 * 5067 * @param dbc the current database context 5068 * @param filter the filter used for filtering rewrite aliases 5069 * 5070 * @return the rewrite aliases matching the given filter 5071 * 5072 * @throws CmsException if something goes wrong 5073 */ 5074 public List<CmsRewriteAlias> getRewriteAliases(CmsDbContext dbc, CmsRewriteAliasFilter filter) throws CmsException { 5075 5076 return getVfsDriver(dbc).readRewriteAliases(dbc, filter); 5077 } 5078 5079 /** 5080 * Collects the groups which constitute a given role.<p> 5081 * 5082 * @param dbc the database context 5083 * @param roleGroupName the group related to the role 5084 * @param directUsersOnly if true, only the group belonging to the entry itself wil 5085 * 5086 * @return the set of groups which constitute the role 5087 * 5088 * @throws CmsException if something goes wrong 5089 */ 5090 public Set<CmsGroup> getRoleGroups(CmsDbContext dbc, String roleGroupName, boolean directUsersOnly) 5091 throws CmsException { 5092 5093 return getRoleGroupsImpl(dbc, roleGroupName, directUsersOnly, new HashMap<String, Set<CmsGroup>>()); 5094 } 5095 5096 /** 5097 * Collects the groups which constitute a given role.<p> 5098 * 5099 * @param dbc the database context 5100 * @param roleGroupName the group related to the role 5101 * @param directUsersOnly if true, only the group belonging to the entry itself wil 5102 * @param accumulator a map for memoizing return values of recursive calls 5103 * 5104 * @return the set of groups which constitute the role 5105 * 5106 * @throws CmsException if something goes wrong 5107 */ 5108 public Set<CmsGroup> getRoleGroupsImpl( 5109 CmsDbContext dbc, 5110 String roleGroupName, 5111 boolean directUsersOnly, 5112 Map<String, Set<CmsGroup>> accumulator) 5113 throws CmsException { 5114 5115 Set<CmsGroup> result = new HashSet<CmsGroup>(); 5116 if (accumulator.get(roleGroupName) != null) { 5117 return accumulator.get(roleGroupName); 5118 } 5119 CmsGroup group = readGroup(dbc, roleGroupName); // check that the group really exists 5120 if ((group == null) || (!group.isRole())) { 5121 throw new CmsDbEntryNotFoundException( 5122 Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, roleGroupName)); 5123 } 5124 result.add(group); 5125 if (!directUsersOnly) { 5126 CmsRole role = CmsRole.valueOf(group); 5127 if (role.getParentRole() != null) { 5128 try { 5129 String parentGroup = role.getParentRole().getGroupName(); 5130 // iterate the parent roles 5131 result.addAll(getRoleGroupsImpl(dbc, parentGroup, directUsersOnly, accumulator)); 5132 } catch (CmsDbEntryNotFoundException e) { 5133 // ignore, this may happen while deleting an orgunit 5134 if (LOG.isDebugEnabled()) { 5135 LOG.debug(e.getLocalizedMessage(), e); 5136 } 5137 } 5138 } 5139 String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn()); 5140 if (parentOu != null) { 5141 // iterate the parent ou's 5142 result.addAll(getRoleGroupsImpl(dbc, parentOu + group.getSimpleName(), directUsersOnly, accumulator)); 5143 } 5144 } 5145 accumulator.put(roleGroupName, result); 5146 return result; 5147 } 5148 5149 /** 5150 * Returns all roles the given user has for the given resource.<p> 5151 * 5152 * @param dbc the current database context 5153 * @param user the user to check 5154 * @param resource the resource to check the roles for 5155 * 5156 * @return a list of {@link CmsRole} objects 5157 * 5158 * @throws CmsException if something goes wrong 5159 */ 5160 public List<CmsRole> getRolesForResource(CmsDbContext dbc, CmsUser user, CmsResource resource) throws CmsException { 5161 5162 // guest user has no role 5163 if (user.isGuestUser()) { 5164 return Collections.emptyList(); 5165 } 5166 5167 // try to read from cache 5168 String key = user.getId().toString() + resource.getRootPath(); 5169 List<CmsRole> result = m_monitor.getCachedRoleList(key); 5170 if (result != null) { 5171 return result; 5172 } 5173 result = new ArrayList<CmsRole>(); 5174 5175 Iterator<CmsOrganizationalUnit> itOus = getResourceOrgUnits(dbc, resource).iterator(); 5176 while (itOus.hasNext()) { 5177 CmsOrganizationalUnit ou = itOus.next(); 5178 5179 // read all roles of the current user 5180 List<CmsGroup> groups = new ArrayList<CmsGroup>( 5181 getGroupsOfUser( 5182 dbc, 5183 user.getName(), 5184 ou.getName(), 5185 false, 5186 true, 5187 false, 5188 dbc.getRequestContext().getRemoteAddress())); 5189 // check the roles applying to the given resource 5190 Iterator<CmsGroup> it = groups.iterator(); 5191 while (it.hasNext()) { 5192 CmsGroup group = it.next(); 5193 CmsRole givenRole = CmsRole.valueOf(group).forOrgUnit(null); 5194 if (givenRole.isOrganizationalUnitIndependent() || result.contains(givenRole)) { 5195 // skip already added roles 5196 continue; 5197 } 5198 result.add(givenRole); 5199 } 5200 } 5201 5202 result = Collections.unmodifiableList(result); 5203 m_monitor.cacheRoleList(key, result); 5204 return result; 5205 } 5206 5207 /** 5208 * Returns all roles the given user has independent of the resource.<p> 5209 * 5210 * @param dbc the current database context 5211 * @param user the user to check 5212 * 5213 * @return a list of {@link CmsRole} objects 5214 * 5215 * @throws CmsException if something goes wrong 5216 */ 5217 public List<CmsRole> getRolesForUser(CmsDbContext dbc, CmsUser user) throws CmsException { 5218 5219 // guest user has no role 5220 if (user.isGuestUser()) { 5221 return Collections.emptyList(); 5222 } 5223 5224 // try to read from cache 5225 List<CmsRole> result = m_monitor.getGroupListCache().getBareRoles(user.getId()); 5226 if (result != null) { 5227 return result; 5228 } 5229 result = new ArrayList<CmsRole>(); 5230 5231 // read all roles of the current user 5232 List<CmsGroup> groups = new ArrayList<CmsGroup>( 5233 getGroupsOfUser(dbc, user.getName(), "", true, true, false, dbc.getRequestContext().getRemoteAddress())); 5234 5235 // check the roles applying to the given resource 5236 Iterator<CmsGroup> it = groups.iterator(); 5237 while (it.hasNext()) { 5238 CmsGroup group = it.next(); 5239 CmsRole givenRole = CmsRole.valueOf(group); 5240 givenRole = givenRole.forOrgUnit(null); 5241 if (!result.contains(givenRole)) { 5242 result.add(givenRole); 5243 } 5244 } 5245 result = Collections.unmodifiableList(result); 5246 m_monitor.getGroupListCache().setBareRoles(user, result); 5247 return result; 5248 } 5249 5250 /** 5251 * Returns the security manager this driver manager belongs to.<p> 5252 * 5253 * @return the security manager this driver manager belongs to 5254 */ 5255 public CmsSecurityManager getSecurityManager() { 5256 5257 return m_securityManager; 5258 } 5259 5260 /** 5261 * Returns an instance of the common sql manager.<p> 5262 * 5263 * @return an instance of the common sql manager 5264 */ 5265 public CmsSqlManager getSqlManager() { 5266 5267 return m_sqlManager; 5268 } 5269 5270 /** 5271 * Returns a delivery-capable storage backend by its stable storage identifier.<p> 5272 * 5273 * @param dbc the current database context 5274 * @param storage the stable storage identifier 5275 * 5276 * @return the delivery-capable storage backend, or <code>null</code> if not supported 5277 * 5278 * @throws CmsException if operation was not successful 5279 */ 5280 public I_CmsStorageDelivery getStoredContentDelivery(CmsDbContext dbc, String storage) throws CmsException { 5281 5282 return getVfsDriver(dbc).getStoredContentDelivery(dbc, storage); 5283 } 5284 5285 /** 5286 * Returns the subscription driver of this driver manager.<p> 5287 * 5288 * @return a subscription driver 5289 */ 5290 public I_CmsSubscriptionDriver getSubscriptionDriver() { 5291 5292 return m_subscriptionDriver; 5293 } 5294 5295 /** 5296 * Returns the user driver.<p> 5297 * 5298 * @return the user driver 5299 */ 5300 public I_CmsUserDriver getUserDriver() { 5301 5302 return m_userDriver; 5303 } 5304 5305 /** 5306 * Returns the user driver for a given database context.<p> 5307 * 5308 * @param dbc the database context 5309 * 5310 * @return the user driver for the database context 5311 */ 5312 public I_CmsUserDriver getUserDriver(CmsDbContext dbc) { 5313 5314 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5315 return m_userDriver; 5316 } 5317 I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId()); 5318 return driver != null ? driver : m_userDriver; 5319 5320 } 5321 5322 /** 5323 * Returns either the user driver for the given DB context (if it has one) or a default value instead.<p> 5324 * 5325 * @param dbc the DB context 5326 * @param defaultDriver the driver that should be returned if no driver for the DB context was found 5327 * 5328 * @return either the user driver for the DB context, or <code>defaultDriver</code> if none were found 5329 */ 5330 public I_CmsUserDriver getUserDriver(CmsDbContext dbc, I_CmsUserDriver defaultDriver) { 5331 5332 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5333 return defaultDriver; 5334 } 5335 I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId()); 5336 return driver != null ? driver : defaultDriver; 5337 } 5338 5339 /** 5340 * Returns all direct users of the given organizational unit.<p> 5341 * 5342 * @param dbc the current db context 5343 * @param orgUnit the organizational unit to get all users for 5344 * @param recursive if all groups of sub-organizational units should be retrieved too 5345 * 5346 * @return all <code>{@link CmsUser}</code> objects in the organizational unit 5347 * 5348 * @throws CmsException if operation was not successful 5349 * 5350 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5351 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5352 */ 5353 public List<CmsUser> getUsers(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, boolean recursive) 5354 throws CmsException { 5355 5356 return getUserDriver(dbc).getUsers(dbc, orgUnit, recursive); 5357 } 5358 5359 /** 5360 * Returns a list of users in a group.<p> 5361 * 5362 * @param dbc the current database context 5363 * @param groupname the name of the group to list users from 5364 * @param includeOtherOuUsers include users of other organizational units 5365 * @param directUsersOnly if set only the direct assigned users will be returned, 5366 * if not also indirect users, ie. members of parent roles, 5367 * this parameter only works with roles 5368 * @param readRoles if to read roles or groups 5369 * 5370 * @return all <code>{@link CmsUser}</code> objects in the group 5371 * 5372 * @throws CmsException if operation was not successful 5373 */ 5374 public List<CmsUser> getUsersOfGroup( 5375 CmsDbContext dbc, 5376 String groupname, 5377 boolean includeOtherOuUsers, 5378 boolean directUsersOnly, 5379 boolean readRoles) 5380 throws CmsException { 5381 5382 return internalUsersOfGroup( 5383 dbc, 5384 CmsOrganizationalUnit.getParentFqn(groupname), 5385 groupname, 5386 includeOtherOuUsers, 5387 directUsersOnly, 5388 readRoles); 5389 } 5390 5391 /** 5392 * Returns the given user's publish list.<p> 5393 * 5394 * @param dbc the database context 5395 * @param userId the user's id 5396 * 5397 * @return the given user's publish list 5398 * 5399 * @throws CmsDataAccessException if something goes wrong 5400 */ 5401 public List<CmsResource> getUsersPubList(CmsDbContext dbc, CmsUUID userId) throws CmsDataAccessException { 5402 5403 synchronized (m_publishListUpdateLock) { 5404 updateLog(dbc); 5405 return m_projectDriver.getUsersPubList(dbc, userId); 5406 } 5407 } 5408 5409 /** 5410 * Returns all direct users of the given organizational unit, without their additional info.<p> 5411 * 5412 * @param dbc the current db context 5413 * @param orgUnit the organizational unit to get all users for 5414 * @param recursive if all groups of sub-organizational units should be retrieved too 5415 * 5416 * @return all <code>{@link CmsUser}</code> objects in the organizational unit 5417 * 5418 * @throws CmsException if operation was not successful 5419 * 5420 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5421 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5422 */ 5423 public List<CmsUser> getUsersWithoutAdditionalInfo( 5424 CmsDbContext dbc, 5425 CmsOrganizationalUnit orgUnit, 5426 boolean recursive) 5427 throws CmsException { 5428 5429 return getUserDriver(dbc).getUsersWithoutAdditionalInfo(dbc, orgUnit, recursive); 5430 } 5431 5432 /** 5433 * Returns the VFS driver.<p> 5434 * 5435 * @return the VFS driver 5436 */ 5437 public I_CmsVfsDriver getVfsDriver() { 5438 5439 return m_vfsDriver; 5440 } 5441 5442 /** 5443 * Returns the VFS driver for the given database context.<p> 5444 * 5445 * @param dbc the database context 5446 * 5447 * @return a VFS driver 5448 */ 5449 public I_CmsVfsDriver getVfsDriver(CmsDbContext dbc) { 5450 5451 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5452 return m_vfsDriver; 5453 } 5454 I_CmsVfsDriver driver = dbc.getVfsDriver(dbc.getProjectId()); 5455 return driver != null ? driver : m_vfsDriver; 5456 5457 } 5458 5459 /** 5460 * Writes a vector of access control entries as new access control entries of a given resource.<p> 5461 * 5462 * Already existing access control entries of this resource are removed before. 5463 * Access is granted, if:<p> 5464 * <ul> 5465 * <li>the current user has control permission on the resource</li> 5466 * </ul> 5467 * 5468 * @param dbc the current database context 5469 * @param resource the resource 5470 * @param acEntries a list of <code>{@link CmsAccessControlEntry}</code> objects 5471 * 5472 * @throws CmsException if something goes wrong 5473 */ 5474 public void importAccessControlEntries( 5475 CmsDbContext dbc, 5476 CmsResource resource, 5477 List<CmsAccessControlEntry> acEntries) 5478 throws CmsException { 5479 5480 I_CmsUserDriver userDriver = getUserDriver(dbc); 5481 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId()); 5482 List<CmsAccessControlEntry> fixedAces = new ArrayList<>(); 5483 for (CmsAccessControlEntry entry : acEntries) { 5484 if (entry.getResource() == null) { 5485 entry = new CmsAccessControlEntry( 5486 resource.getResourceId(), 5487 entry.getPrincipal(), 5488 entry.getPermissions(), 5489 entry.getFlags()); 5490 } 5491 fixedAces.add(entry); 5492 } 5493 5494 Iterator<CmsAccessControlEntry> i = fixedAces.iterator(); 5495 while (i.hasNext()) { 5496 userDriver.writeAccessControlEntry(dbc, dbc.currentProject(), i.next()); 5497 } 5498 m_monitor.clearAccessControlListCache(); 5499 } 5500 5501 /** 5502 * Imports a rewrite alias.<p> 5503 * 5504 * @param dbc the database context 5505 * @param siteRoot the site root of the alias 5506 * @param source the source of the alias 5507 * @param target the target of the alias 5508 * @param mode the alias mode 5509 * 5510 * @return the import result 5511 * 5512 * @throws CmsException if something goes wrong 5513 */ 5514 public CmsAliasImportResult importRewriteAlias( 5515 CmsDbContext dbc, 5516 String siteRoot, 5517 String source, 5518 String target, 5519 CmsAliasMode mode) 5520 throws CmsException { 5521 5522 I_CmsVfsDriver vfs = getVfsDriver(dbc); 5523 List<CmsRewriteAlias> existingAliases = vfs.readRewriteAliases( 5524 dbc, 5525 new CmsRewriteAliasFilter().setSiteRoot(siteRoot)); 5526 CmsUUID idToDelete = null; 5527 for (CmsRewriteAlias alias : existingAliases) { 5528 if (alias.getPatternString().equals(source)) { 5529 idToDelete = alias.getId(); 5530 } 5531 } 5532 if (idToDelete != null) { 5533 vfs.deleteRewriteAliases(dbc, new CmsRewriteAliasFilter().setId(idToDelete)); 5534 } 5535 CmsRewriteAlias alias = new CmsRewriteAlias(new CmsUUID(), siteRoot, source, target, mode); 5536 List<CmsRewriteAlias> aliases = new ArrayList<CmsRewriteAlias>(); 5537 aliases.add(alias); 5538 getVfsDriver(dbc).insertRewriteAliases(dbc, aliases); 5539 CmsAliasImportResult result = new CmsAliasImportResult( 5540 CmsAliasImportStatus.aliasNew, 5541 "OK", 5542 source, 5543 target, 5544 mode); 5545 return result; 5546 } 5547 5548 /** 5549 * Creates a new user by import.<p> 5550 * 5551 * @param dbc the current database context 5552 * @param id the id of the user 5553 * @param name the new name for the user 5554 * @param password the new password for the user (already encrypted) 5555 * @param firstname the firstname of the user 5556 * @param lastname the lastname of the user 5557 * @param email the email of the user 5558 * @param flags the flags for a user (for example <code>{@link I_CmsPrincipal#FLAG_ENABLED}</code>) 5559 * @param dateCreated the creation date 5560 * @param additionalInfos the additional user infos 5561 * 5562 * @return the imported user 5563 * 5564 * @throws CmsException if something goes wrong 5565 */ 5566 public CmsUser importUser( 5567 CmsDbContext dbc, 5568 String id, 5569 String name, 5570 String password, 5571 String firstname, 5572 String lastname, 5573 String email, 5574 int flags, 5575 long dateCreated, 5576 Map<String, Object> additionalInfos) 5577 throws CmsException { 5578 5579 // no space before or after the name 5580 name = name.trim(); 5581 // check the user name 5582 String userName = CmsOrganizationalUnit.getSimpleName(name); 5583 OpenCms.getValidationHandler().checkUserName(userName); 5584 if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) { 5585 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName)); 5586 } 5587 // check the ou 5588 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 5589 5590 // check webuser ou 5591 if (ou.hasFlagWebuser() && ((flags & I_CmsPrincipal.FLAG_USER_WEBUSER) == 0)) { 5592 flags += I_CmsPrincipal.FLAG_USER_WEBUSER; 5593 } 5594 CmsUser newUser = getUserDriver(dbc).createUser( 5595 dbc, 5596 new CmsUUID(id), 5597 name, 5598 password, 5599 firstname, 5600 lastname, 5601 email, 5602 0, 5603 flags, 5604 dateCreated, 5605 additionalInfos); 5606 return newUser; 5607 } 5608 5609 /** 5610 * Increments a counter and returns its value before incrementing.<p> 5611 * 5612 * @param dbc the current database context 5613 * @param name the name of the counter which should be incremented 5614 * 5615 * @return the value of the counter 5616 * 5617 * @throws CmsException if something goes wrong 5618 */ 5619 public int incrementCounter(CmsDbContext dbc, String name) throws CmsException { 5620 5621 return getVfsDriver(dbc).incrementCounter(dbc, name); 5622 } 5623 5624 /** 5625 * Initializes the driver and sets up all required modules and connections.<p> 5626 * 5627 * @param configurationManager the configuration manager 5628 * @param dbContextFactory the db context factory 5629 * 5630 * @throws CmsException if something goes wrong 5631 * @throws Exception if something goes wrong 5632 */ 5633 public void init(CmsConfigurationManager configurationManager, I_CmsDbContextFactory dbContextFactory) 5634 throws CmsException, Exception { 5635 5636 // initialize the access-module. 5637 if (CmsLog.INIT.isInfoEnabled()) { 5638 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE4_0)); 5639 } 5640 // store local reference to the memory monitor to avoid multiple lookups through the OpenCms singelton 5641 m_monitor = OpenCms.getMemoryMonitor(); 5642 5643 CmsSystemConfiguration systemConfiguation = (CmsSystemConfiguration)configurationManager.getConfiguration( 5644 CmsSystemConfiguration.class); 5645 CmsCacheSettings settings = systemConfiguation.getCacheSettings(); 5646 5647 // initialize the key generator 5648 m_keyGenerator = (I_CmsCacheKey)Class.forName(settings.getCacheKeyGenerator()).newInstance(); 5649 5650 // initialize the HTML link validator 5651 m_htmlLinkValidator = new CmsRelationSystemValidator(this); 5652 5653 // fills the defaults if needed 5654 CmsDbContext dbc1 = dbContextFactory.getDbContext(); 5655 getUserDriver().fillDefaults(dbc1); 5656 getProjectDriver().fillDefaults(dbc1); 5657 5658 // set the driver manager in the publish engine 5659 m_publishEngine.setDriverManager(this); 5660 // create the root organizational unit if needed 5661 CmsDbContext dbc2 = dbContextFactory.getDbContext( 5662 new CmsRequestContext( 5663 readUser(dbc1, OpenCms.getDefaultUsers().getUserAdmin()), 5664 readProject(dbc1, CmsProject.ONLINE_PROJECT_ID), 5665 null, 5666 CmsSiteMatcher.DEFAULT_MATCHER, 5667 "", 5668 false, 5669 null, 5670 null, 5671 null, 5672 0, 5673 null, 5674 null, 5675 "", 5676 false)); 5677 dbc1.clear(); 5678 getUserDriver().createRootOrganizationalUnit(dbc2); 5679 dbc2.clear(); 5680 } 5681 5682 /** 5683 * Initializes the organizational unit.<p> 5684 * 5685 * @param dbc the DB context 5686 * @param ou the organizational unit 5687 */ 5688 public void initOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit ou) { 5689 5690 try { 5691 dbc.setAttribute(ATTR_INIT_OU, ou); 5692 m_userDriver.fillDefaults(dbc); 5693 } finally { 5694 dbc.removeAttribute(ATTR_INIT_OU); 5695 } 5696 } 5697 5698 /** 5699 * Checks if the specified resource is inside the current project.<p> 5700 * 5701 * The project "view" is determined by a set of path prefixes. 5702 * If the resource starts with any one of this prefixes, it is considered to 5703 * be "inside" the project.<p> 5704 * 5705 * @param dbc the current database context 5706 * @param resourcename the specified resource name (full path) 5707 * 5708 * @return <code>true</code>, if the specified resource is inside the current project 5709 */ 5710 public boolean isInsideCurrentProject(CmsDbContext dbc, String resourcename) { 5711 5712 List<String> projectResources = null; 5713 try { 5714 projectResources = readProjectResources(dbc, dbc.currentProject()); 5715 } catch (CmsException e) { 5716 if (LOG.isErrorEnabled()) { 5717 LOG.error( 5718 Messages.get().getBundle().key( 5719 Messages.LOG_CHECK_RESOURCE_INSIDE_CURRENT_PROJECT_2, 5720 resourcename, 5721 dbc.currentProject().getName()), 5722 e); 5723 } 5724 return false; 5725 } 5726 return CmsProject.isInsideProject(projectResources, resourcename); 5727 } 5728 5729 /** 5730 * Checks whether the subscription driver is available.<p> 5731 * 5732 * @return true if the subscription driver is available 5733 */ 5734 public boolean isSubscriptionDriverAvailable() { 5735 5736 return m_subscriptionDriver != null; 5737 } 5738 5739 /** 5740 * Checks if a project is the tempfile project.<p> 5741 * @param project the project to test 5742 * @return true if the project is the tempfile project 5743 */ 5744 public boolean isTempfileProject(CmsProject project) { 5745 5746 return project.getName().equals("tempFileProject"); 5747 } 5748 5749 /** 5750 * Checks if one of the resources (except the resource itself) 5751 * is a sibling in a "labeled" site folder.<p> 5752 * 5753 * This method is used when creating a new sibling 5754 * (use the <code>newResource</code> parameter & <code>action = 1</code>) 5755 * or deleting/importing a resource (call with <code>action = 2</code>).<p> 5756 * 5757 * @param dbc the current database context 5758 * @param resource the resource 5759 * @param newResource absolute path for a resource sibling which will be created 5760 * @param action the action which has to be performed (1: create VFS link, 2: all other actions) 5761 * 5762 * @return <code>true</code> if the flag should be set for the resource, otherwise <code>false</code> 5763 * 5764 * @throws CmsDataAccessException if something goes wrong 5765 */ 5766 public boolean labelResource(CmsDbContext dbc, CmsResource resource, String newResource, int action) 5767 throws CmsDataAccessException { 5768 5769 // get the list of labeled site folders from the runtime property 5770 List<String> labeledSites = OpenCms.getWorkplaceManager().getLabelSiteFolders(); 5771 5772 if (labeledSites.size() == 0) { 5773 // no labeled sites defined, just return false 5774 return false; 5775 } 5776 5777 if (action == 1) { 5778 // CASE 1: a new resource is created, check the sites 5779 if (!resource.isLabeled()) { 5780 // source isn't labeled yet, so check! 5781 boolean linkInside = false; 5782 boolean sourceInside = false; 5783 for (int i = 0; i < labeledSites.size(); i++) { 5784 String curSite = labeledSites.get(i); 5785 if (newResource.startsWith(curSite)) { 5786 // the link lies in a labeled site 5787 linkInside = true; 5788 } 5789 if (resource.getRootPath().startsWith(curSite)) { 5790 // the source lies in a labeled site 5791 sourceInside = true; 5792 } 5793 if (linkInside && sourceInside) { 5794 break; 5795 } 5796 } 5797 // return true when either source or link is in labeled site, otherwise false 5798 return (linkInside != sourceInside); 5799 } 5800 // resource is already labeled 5801 return false; 5802 5803 } else { 5804 // CASE 2: the resource will be deleted or created (import) 5805 // check if at least one of the other siblings resides inside a "labeled site" 5806 // and if at least one of the other siblings resides outside a "labeled site" 5807 boolean isInside = false; 5808 boolean isOutside = false; 5809 // check if one of the other vfs links lies in a labeled site folder 5810 List<CmsResource> siblings = getVfsDriver( 5811 dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, false); 5812 updateContextDates(dbc, siblings); 5813 Iterator<CmsResource> i = siblings.iterator(); 5814 while (i.hasNext() && (!isInside || !isOutside)) { 5815 CmsResource currentResource = i.next(); 5816 if (currentResource.equals(resource)) { 5817 // dont't check the resource itself! 5818 continue; 5819 } 5820 String curPath = currentResource.getRootPath(); 5821 boolean curInside = false; 5822 for (int k = 0; k < labeledSites.size(); k++) { 5823 if (curPath.startsWith(labeledSites.get(k))) { 5824 // the link is in the labeled site 5825 isInside = true; 5826 curInside = true; 5827 break; 5828 } 5829 } 5830 if (!curInside) { 5831 // the current link was not found in labeled site, so it is outside 5832 isOutside = true; 5833 } 5834 } 5835 // now check the new resource name if present 5836 if (newResource != null) { 5837 boolean curInside = false; 5838 for (int k = 0; k < labeledSites.size(); k++) { 5839 if (newResource.startsWith(labeledSites.get(k))) { 5840 // the new resource is in the labeled site 5841 isInside = true; 5842 curInside = true; 5843 break; 5844 } 5845 } 5846 if (!curInside) { 5847 // the new resource was not found in labeled site, so it is outside 5848 isOutside = true; 5849 } 5850 } 5851 return (isInside && isOutside); 5852 } 5853 } 5854 5855 /** 5856 * Returns the user, who had locked the resource.<p> 5857 * 5858 * A user can lock a resource, so he is the only one who can write this 5859 * resource. This methods checks, if a resource was locked. 5860 * 5861 * @param dbc the current database context 5862 * @param resource the resource 5863 * 5864 * @return the user, who had locked the resource 5865 * 5866 * @throws CmsException will be thrown, if the user has not the rights for this resource 5867 */ 5868 public CmsUser lockedBy(CmsDbContext dbc, CmsResource resource) throws CmsException { 5869 5870 return readUser(dbc, m_lockManager.getLock(dbc, resource).getEditionLock().getUserId()); 5871 } 5872 5873 /** 5874 * Locks a resource.<p> 5875 * 5876 * The <code>type</code> parameter controls what kind of lock is used.<br> 5877 * Possible values for this parameter are: <br> 5878 * <ul> 5879 * <li><code>{@link org.opencms.lock.CmsLockType#EXCLUSIVE}</code></li> 5880 * <li><code>{@link org.opencms.lock.CmsLockType#TEMPORARY}</code></li> 5881 * <li><code>{@link org.opencms.lock.CmsLockType#PUBLISH}</code></li> 5882 * </ul><p> 5883 * 5884 * @param dbc the current database context 5885 * @param resource the resource to lock 5886 * @param type type of the lock 5887 * 5888 * @throws CmsException if something goes wrong 5889 * 5890 * @see CmsObject#lockResource(String) 5891 * @see CmsObject#lockResourceTemporary(String) 5892 * @see org.opencms.file.types.I_CmsResourceType#lockResource(CmsObject, CmsSecurityManager, CmsResource, CmsLockType) 5893 */ 5894 public void lockResource(CmsDbContext dbc, CmsResource resource, CmsLockType type) throws CmsException { 5895 5896 // update the resource cache 5897 m_monitor.clearResourceCache(); 5898 5899 CmsProject project = dbc.currentProject(); 5900 5901 // add the resource to the lock dispatcher 5902 m_lockManager.addResource(dbc, resource, dbc.currentUser(), project, type); 5903 boolean changedProjectLastModified = false; 5904 if (!resource.getState().isUnchanged() && !resource.getState().isKeep()) { 5905 // update the project flag of a modified resource as "last modified inside the current project" 5906 getVfsDriver(dbc).writeLastModifiedProjectId(dbc, project, project.getUuid(), resource); 5907 changedProjectLastModified = true; 5908 } 5909 5910 // we must also clear the permission cache 5911 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION); 5912 5913 // fire resource modification event 5914 Map<String, Object> data = new HashMap<String, Object>(2); 5915 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 5916 data.put( 5917 I_CmsEventListener.KEY_CHANGE, 5918 Integer.valueOf(changedProjectLastModified ? CHANGED_PROJECT : NOTHING_CHANGED)); 5919 data.put(I_CmsEventListener.KEY_SKIPINDEX, Boolean.TRUE); 5920 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 5921 } 5922 5923 /** 5924 * Adds the given log entry to the current user's log.<p> 5925 * 5926 * This operation works only on memory, to get the log entries actually 5927 * written to DB you have to call the {@link #updateLog(CmsDbContext)} method.<p> 5928 * 5929 * @param dbc the current database context 5930 * @param logEntry the log entry to create 5931 * @param force forces the log entry to be counted, 5932 * if not only the first log entry in a transaction will be taken into account 5933 */ 5934 public void log(CmsDbContext dbc, CmsLogEntry logEntry, boolean force) { 5935 5936 if (dbc == null) { 5937 return; 5938 } 5939 // check log level 5940 if (!logEntry.getType().isActive()) { 5941 // do not log inactive entries 5942 return; 5943 } 5944 // if not forcing 5945 if (!force) { 5946 // operation already logged 5947 boolean abort = (dbc.getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null); 5948 // disabled logging from outside 5949 abort |= (dbc.getRequestContext().getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null); 5950 if (abort) { 5951 return; 5952 } 5953 } 5954 // prevent several entries for the same operation 5955 dbc.setAttribute(CmsLogEntry.ATTR_LOG_ENTRY, Boolean.TRUE); 5956 // keep it for later 5957 m_log.add(logEntry); 5958 } 5959 5960 /** 5961 * Attempts to authenticate a user into OpenCms with the given password. 5962 * 5963 * <p>The method can be used in multiple modes (see the CmsDriverManager.LoginUserMode enum): Standard mode is the mode for actually logging in a user, 5964 * while check mode merely checks the login details without firing the events normally fired during login, and without modifying the user. However, 5965 * in the case an incorrect password is given, the invalid login counter is still incremented. 5966 * 5967 * @param dbc the current database context 5968 * @param userName the name of the user to be logged in 5969 * @param password the password of the user 5970 * @param secondFactorInfo the second factor information for 2FA (may be null) 5971 * @param remoteAddress the ip address of the request 5972 * @param mode the mode to use (real login or check only) 5973 * 5974 * @return the logged in user 5975 * 5976 * @throws CmsAuthentificationException if the login was not successful 5977 * @throws CmsDataAccessException in case of errors accessing the database 5978 * @throws CmsPasswordEncryptionException in case of errors encrypting the users password 5979 */ 5980 public CmsUser loginUser( 5981 CmsDbContext dbc, 5982 String userName, 5983 String password, 5984 CmsSecondFactorInfo secondFactorInfo, 5985 String remoteAddress, 5986 LoginUserMode mode) 5987 throws CmsAuthentificationException, CmsDataAccessException, CmsPasswordEncryptionException { 5988 5989 if (CmsStringUtil.isEmptyOrWhitespaceOnly(password)) { 5990 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, userName)); 5991 } 5992 CmsUser newUser; 5993 CmsUser userCopy; 5994 try { 5995 // read the user from the driver to avoid the cache 5996 newUser = getUserDriver(dbc).readUser(dbc, userName, password, remoteAddress); 5997 userCopy = newUser.clone(); 5998 userName = newUser.getName(); 5999 6000 } catch (CmsDbEntryNotFoundException e) { 6001 // this indicates that the username / password combination does not exist 6002 // any other exception indicates database issues, these are not catched here 6003 6004 // check if a user with this name exists at all 6005 CmsUser user = null; 6006 try { 6007 user = readUser(dbc, userName); 6008 userName = user.getName(); 6009 } catch (CmsDataAccessException e2) { 6010 // apparently this user does not exist in the database 6011 } 6012 6013 if (user != null) { 6014 if (dbc.currentUser().isGuestUser()) { 6015 // add an invalid login attempt for this user to the storage 6016 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 6017 } 6018 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6019 throw new CmsAuthentificationException( 6020 org.opencms.security.Messages.get().container( 6021 org.opencms.security.Messages.ERR_LOGIN_FAILED_2, 6022 userName, 6023 remoteAddress), 6024 e); 6025 } else { 6026 String userOu = CmsOrganizationalUnit.getParentFqn(userName); 6027 if (userOu != null) { 6028 String parentOu = CmsOrganizationalUnit.getParentFqn(userOu); 6029 if (parentOu != null) { 6030 // try a higher level ou 6031 String uName = CmsOrganizationalUnit.getSimpleName(userName); 6032 return loginUser(dbc, parentOu + uName, password, secondFactorInfo, remoteAddress, mode); 6033 } 6034 } 6035 throw new CmsAuthentificationException( 6036 org.opencms.security.Messages.get().container( 6037 org.opencms.security.Messages.ERR_LOGIN_FAILED_NO_USER_2, 6038 userName, 6039 remoteAddress), 6040 e); 6041 } 6042 } 6043 // check if the "enabled" flag is set for the user 6044 if (!newUser.isEnabled()) { 6045 // user is disabled, throw a securiy exception 6046 throw new CmsAuthentificationException( 6047 org.opencms.security.Messages.get().container( 6048 org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2, 6049 userName, 6050 remoteAddress)); 6051 } 6052 6053 if (mode == LoginUserMode.standard) { 6054 CmsTwoFactorAuthenticationHandler handler = OpenCms.getTwoFactorAuthenticationHandler(); 6055 if (handler.needsTwoFactorAuthentication(newUser)) { 6056 // note that password check must already have been successful at this stage 6057 6058 if (handler.hasSecondFactor(newUser)) { 6059 if (!handler.verifySecondFactor(newUser, secondFactorInfo)) { 6060 if (dbc.currentUser().isGuestUser()) { 6061 // add an invalid login attempt for this user to the storage 6062 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 6063 } 6064 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6065 throw new CmsAuthentificationException( 6066 org.opencms.security.Messages.get().container( 6067 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 6068 userName)); 6069 } 6070 } else { 6071 try { 6072 if (handler.setUpAndVerifySecondFactor(newUser, secondFactorInfo)) { 6073 LOG.info("Second factor setup successful for user " + newUser.getName()); 6074 } else { 6075 if (dbc.currentUser().isGuestUser()) { 6076 // add an invalid login attempt for this user to the storage 6077 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 6078 } 6079 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6080 throw new CmsAuthentificationException( 6081 org.opencms.security.Messages.get().container( 6082 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 6083 userName)); 6084 } 6085 } catch (CmsSecondFactorSetupException e) { 6086 throw new CmsAuthentificationException( 6087 org.opencms.security.Messages.get().container( 6088 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 6089 userName), 6090 e); 6091 } 6092 } 6093 } 6094 } 6095 if (dbc.currentUser().isGuestUser()) { 6096 // check if this account is temporarily disabled because of too many invalid login attempts 6097 // this will throw an exception if the test fails 6098 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6099 if (mode == LoginUserMode.standard) { 6100 // test successful, remove all previous invalid login attempts for this user from the storage 6101 OpenCms.getLoginManager().removeInvalidLogins(userName, remoteAddress); 6102 } 6103 } 6104 6105 if (!m_securityManager.hasRole( 6106 dbc, 6107 newUser, 6108 CmsRole.ADMINISTRATOR.forOrgUnit(dbc.getRequestContext().getOuFqn()))) { 6109 // new user is not Administrator, check if login is currently allowed 6110 OpenCms.getLoginManager().checkLoginAllowed(); 6111 } 6112 6113 if (mode == LoginUserMode.standard) { 6114 6115 newUser.setLastlogin(System.currentTimeMillis()); 6116 m_monitor.clearUserCache(newUser); 6117 6118 // write the changed user object back to the user driver 6119 Map<String, Object> additionalInfosForRepositories = OpenCms.getRepositoryManager().getAdditionalInfoForLogin( 6120 newUser.getName(), 6121 password); 6122 boolean requiresAddInfoUpdate = false; 6123 6124 // check for changes 6125 for (Entry<String, Object> entry : additionalInfosForRepositories.entrySet()) { 6126 Object value = entry.getValue(); 6127 Object current = newUser.getAdditionalInfo(entry.getKey()); 6128 if (((value == null) && (current != null)) || ((value != null) && !value.equals(current))) { 6129 requiresAddInfoUpdate = true; 6130 break; 6131 } 6132 } 6133 if (requiresAddInfoUpdate) { 6134 newUser.getAdditionalInfo().putAll(additionalInfosForRepositories); 6135 } 6136 String lastPasswordChange = (String)newUser.getAdditionalInfo( 6137 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE); 6138 if (lastPasswordChange == null) { 6139 requiresAddInfoUpdate = true; 6140 newUser.getAdditionalInfo().put( 6141 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 6142 "" + System.currentTimeMillis()); 6143 } 6144 if (!requiresAddInfoUpdate) { 6145 dbc.setAttribute(ATTRIBUTE_LOGIN, newUser.getName()); 6146 } 6147 6148 if (mode == LoginUserMode.standard) { 6149 OpenCms.getTwoFactorAuthenticationHandler().trackUserChange(dbc.getRequestContext(), userCopy, newUser); 6150 getUserDriver(dbc).writeUser(dbc, newUser); 6151 } 6152 int changes = CmsUser.FLAG_LAST_LOGIN; 6153 6154 // check if we need to update the password 6155 if (!OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), false) 6156 && OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), true)) { 6157 // the password does not check with the current hash algorithm but with the fall back, update the password 6158 getUserDriver(dbc).writePassword(dbc, userName, password, password); 6159 changes = changes | CmsUser.FLAG_CORE_DATA; 6160 } 6161 6162 // update cache 6163 m_monitor.cacheUser(newUser); 6164 6165 // invalidate all user dependent caches 6166 m_monitor.flushCache( 6167 CmsMemoryMonitor.CacheType.ACL, 6168 CmsMemoryMonitor.CacheType.GROUP, 6169 CmsMemoryMonitor.CacheType.ORG_UNIT, 6170 CmsMemoryMonitor.CacheType.USER_LIST, 6171 CmsMemoryMonitor.CacheType.PERMISSION, 6172 CmsMemoryMonitor.CacheType.RESOURCE_LIST); 6173 6174 // fire user modified event 6175 Map<String, Object> eventData = new HashMap<String, Object>(); 6176 eventData.put(I_CmsEventListener.KEY_USER_ID, newUser.getId().toString()); 6177 eventData.put(I_CmsEventListener.KEY_USER_NAME, newUser.getName()); 6178 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 6179 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(changes)); 6180 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 6181 } 6182 6183 // return the user object read from the driver 6184 return newUser.clone(); 6185 } 6186 6187 /** 6188 * Lookup and read the user or group with the given UUID.<p> 6189 * 6190 * @param dbc the current database context 6191 * @param principalId the UUID of the principal to lookup 6192 * 6193 * @return the principal (group or user) if found, otherwise <code>null</code> 6194 */ 6195 public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, CmsUUID principalId) { 6196 6197 try { 6198 CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalId); 6199 if (group != null) { 6200 return group; 6201 } 6202 } catch (Exception e) { 6203 // ignore this exception 6204 } 6205 6206 try { 6207 CmsUser user = readUser(dbc, principalId); 6208 if (user != null) { 6209 return user; 6210 } 6211 } catch (Exception e) { 6212 // ignore this exception 6213 } 6214 6215 return null; 6216 } 6217 6218 /** 6219 * Lookup and read the user or group with the given name.<p> 6220 * 6221 * @param dbc the current database context 6222 * @param principalName the name of the principal to lookup 6223 * 6224 * @return the principal (group or user) if found, otherwise <code>null</code> 6225 */ 6226 public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, String principalName) { 6227 6228 try { 6229 CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalName); 6230 if (group != null) { 6231 return group; 6232 } 6233 } catch (Exception e) { 6234 // ignore this exception 6235 } 6236 6237 try { 6238 CmsUser user = readUser(dbc, principalName); 6239 if (user != null) { 6240 return user; 6241 } 6242 } catch (Exception e) { 6243 // ignore this exception 6244 } 6245 6246 return null; 6247 } 6248 6249 /** 6250 * Mark the given resource as visited by the user.<p> 6251 * 6252 * @param dbc the database context 6253 * @param poolName the name of the database pool to use 6254 * @param resource the resource to mark as visited 6255 * @param user the user that visited the resource 6256 * 6257 * @throws CmsException if something goes wrong 6258 */ 6259 public void markResourceAsVisitedBy(CmsDbContext dbc, String poolName, CmsResource resource, CmsUser user) 6260 throws CmsException { 6261 6262 getSubscriptionDriver().markResourceAsVisitedBy(dbc, poolName, resource, user); 6263 } 6264 6265 /** 6266 * Moves a resource.<p> 6267 * 6268 * You must ensure that the parent of the destination path is an absolute, valid and 6269 * existing VFS path. Relative paths from the source are not supported.<p> 6270 * 6271 * The moved resource will always be locked to the current user 6272 * after the move operation.<p> 6273 * 6274 * In case the target resource already exists, it will be overwritten with the 6275 * source resource if possible.<p> 6276 * 6277 * @param dbc the current database context 6278 * @param source the resource to move 6279 * @param destination the name of the move destination with complete path 6280 * @param internal if set nothing more than the path is modified 6281 * 6282 * @throws CmsException if something goes wrong 6283 * 6284 * @see CmsSecurityManager#moveResource(CmsRequestContext, CmsResource, String) 6285 */ 6286 public void moveResource(CmsDbContext dbc, CmsResource source, String destination, boolean internal) 6287 throws CmsException { 6288 6289 CmsFolder destinationFolder = readFolder(dbc, CmsResource.getParentFolder(destination), CmsResourceFilter.ALL); 6290 m_securityManager.checkPermissions( 6291 dbc, 6292 destinationFolder, 6293 CmsPermissionSet.ACCESS_WRITE, 6294 false, 6295 CmsResourceFilter.ALL); 6296 6297 if (source.isFolder()) { 6298 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 6299 } 6300 getVfsDriver(dbc).moveResource(dbc, dbc.getRequestContext().getCurrentProject().getUuid(), source, destination); 6301 6302 if (!internal) { 6303 CmsResourceState newState = CmsResource.STATE_CHANGED; 6304 if (source.getState().isNew()) { 6305 newState = CmsResource.STATE_NEW; 6306 } else if (source.getState().isDeleted()) { 6307 newState = CmsResource.STATE_DELETED; 6308 } 6309 source.setState(newState); 6310 // safe since this operation always uses the ids instead of the resource path 6311 getVfsDriver(dbc).writeResourceState( 6312 dbc, 6313 dbc.currentProject(), 6314 source, 6315 CmsDriverManager.UPDATE_STRUCTURE_STATE, 6316 false); 6317 // log it 6318 log( 6319 dbc, 6320 new CmsLogEntry( 6321 dbc, 6322 source.getStructureId(), 6323 CmsLogEntryType.RESOURCE_MOVED, 6324 new String[] {source.getRootPath(), destination}), 6325 false); 6326 } 6327 6328 CmsResource destRes = readResource(dbc, destination, CmsResourceFilter.ALL); 6329 // move lock 6330 m_lockManager.moveResource(source.getRootPath(), destRes.getRootPath()); 6331 6332 // flush all relevant caches 6333 m_monitor.clearAccessControlListCache(); 6334 m_monitor.flushCache( 6335 CmsMemoryMonitor.CacheType.PROPERTY, 6336 CmsMemoryMonitor.CacheType.PROPERTY_LIST, 6337 CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 6338 6339 List<CmsResource> resources = new ArrayList<CmsResource>(4); 6340 // source 6341 resources.add(source); 6342 try { 6343 resources.add(readFolder(dbc, CmsResource.getParentFolder(source.getRootPath()), CmsResourceFilter.ALL)); 6344 } catch (Exception e) { 6345 if (LOG.isDebugEnabled()) { 6346 LOG.debug(e.getLocalizedMessage(), e); 6347 } 6348 } 6349 // destination 6350 resources.add(destRes); 6351 resources.add(destinationFolder); 6352 6353 Map<String, Object> eventData = new HashMap<String, Object>(); 6354 eventData.put(I_CmsEventListener.KEY_RESOURCES, resources); 6355 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 6356 6357 // fire the events 6358 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MOVED, eventData)); 6359 } 6360 6361 /** 6362 * Moves a resource to the "lost and found" folder.<p> 6363 * 6364 * The method can also be used to check get the name of a resource 6365 * in the "lost and found" folder only without actually moving the 6366 * the resource. To do this, the <code>returnNameOnly</code> flag 6367 * must be set to <code>true</code>.<p> 6368 * 6369 * @param dbc the current database context 6370 * @param resource the resource to apply this operation to 6371 * @param returnNameOnly if <code>true</code>, only the name of the resource in the "lost and found" 6372 * folder is returned, the move operation is not really performed 6373 * 6374 * @return the name of the resource inside the "lost and found" folder 6375 * 6376 * @throws CmsException if something goes wrong 6377 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 6378 * 6379 * @see CmsObject#moveToLostAndFound(String) 6380 * @see CmsObject#getLostAndFoundName(String) 6381 */ 6382 public String moveToLostAndFound(CmsDbContext dbc, CmsResource resource, boolean returnNameOnly) 6383 throws CmsException, CmsIllegalArgumentException { 6384 6385 String resourcename = dbc.removeSiteRoot(resource.getRootPath()); 6386 6387 String siteRoot = dbc.getRequestContext().getSiteRoot(); 6388 dbc.getRequestContext().setSiteRoot(""); 6389 String destination = CmsDriverManager.LOST_AND_FOUND_FOLDER + resourcename; 6390 // create the required folders if necessary 6391 try { 6392 // collect all folders... 6393 String folderPath = CmsResource.getParentFolder(destination); 6394 folderPath = folderPath.substring(1, folderPath.length() - 1); // cut out leading and trailing '/' 6395 Iterator<String> folders = CmsStringUtil.splitAsList(folderPath, '/').iterator(); 6396 // ...now create them.... 6397 folderPath = "/"; 6398 while (folders.hasNext()) { 6399 folderPath += folders.next().toString() + "/"; 6400 try { 6401 readFolder(dbc, folderPath, CmsResourceFilter.IGNORE_EXPIRATION); 6402 } catch (Exception e1) { 6403 if (returnNameOnly) { 6404 // we can use the original name without risk, and we do not need to recreate the parent folders 6405 break; 6406 } 6407 // the folder is not existing, so create it 6408 createResource( 6409 dbc, 6410 folderPath, 6411 CmsResourceTypeFolder.RESOURCE_TYPE_ID, 6412 null, 6413 new ArrayList<CmsProperty>()); 6414 } 6415 } 6416 // check if this resource name does already exist 6417 // if so add a postfix to the name 6418 String des = destination; 6419 int postfix = 1; 6420 boolean found = true; 6421 while (found) { 6422 try { 6423 // try to read the file..... 6424 found = true; 6425 readResource(dbc, des, CmsResourceFilter.ALL); 6426 // ....it's there, so add a postfix and try again 6427 String path = destination.substring(0, destination.lastIndexOf('/') + 1); 6428 String filename = destination.substring(destination.lastIndexOf('/') + 1, destination.length()); 6429 6430 des = path; 6431 6432 if (filename.lastIndexOf('.') > 0) { 6433 des += filename.substring(0, filename.lastIndexOf('.')); 6434 } else { 6435 des += filename; 6436 } 6437 des += "_" + postfix; 6438 if (filename.lastIndexOf('.') > 0) { 6439 des += filename.substring(filename.lastIndexOf('.'), filename.length()); 6440 } 6441 postfix++; 6442 } catch (CmsException e3) { 6443 // the file does not exist, so we can use this filename 6444 found = false; 6445 } 6446 } 6447 destination = des; 6448 6449 if (!returnNameOnly) { 6450 // do not use the move semantic here! to prevent links pointing to the lost & found folder 6451 copyResource(dbc, resource, destination, CmsResource.COPY_AS_SIBLING); 6452 deleteResource(dbc, resource, CmsResource.DELETE_PRESERVE_SIBLINGS); 6453 } 6454 } catch (CmsException e2) { 6455 throw e2; 6456 } finally { 6457 // set the site root to the old value again 6458 dbc.getRequestContext().setSiteRoot(siteRoot); 6459 } 6460 return destination; 6461 } 6462 6463 /** 6464 * Gets a new driver instance.<p> 6465 * 6466 * @param dbc the database context 6467 * @param configurationManager the configuration manager 6468 * @param driverName the driver name 6469 * @param successiveDrivers the list of successive drivers 6470 * 6471 * @return the driver object 6472 * @throws CmsInitException if the selected driver could not be initialized 6473 */ 6474 public Object newDriverInstance( 6475 CmsDbContext dbc, 6476 CmsConfigurationManager configurationManager, 6477 String driverName, 6478 List<String> successiveDrivers) 6479 throws CmsInitException { 6480 6481 Class<?> driverClass = null; 6482 I_CmsDriver driver = null; 6483 6484 try { 6485 // try to get the class 6486 driverClass = Class.forName(driverName); 6487 if (CmsLog.INIT.isInfoEnabled()) { 6488 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName)); 6489 } 6490 6491 // try to create a instance 6492 driver = (I_CmsDriver)driverClass.newInstance(); 6493 if (CmsLog.INIT.isInfoEnabled()) { 6494 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName)); 6495 } 6496 6497 // invoke the init-method of this access class 6498 driver.init(dbc, configurationManager, successiveDrivers, this); 6499 if (CmsLog.INIT.isInfoEnabled()) { 6500 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_0)); 6501 } 6502 6503 } catch (Throwable t) { 6504 CmsMessageContainer message = Messages.get().container( 6505 Messages.ERR_ERROR_INITIALIZING_DRIVER_1, 6506 driverName); 6507 if (LOG.isErrorEnabled()) { 6508 LOG.error(message.key(), t); 6509 } 6510 throw new CmsInitException(message, t); 6511 } 6512 6513 return driver; 6514 } 6515 6516 /** 6517 * Method to create a new instance of a driver.<p> 6518 * 6519 * @param configuration the configurations from the propertyfile 6520 * @param driverName the class name of the driver 6521 * @param driverPoolUrl the pool url for the driver 6522 * @return an initialized instance of the driver 6523 * @throws CmsException if something goes wrong 6524 */ 6525 public Object newDriverInstance(CmsParameterConfiguration configuration, String driverName, String driverPoolUrl) 6526 throws CmsException { 6527 6528 Class<?>[] initParamClasses = {CmsParameterConfiguration.class, String.class, CmsDriverManager.class}; 6529 Object[] initParams = {configuration, driverPoolUrl, this}; 6530 6531 Class<?> driverClass = null; 6532 Object driver = null; 6533 6534 try { 6535 // try to get the class 6536 driverClass = Class.forName(driverName); 6537 if (CmsLog.INIT.isInfoEnabled()) { 6538 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName)); 6539 } 6540 6541 // try to create a instance 6542 driver = driverClass.newInstance(); 6543 if (CmsLog.INIT.isInfoEnabled()) { 6544 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName)); 6545 } 6546 6547 // invoke the init-method of this access class 6548 driver.getClass().getMethod("init", initParamClasses).invoke(driver, initParams); 6549 if (CmsLog.INIT.isInfoEnabled()) { 6550 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_1, driverPoolUrl)); 6551 } 6552 6553 } catch (Exception exc) { 6554 6555 CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_DRIVER_MANAGER_1); 6556 if (LOG.isFatalEnabled()) { 6557 LOG.fatal(message.key(), exc); 6558 } 6559 throw new CmsDbException(message, exc); 6560 6561 } 6562 6563 return driver; 6564 } 6565 6566 /** 6567 * Method to create a new instance of a pool.<p> 6568 * 6569 * @param configuration the configurations from the propertyfile 6570 * @param poolName the configuration name of the pool 6571 * 6572 * @throws CmsInitException if the pools could not be initialized 6573 */ 6574 public void newPoolInstance(CmsParameterConfiguration configuration, String poolName) throws CmsInitException { 6575 6576 CmsDbPoolV11 pool; 6577 6578 try { 6579 pool = new CmsDbPoolV11(configuration, poolName); 6580 } catch (Exception e) { 6581 6582 CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_CONN_POOL_1, poolName); 6583 if (LOG.isErrorEnabled()) { 6584 LOG.error(message.key(), e); 6585 } 6586 throw new CmsInitException(message, e); 6587 } 6588 addPool(pool); 6589 } 6590 6591 /** 6592 * Publishes the given publish job.<p> 6593 * 6594 * @param cms the cms context 6595 * @param dbc the db context 6596 * @param publishList the list of resources to publish 6597 * @param report the report to write to 6598 * 6599 * @throws CmsException if something goes wrong 6600 */ 6601 public void publishJob(CmsObject cms, CmsDbContext dbc, CmsPublishList publishList, I_CmsReport report) 6602 throws CmsException { 6603 6604 try { 6605 // check state and lock 6606 List<CmsResource> allResources = new ArrayList<CmsResource>(publishList.getFolderList()); 6607 allResources.addAll(publishList.getDeletedFolderList()); 6608 allResources.addAll(publishList.getFileList()); 6609 Iterator<CmsResource> itResources = allResources.iterator(); 6610 while (itResources.hasNext()) { 6611 CmsResource resource = itResources.next(); 6612 try { 6613 resource = readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL); 6614 } catch (CmsVfsResourceNotFoundException e) { 6615 continue; 6616 } 6617 if (resource.getState().isUnchanged()) { 6618 // remove files that were published by a concurrent job 6619 if (LOG.isDebugEnabled()) { 6620 LOG.debug( 6621 Messages.get().getBundle().key( 6622 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6623 dbc.removeSiteRoot(resource.getRootPath()))); 6624 } 6625 publishList.remove(resource); 6626 unlockResource(dbc, resource, true, true); 6627 continue; 6628 } 6629 if (!CmsModificationContext.isInOnlineFolder(resource.getRootPath())) { 6630 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6631 if (!lock.getSystemLock().isPublish()) { 6632 // remove files that are not locked for publishing 6633 if (LOG.isDebugEnabled()) { 6634 LOG.debug( 6635 Messages.get().getBundle().key( 6636 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6637 dbc.removeSiteRoot(resource.getRootPath()))); 6638 } 6639 publishList.remove(resource); 6640 continue; 6641 } 6642 } 6643 } 6644 6645 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 6646 6647 // clear the cache 6648 m_monitor.clearCacheForPublishing(); 6649 6650 int publishTag = getNextPublishTag(dbc); 6651 getProjectDriver(dbc).publishProject(dbc, report, onlineProject, publishList, publishTag); 6652 6653 // iterate the initialized module action instances 6654 Iterator<String> i = OpenCms.getModuleManager().getModuleNames().iterator(); 6655 while (i.hasNext()) { 6656 CmsModule module = OpenCms.getModuleManager().getModule(i.next()); 6657 if ((module != null) && (module.getActionInstance() != null)) { 6658 module.getActionInstance().publishProject(cms, publishList, publishTag, report); 6659 } 6660 } 6661 6662 boolean temporaryProject = (cms.getRequestContext().getCurrentProject().getType() == CmsProject.PROJECT_TYPE_TEMPORARY); 6663 // the project was stored in the history tables for history 6664 // it will be deleted if the project_flag is PROJECT_TYPE_TEMPORARY 6665 if ((temporaryProject) && (!publishList.isDirectPublish())) { 6666 try { 6667 getProjectDriver(dbc).deleteProject(dbc, dbc.currentProject()); 6668 } catch (CmsException e) { 6669 LOG.error( 6670 Messages.get().getBundle().key( 6671 Messages.LOG_DELETE_TEMP_PROJECT_FAILED_1, 6672 cms.getRequestContext().getCurrentProject().getName())); 6673 } 6674 // if project was temporary set context to online project 6675 cms.getRequestContext().setCurrentProject(onlineProject); 6676 } 6677 } finally { 6678 // clear the cache again 6679 m_monitor.clearCacheForPublishing(); 6680 } 6681 } 6682 6683 /** 6684 * Publishes the resources of a specified publish list.<p> 6685 * 6686 * @param cms the current request context 6687 * @param dbc the current database context 6688 * @param publishList a publish list 6689 * @param report an instance of <code>{@link I_CmsReport}</code> to print messages 6690 * 6691 * @throws CmsException if something goes wrong 6692 * 6693 * @see #fillPublishList(CmsDbContext, CmsPublishList) 6694 */ 6695 public synchronized void publishProject( 6696 CmsObject cms, 6697 CmsDbContext dbc, 6698 CmsPublishList publishList, 6699 I_CmsReport report) 6700 throws CmsException { 6701 6702 // check the parent folders 6703 checkParentFolders(dbc, publishList); 6704 ensureSubResourcesOfMovedFoldersPublished(cms, dbc, publishList); 6705 OpenCms.getPublishManager().getPublishListVerifier().checkPublishList(publishList); 6706 6707 try { 6708 // fire an event that a project is to be published 6709 Map<String, Object> eventData = new HashMap<String, Object>(); 6710 eventData.put(I_CmsEventListener.KEY_REPORT, report); 6711 eventData.put(I_CmsEventListener.KEY_PUBLISHLIST, publishList); 6712 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 6713 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 6714 CmsEvent beforePublishEvent = new CmsEvent(I_CmsEventListener.EVENT_BEFORE_PUBLISH_PROJECT, eventData); 6715 OpenCms.fireCmsEvent(beforePublishEvent); 6716 } catch (Throwable t) { 6717 if (report != null) { 6718 report.addError(t); 6719 report.println(t); 6720 } 6721 if (LOG.isErrorEnabled()) { 6722 LOG.error(t.getLocalizedMessage(), t); 6723 } 6724 } 6725 6726 // lock all resources with the special publish lock 6727 Iterator<CmsResource> itResources = new ArrayList<CmsResource>(publishList.getAllResources()).iterator(); 6728 while (itResources.hasNext()) { 6729 CmsResource resource = itResources.next(); 6730 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6731 if (lock.getSystemLock().isUnlocked() && lock.isLockableBy(dbc.currentUser())) { 6732 if (getLock(dbc, resource).getEditionLock().isNullLock()) { 6733 lockResource(dbc, resource, CmsLockType.PUBLISH); 6734 } else { 6735 changeLock(dbc, resource, CmsLockType.PUBLISH); 6736 } 6737 } else if (lock.getSystemLock().isPublish()) { 6738 if (LOG.isWarnEnabled()) { 6739 LOG.warn( 6740 Messages.get().getBundle().key( 6741 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6742 dbc.removeSiteRoot(resource.getRootPath()))); 6743 } 6744 // remove files that are already waiting to be published 6745 publishList.remove(resource); 6746 continue; 6747 } else { 6748 // this is needed to fix TestPublishIsssues#testPublishScenarioE 6749 changeLock(dbc, resource, CmsLockType.PUBLISH); 6750 } 6751 // now re-check the lock state 6752 lock = m_lockManager.getLock(dbc, resource, false); 6753 if (!lock.getSystemLock().isPublish()) { 6754 if (report != null) { 6755 report.println( 6756 Messages.get().container( 6757 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6758 dbc.removeSiteRoot(resource.getRootPath())), 6759 I_CmsReport.FORMAT_WARNING); 6760 } 6761 if (LOG.isWarnEnabled()) { 6762 LOG.warn( 6763 Messages.get().getBundle().key( 6764 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6765 dbc.removeSiteRoot(resource.getRootPath()))); 6766 } 6767 // remove files that could not be locked 6768 publishList.remove(resource); 6769 } 6770 } 6771 6772 // enqueue the publish job 6773 CmsException enqueueException = null; 6774 try { 6775 m_publishEngine.enqueuePublishJob(cms, publishList, report); 6776 } catch (CmsException exc) { 6777 enqueueException = exc; 6778 } 6779 6780 // if an exception was raised, remove the publish locks 6781 // and throw the exception again 6782 if (enqueueException != null) { 6783 itResources = publishList.getAllResources().iterator(); 6784 while (itResources.hasNext()) { 6785 CmsResource resource = itResources.next(); 6786 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6787 if (lock.getSystemLock().isPublish() 6788 && lock.getSystemLock().isOwnedInProjectBy( 6789 cms.getRequestContext().getCurrentUser(), 6790 cms.getRequestContext().getCurrentProject())) { 6791 unlockResource(dbc, resource, true, true); 6792 } 6793 } 6794 6795 throw enqueueException; 6796 } 6797 } 6798 6799 /** 6800 * Transfers the new URL name mappings (if any) for a given resource to the online project.<p> 6801 * 6802 * @param dbc the current database context 6803 * @param res the resource whose new URL name mappings should be transferred to the online project 6804 * 6805 * @throws CmsDataAccessException if something goes wrong 6806 */ 6807 public void publishUrlNameMapping(CmsDbContext dbc, CmsResource res) throws CmsDataAccessException { 6808 6809 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 6810 6811 if (res.getState().isDeleted()) { 6812 // remove both offline and online mappings 6813 CmsUrlNameMappingFilter idFilter = CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()); 6814 vfsDriver.deleteUrlNameMappingEntries(dbc, true, idFilter); 6815 vfsDriver.deleteUrlNameMappingEntries(dbc, false, idFilter); 6816 } else { 6817 // copy the new entries to the online table 6818 List<CmsUrlNameMappingEntry> entries = vfsDriver.readUrlNameMappingEntries( 6819 dbc, 6820 false, 6821 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates( 6822 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 6823 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 6824 6825 boolean isReplaceOnPublish = false; 6826 for (CmsUrlNameMappingEntry entry : entries) { 6827 isReplaceOnPublish |= entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH; 6828 } 6829 6830 if (!entries.isEmpty()) { 6831 6832 long now = System.currentTimeMillis(); 6833 if (isReplaceOnPublish) { 6834 vfsDriver.deleteUrlNameMappingEntries( 6835 dbc, 6836 true, 6837 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId())); 6838 vfsDriver.deleteUrlNameMappingEntries( 6839 dbc, 6840 false, 6841 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId())); 6842 } 6843 6844 for (CmsUrlNameMappingEntry entry : entries) { 6845 CmsUrlNameMappingFilter nameFilter = CmsUrlNameMappingFilter.ALL.filterName(entry.getName()); 6846 if (!isReplaceOnPublish) { // we already handled the other case above 6847 vfsDriver.deleteUrlNameMappingEntries(dbc, true, nameFilter); 6848 vfsDriver.deleteUrlNameMappingEntries(dbc, false, nameFilter); 6849 } 6850 } 6851 for (CmsUrlNameMappingEntry entry : entries) { 6852 CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry( 6853 entry.getName(), 6854 entry.getStructureId(), 6855 entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_NEW 6856 ? CmsUrlNameMappingEntry.MAPPING_STATUS_PUBLISHED 6857 : CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH_PUBLISHED, 6858 now, 6859 entry.getLocale()); 6860 vfsDriver.addUrlNameMappingEntry(dbc, true, newEntry); 6861 vfsDriver.addUrlNameMappingEntry(dbc, false, newEntry); 6862 } 6863 } 6864 } 6865 } 6866 6867 /** 6868 * Reads an access control entry from the cms.<p> 6869 * 6870 * The access control entries of a resource are readable by everyone. 6871 * 6872 * @param dbc the current database context 6873 * @param resource the resource 6874 * @param principal the id of a group or a user any other entity 6875 * @return an access control entry that defines the permissions of the entity for the given resource 6876 * @throws CmsException if something goes wrong 6877 */ 6878 public CmsAccessControlEntry readAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) 6879 throws CmsException { 6880 6881 return getUserDriver( 6882 dbc).readAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal); 6883 } 6884 6885 /** 6886 * Finds the alias with a given path.<p> 6887 * 6888 * If no alias is found, null is returned.<p> 6889 * 6890 * @param dbc the current database context 6891 * @param project the current project 6892 * @param siteRoot the site root 6893 * @param path the path of the alias 6894 * 6895 * @return the alias with the given path 6896 * 6897 * @throws CmsException if something goes wrong 6898 */ 6899 6900 public CmsAlias readAliasByPath(CmsDbContext dbc, CmsProject project, String siteRoot, String path) 6901 throws CmsException { 6902 6903 List<CmsAlias> aliases = getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(siteRoot, path, null)); 6904 if (aliases.isEmpty()) { 6905 return null; 6906 } else { 6907 return aliases.get(0); 6908 } 6909 } 6910 6911 /** 6912 * Reads the aliases for a given site root.<p> 6913 * 6914 * @param dbc the current database context 6915 * @param currentProject the current project 6916 * @param siteRoot the site root 6917 * 6918 * @return the list of aliases for the given site root 6919 * 6920 * @throws CmsException if something goes wrong 6921 */ 6922 public List<CmsAlias> readAliasesBySite(CmsDbContext dbc, CmsProject currentProject, String siteRoot) 6923 throws CmsException { 6924 6925 return getVfsDriver(dbc).readAliases(dbc, currentProject, new CmsAliasFilter(siteRoot, null, null)); 6926 } 6927 6928 /** 6929 * Reads the aliases which point to a given structure id.<p> 6930 * 6931 * @param dbc the current database context 6932 * @param project the current project 6933 * @param structureId the structure id for which we want to read the aliases 6934 * 6935 * @return the list of aliases pointing to the structure id 6936 * @throws CmsException if something goes wrong 6937 */ 6938 public List<CmsAlias> readAliasesByStructureId(CmsDbContext dbc, CmsProject project, CmsUUID structureId) 6939 throws CmsException { 6940 6941 return getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(null, null, structureId)); 6942 } 6943 6944 /** 6945 * Reads all versions of the given resource.<br> 6946 * 6947 * This method returns a list with the history of the given resource, i.e. 6948 * the historical resource entries, independent of the project they were attached to.<br> 6949 * 6950 * The reading excludes the file content.<p> 6951 * 6952 * @param dbc the current database context 6953 * @param resource the resource to read the history for 6954 * 6955 * @return a list of file headers, as <code>{@link I_CmsHistoryResource}</code> objects 6956 * 6957 * @throws CmsException if something goes wrong 6958 */ 6959 public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsResource resource) 6960 throws CmsException { 6961 6962 // read the historical resources 6963 List<I_CmsHistoryResource> versions = getHistoryDriver(dbc).readAllAvailableVersions( 6964 dbc, 6965 resource.getStructureId()); 6966 if ((versions.size() > OpenCms.getSystemInfo().getHistoryVersions()) 6967 && (OpenCms.getSystemInfo().getHistoryVersions() > -1)) { 6968 return versions.subList(0, OpenCms.getSystemInfo().getHistoryVersions()); 6969 } 6970 return versions; 6971 } 6972 6973 /** 6974 * Reads all property definitions for the given mapping type.<p> 6975 * 6976 * @param dbc the current database context 6977 * 6978 * @return a list with the <code>{@link CmsPropertyDefinition}</code> objects (may be empty) 6979 * 6980 * @throws CmsException if something goes wrong 6981 */ 6982 public List<CmsPropertyDefinition> readAllPropertyDefinitions(CmsDbContext dbc) throws CmsException { 6983 6984 List<CmsPropertyDefinition> result = getVfsDriver(dbc).readPropertyDefinitions( 6985 dbc, 6986 dbc.currentProject().getUuid()); 6987 Collections.sort(result); 6988 return result; 6989 } 6990 6991 /** 6992 * Returns all resources subscribed by the given user or group.<p> 6993 * 6994 * @param dbc the database context 6995 * @param poolName the name of the database pool to use 6996 * @param principal the principal to read the subscribed resources 6997 * 6998 * @return all resources subscribed by the given user or group 6999 * 7000 * @throws CmsException if something goes wrong 7001 */ 7002 public List<CmsResource> readAllSubscribedResources(CmsDbContext dbc, String poolName, CmsPrincipal principal) 7003 throws CmsException { 7004 7005 List<CmsResource> result = getSubscriptionDriver().readAllSubscribedResources(dbc, poolName, principal); 7006 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 7007 return result; 7008 } 7009 7010 /** 7011 * Selects the best url name for a given resource and locale.<p> 7012 * 7013 * @param dbc the database context 7014 * @param id the resource's structure id 7015 * @param locale the requested locale 7016 * @param defaultLocales the default locales to use if the locale isn't available 7017 * 7018 * @return the URL name which was found 7019 * 7020 * @throws CmsDataAccessException if the database operation failed 7021 */ 7022 public String readBestUrlName(CmsDbContext dbc, CmsUUID id, Locale locale, List<Locale> defaultLocales) 7023 throws CmsDataAccessException { 7024 7025 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7026 dbc, 7027 dbc.currentProject().isOnlineProject(), 7028 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 7029 if (entries.isEmpty()) { 7030 return null; 7031 } 7032 7033 ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create(); 7034 for (CmsUrlNameMappingEntry entry : entries) { 7035 entriesByLocale.put(entry.getLocale(), entry); 7036 } 7037 List<CmsUrlNameMappingEntry> lastEntries = new ArrayList<CmsUrlNameMappingEntry>(); 7038 Comparator<CmsUrlNameMappingEntry> dateChangedComparator = new UrlNameMappingComparator(); 7039 for (String localeKey : entriesByLocale.keySet()) { 7040 // for each locale select the latest mapping entry 7041 CmsUrlNameMappingEntry latestEntryForLocale = Collections.max( 7042 entriesByLocale.get(localeKey), 7043 dateChangedComparator); 7044 lastEntries.add(latestEntryForLocale); 7045 } 7046 CmsLocaleManager localeManager = OpenCms.getLocaleManager(); 7047 List<Locale> availableLocales = new ArrayList<Locale>(); 7048 for (CmsUrlNameMappingEntry entry : lastEntries) { 7049 availableLocales.add(CmsLocaleManager.getLocale(entry.getLocale())); 7050 } 7051 Locale bestLocale = localeManager.getBestMatchingLocale(locale, defaultLocales, availableLocales); 7052 String bestLocaleStr = bestLocale.toString(); 7053 for (CmsUrlNameMappingEntry entry : lastEntries) { 7054 if (entry.getLocale().equals(bestLocaleStr)) { 7055 return entry.getName(); 7056 } 7057 } 7058 return null; 7059 } 7060 7061 /** 7062 * Returns the child resources of a resource, that is the resources 7063 * contained in a folder.<p> 7064 * 7065 * With the parameters <code>getFolders</code> and <code>getFiles</code> 7066 * you can control what type of resources you want in the result list: 7067 * files, folders, or both.<p> 7068 * 7069 * This method is mainly used by the workplace explorer.<p> 7070 * 7071 * @param dbc the current database context 7072 * @param resource the resource to return the child resources for 7073 * @param filter the resource filter to use 7074 * @param getFolders if true the child folders are included in the result 7075 * @param getFiles if true the child files are included in the result 7076 * @param checkPermissions if the resources should be filtered with the current user permissions 7077 * 7078 * @return a list of all child resources 7079 * 7080 * @throws CmsException if something goes wrong 7081 */ 7082 public List<CmsResource> readChildResources( 7083 CmsDbContext dbc, 7084 CmsResource resource, 7085 CmsResourceFilter filter, 7086 boolean getFolders, 7087 boolean getFiles, 7088 boolean checkPermissions) 7089 throws CmsException { 7090 7091 String cacheKey = null; 7092 List<CmsResource> resourceList = null; 7093 if (m_monitor.isEnabled(CmsMemoryMonitor.CacheType.RESOURCE_LIST)) { // check this here to skip the complex cache key generation 7094 String time = ""; 7095 if (checkPermissions) { 7096 // ensure correct caching if site time offset is set 7097 if ((dbc.getRequestContext() != null) 7098 && (OpenCms.getSiteManager().getSiteForSiteRoot(dbc.getRequestContext().getSiteRoot()) != null)) { 7099 time += OpenCms.getSiteManager().getSiteForSiteRoot( 7100 dbc.getRequestContext().getSiteRoot()).getSiteMatcher().getTimeOffset(); 7101 } 7102 } 7103 // try to get the sub resources from the cache 7104 cacheKey = getCacheKey( 7105 new String[] { 7106 dbc.currentUser().getName(), 7107 getFolders 7108 ? (getFiles ? CmsCacheKey.CACHE_KEY_SUBALL : CmsCacheKey.CACHE_KEY_SUBFOLDERS) 7109 : CmsCacheKey.CACHE_KEY_SUBFILES, 7110 checkPermissions ? "+" + time : "-", 7111 filter.getCacheId(), 7112 resource.getRootPath()}, 7113 dbc); 7114 7115 resourceList = m_monitor.getCachedResourceList(cacheKey); 7116 } 7117 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 7118 // read the result form the database 7119 resourceList = getVfsDriver( 7120 dbc).readChildResources(dbc, dbc.currentProject(), resource, getFolders, getFiles); 7121 7122 if (checkPermissions) { 7123 // apply the permission filter 7124 resourceList = filterPermissions(dbc, resourceList, filter); 7125 } 7126 // cache the sub resources 7127 if (dbc.getProjectId().isNullUUID()) { 7128 m_monitor.cacheResourceList(cacheKey, resourceList); 7129 } 7130 } 7131 7132 // we must always apply the result filter and update the context dates 7133 return updateContextDates(dbc, resourceList, filter); 7134 } 7135 7136 /** 7137 * Returns the default file for the given folder.<p> 7138 * 7139 * If the given resource is a file, then this file is returned.<p> 7140 * 7141 * Otherwise, in case of a folder:<br> 7142 * <ol> 7143 * <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and 7144 * <li>if still no file could be found, the configured default files in the 7145 * <code>opencms-vfs.xml</code> configuration are iterated until a match is 7146 * found, and 7147 * <li>if still no file could be found, <code>null</code> is retuned 7148 * </ol> 7149 * 7150 * @param dbc the database context 7151 * @param resource the folder to get the default file for 7152 * @param resourceFilter the resource filter 7153 * 7154 * @return the default file for the given folder 7155 */ 7156 public CmsResource readDefaultFile(CmsDbContext dbc, CmsResource resource, CmsResourceFilter resourceFilter) { 7157 7158 // resource exists, lets check if we have a file or a folder 7159 if (resource.isFolder()) { 7160 // the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder 7161 try { 7162 String defaultFileName = readPropertyObject( 7163 dbc, 7164 resource, 7165 CmsPropertyDefinition.PROPERTY_DEFAULT_FILE, 7166 false).getValue(); 7167 // check if the default file property does not match the navigation level folder marker value 7168 if ((defaultFileName != null) && !CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER.equals(defaultFileName)) { 7169 // property was set, so look up this file first 7170 String folderName = CmsResource.getFolderPath(resource.getRootPath()); 7171 resource = readResource(dbc, folderName + defaultFileName, resourceFilter.addRequireFile()); 7172 } 7173 } catch (CmsException e) { 7174 // ignore all other exceptions and continue the lookup process 7175 if (LOG.isDebugEnabled()) { 7176 LOG.debug(e.getLocalizedMessage(), e); 7177 } 7178 } 7179 if (resource.isFolder()) { 7180 String folderName = CmsResource.getFolderPath(resource.getRootPath()); 7181 // resource is (still) a folder, check default files specified in configuration 7182 Iterator<String> it = OpenCms.getDefaultFiles().iterator(); 7183 while (it.hasNext()) { 7184 String tmpResourceName = folderName + it.next(); 7185 try { 7186 resource = readResource(dbc, tmpResourceName, resourceFilter.addRequireFile()); 7187 // no exception? So we have found the default file 7188 // stop looking for default files 7189 break; 7190 } catch (CmsException e) { 7191 // ignore all other exceptions and continue the lookup process 7192 if (LOG.isDebugEnabled()) { 7193 LOG.debug(e.getLocalizedMessage(), e); 7194 } 7195 } 7196 } 7197 } 7198 } 7199 if (resource.isFolder()) { 7200 // we only want files as a result for further processing 7201 resource = null; 7202 } 7203 return resource; 7204 } 7205 7206 /** 7207 * Reads all deleted (historical) resources below the given path, 7208 * including the full tree below the path, if required.<p> 7209 * 7210 * @param dbc the current db context 7211 * @param resource the parent resource to read the resources from 7212 * @param readTree <code>true</code> to read all subresources 7213 * @param isVfsManager <code>true</code> if the current user has the vfs manager role 7214 * 7215 * @return a list of <code>{@link I_CmsHistoryResource}</code> objects 7216 * 7217 * @throws CmsException if something goes wrong 7218 * 7219 * @see CmsObject#readResource(CmsUUID, int) 7220 * @see CmsObject#readResources(String, CmsResourceFilter, boolean) 7221 * @see CmsObject#readDeletedResources(String, boolean) 7222 */ 7223 public List<I_CmsHistoryResource> readDeletedResources( 7224 CmsDbContext dbc, 7225 CmsResource resource, 7226 boolean readTree, 7227 boolean isVfsManager) 7228 throws CmsException { 7229 7230 Set<I_CmsHistoryResource> result = new HashSet<I_CmsHistoryResource>(); 7231 List<I_CmsHistoryResource> deletedResources; 7232 dbc.getRequestContext().setAttribute("ATTR_RESOURCE_NAME", resource.getRootPath()); 7233 try { 7234 deletedResources = getHistoryDriver(dbc).readDeletedResources( 7235 dbc, 7236 resource.getStructureId(), 7237 isVfsManager ? null : dbc.currentUser().getId()); 7238 } finally { 7239 dbc.getRequestContext().removeAttribute("ATTR_RESOURCE_NAME"); 7240 } 7241 result.addAll(deletedResources); 7242 Set<I_CmsHistoryResource> newResult = new HashSet<I_CmsHistoryResource>(result.size()); 7243 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 7244 Iterator<I_CmsHistoryResource> it = result.iterator(); 7245 while (it.hasNext()) { 7246 I_CmsHistoryResource histRes = it.next(); 7247 // adjust the paths 7248 try { 7249 if (vfsDriver.validateStructureIdExists( 7250 dbc, 7251 dbc.currentProject().getUuid(), 7252 histRes.getStructureId())) { 7253 newResult.add(histRes); 7254 continue; 7255 } 7256 // adjust the path in case of deleted files 7257 String resourcePath = histRes.getRootPath(); 7258 String resName = CmsResource.getName(resourcePath); 7259 String path = CmsResource.getParentFolder(resourcePath); 7260 7261 CmsUUID parentId = histRes.getParentId(); 7262 try { 7263 // first look for the path through the parent id 7264 path = readResource(dbc, parentId, CmsResourceFilter.IGNORE_EXPIRATION).getRootPath(); 7265 } catch (CmsDataAccessException e) { 7266 // if the resource with the parent id is not found, try to get a new parent id with the path 7267 try { 7268 parentId = readResource(dbc, path, CmsResourceFilter.IGNORE_EXPIRATION).getStructureId(); 7269 } catch (CmsDataAccessException e1) { 7270 // ignore, the parent folder has been completely deleted 7271 } 7272 } 7273 resourcePath = path + resName; 7274 7275 boolean isFolder = resourcePath.endsWith("/"); 7276 if (isFolder) { 7277 newResult.add( 7278 new CmsHistoryFolder( 7279 histRes.getPublishTag(), 7280 histRes.getStructureId(), 7281 histRes.getResourceId(), 7282 resourcePath, 7283 histRes.getTypeId(), 7284 histRes.getFlags(), 7285 histRes.getProjectLastModified(), 7286 histRes.getState(), 7287 histRes.getDateCreated(), 7288 histRes.getUserCreated(), 7289 histRes.getDateLastModified(), 7290 histRes.getUserLastModified(), 7291 histRes.getDateReleased(), 7292 histRes.getDateExpired(), 7293 histRes.getVersion(), 7294 parentId, 7295 histRes.getResourceVersion(), 7296 histRes.getStructureVersion())); 7297 } else { 7298 newResult.add( 7299 new CmsHistoryFile( 7300 histRes.getPublishTag(), 7301 histRes.getStructureId(), 7302 histRes.getResourceId(), 7303 resourcePath, 7304 histRes.getTypeId(), 7305 histRes.getFlags(), 7306 histRes.getProjectLastModified(), 7307 histRes.getState(), 7308 histRes.getDateCreated(), 7309 histRes.getUserCreated(), 7310 histRes.getDateLastModified(), 7311 histRes.getUserLastModified(), 7312 histRes.getDateReleased(), 7313 histRes.getDateExpired(), 7314 histRes.getLength(), 7315 histRes.getDateContent(), 7316 histRes.getVersion(), 7317 parentId, 7318 null, 7319 histRes.getResourceVersion(), 7320 histRes.getStructureVersion())); 7321 } 7322 } catch (CmsDataAccessException e) { 7323 // should never happen 7324 if (LOG.isErrorEnabled()) { 7325 LOG.error(e.getLocalizedMessage(), e); 7326 } 7327 } 7328 } 7329 if (readTree) { 7330 Iterator<I_CmsHistoryResource> itDeleted = deletedResources.iterator(); 7331 while (itDeleted.hasNext()) { 7332 I_CmsHistoryResource delResource = itDeleted.next(); 7333 if (delResource.isFolder()) { 7334 newResult.addAll(readDeletedResources(dbc, (CmsFolder)delResource, readTree, isVfsManager)); 7335 } 7336 } 7337 try { 7338 readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL); 7339 // resource exists, so recurse 7340 Iterator<CmsResource> itResources = readResources( 7341 dbc, 7342 resource, 7343 CmsResourceFilter.ALL.addRequireFolder(), 7344 readTree).iterator(); 7345 while (itResources.hasNext()) { 7346 CmsResource subResource = itResources.next(); 7347 if (subResource.isFolder()) { 7348 newResult.addAll(readDeletedResources(dbc, subResource, readTree, isVfsManager)); 7349 } 7350 } 7351 } catch (Exception e) { 7352 // resource does not exists 7353 if (LOG.isDebugEnabled()) { 7354 LOG.debug(e.getLocalizedMessage(), e); 7355 } 7356 } 7357 } 7358 List<I_CmsHistoryResource> finalRes = new ArrayList<I_CmsHistoryResource>(newResult); 7359 Collections.sort(finalRes, I_CmsResource.COMPARE_ROOT_PATH); 7360 return finalRes; 7361 } 7362 7363 /** 7364 * Reads a file resource (including it's binary content) from the VFS, 7365 * using the specified resource filter.<p> 7366 * 7367 * In case you do not need the file content, 7368 * use <code>{@link #readResource(CmsDbContext, String, CmsResourceFilter)}</code> instead.<p> 7369 * 7370 * The specified filter controls what kind of resources should be "found" 7371 * during the read operation. This will depend on the application. For example, 7372 * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently 7373 * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code> 7374 * will ignore the date release / date expired information of the resource.<p> 7375 * 7376 * @param dbc the current database context 7377 * @param resource the base file resource (without content) 7378 * @return the file read from the VFS 7379 * @throws CmsException if operation was not successful 7380 */ 7381 public CmsFile readFile(CmsDbContext dbc, CmsResource resource) throws CmsException { 7382 7383 checkFileResource(dbc, resource); 7384 7385 CmsUUID projectId = dbc.currentProject().getUuid(); 7386 CmsFile file = null; 7387 if (resource instanceof I_CmsHistoryResource) { 7388 file = new CmsHistoryFile((I_CmsHistoryResource)resource); 7389 file.setContents(readHistoryContent(dbc, resource)); 7390 } else { 7391 file = new CmsFile(resource); 7392 file.setContents(getVfsDriver(dbc).readContent(dbc, projectId, resource.getResourceId())); 7393 } 7394 return file; 7395 } 7396 7397 /** 7398 * Reads a file resource content from the VFS and passes it to an input stream handler.<p> 7399 * 7400 * @param dbc the current database context 7401 * @param resource the base file resource 7402 * @param handler the stream handler 7403 * 7404 * @throws CmsException if operation was not successful 7405 */ 7406 public void readFileContentFrom(CmsDbContext dbc, CmsResource resource, I_CmsFileContentStreamHandler handler) 7407 throws CmsException { 7408 7409 checkFileResource(dbc, resource); 7410 CmsUUID projectId = dbc.currentProject().getUuid(); 7411 try { 7412 if (resource instanceof I_CmsHistoryResource) { 7413 try (ByteArrayInputStream in = new ByteArrayInputStream(readHistoryContent(dbc, resource))) { 7414 handler.read(in); 7415 } 7416 } else { 7417 getVfsDriver(dbc).readContentFrom(dbc, projectId, resource.getResourceId(), handler); 7418 } 7419 } catch (IOException e) { 7420 throw new CmsDbIoException(Messages.get().container(Messages.ERR_READ_FILE_1, resource.getRootPath()), e); 7421 } catch (CmsException e) { 7422 throw e; 7423 } catch (Exception e) { 7424 throw new CmsDbIoException(Messages.get().container(Messages.ERR_READ_FILE_1, resource.getRootPath()), e); 7425 } 7426 } 7427 7428 /** 7429 * Reads a file resource content from the VFS and writes it to an output stream.<p> 7430 * 7431 * @param dbc the current database context 7432 * @param resource the base file resource 7433 * @param out the output stream to write to 7434 * 7435 * @throws CmsException if operation was not successful 7436 */ 7437 public void readFileContentTo(CmsDbContext dbc, CmsResource resource, OutputStream out) throws CmsException { 7438 7439 checkFileResource(dbc, resource); 7440 CmsUUID projectId = dbc.currentProject().getUuid(); 7441 try { 7442 if (resource instanceof I_CmsHistoryResource) { 7443 out.write(readHistoryContent(dbc, resource)); 7444 } else { 7445 getVfsDriver(dbc).readContentTo(dbc, projectId, resource.getResourceId(), out); 7446 } 7447 } catch (IOException e) { 7448 throw new CmsDbIoException(Messages.get().container(Messages.ERR_READ_FILE_1, resource.getRootPath()), e); 7449 } 7450 } 7451 7452 /** 7453 * Reads a folder from the VFS, 7454 * using the specified resource filter.<p> 7455 * 7456 * @param dbc the current database context 7457 * @param resourcename the name of the folder to read (full path) 7458 * @param filter the resource filter to use while reading 7459 * 7460 * @return the folder that was read 7461 * 7462 * @throws CmsDataAccessException if something goes wrong 7463 * 7464 * @see #readResource(CmsDbContext, String, CmsResourceFilter) 7465 * @see CmsObject#readFolder(String) 7466 * @see CmsObject#readFolder(String, CmsResourceFilter) 7467 */ 7468 public CmsFolder readFolder(CmsDbContext dbc, String resourcename, CmsResourceFilter filter) 7469 throws CmsDataAccessException { 7470 7471 CmsResource resource = readResource(dbc, resourcename, filter); 7472 7473 return convertResourceToFolder(resource); 7474 } 7475 7476 public List<CmsFolderSizeEntry> readFolderSizeStats(CmsDbContext dbc, CmsFolderSizeOptions options) 7477 throws CmsException { 7478 7479 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 7480 List<CmsFolderSizeEntry> stats = vfsDriver.readFolderSizeStats(dbc, false, options); 7481 return stats; 7482 7483 } 7484 7485 /** 7486 * Reads the group of a project.<p> 7487 * 7488 * @param dbc the current database context 7489 * @param project the project to read from 7490 * 7491 * @return the group of a resource 7492 */ 7493 public CmsGroup readGroup(CmsDbContext dbc, CmsProject project) { 7494 7495 try { 7496 return readGroup(dbc, project.getGroupId()); 7497 } catch (CmsException exc) { 7498 return new CmsGroup( 7499 CmsUUID.getNullUUID(), 7500 CmsUUID.getNullUUID(), 7501 project.getGroupId() + "", 7502 "deleted group", 7503 0); 7504 } 7505 } 7506 7507 /** 7508 * Reads a group based on its id.<p> 7509 * 7510 * @param dbc the current database context 7511 * @param groupId the id of the group that is to be read 7512 * 7513 * @return the requested group 7514 * 7515 * @throws CmsException if operation was not successful 7516 */ 7517 public CmsGroup readGroup(CmsDbContext dbc, CmsUUID groupId) throws CmsException { 7518 7519 CmsGroup group = null; 7520 // try to read group from cache 7521 group = m_monitor.getCachedGroup(groupId.toString()); 7522 if (group == null) { 7523 group = getUserDriver(dbc).readGroup(dbc, groupId); 7524 m_monitor.cacheGroup(group); 7525 } 7526 return group; 7527 } 7528 7529 /** 7530 * Reads a group based on its name.<p> 7531 * 7532 * @param dbc the current database context 7533 * @param groupname the name of the group that is to be read 7534 * 7535 * @return the requested group 7536 * 7537 * @throws CmsDataAccessException if operation was not successful 7538 */ 7539 public CmsGroup readGroup(CmsDbContext dbc, String groupname) throws CmsDataAccessException { 7540 7541 CmsGroup group = null; 7542 // try to read group from cache 7543 group = m_monitor.getCachedGroup(groupname); 7544 if (group == null) { 7545 group = getUserDriver(dbc).readGroup(dbc, groupname); 7546 m_monitor.cacheGroup(group); 7547 } 7548 return group; 7549 } 7550 7551 /** 7552 * Reads a principal (an user or group) from the historical archive based on its ID.<p> 7553 * 7554 * @param dbc the current database context 7555 * @param principalId the id of the principal to read 7556 * 7557 * @return the historical principal entry with the given id 7558 * 7559 * @throws CmsException if something goes wrong, ie. {@link CmsDbEntryNotFoundException} 7560 * 7561 * @see CmsObject#readUser(CmsUUID) 7562 * @see CmsObject#readGroup(CmsUUID) 7563 * @see CmsObject#readHistoryPrincipal(CmsUUID) 7564 */ 7565 public CmsHistoryPrincipal readHistoricalPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsException { 7566 7567 return getHistoryDriver(dbc).readPrincipal(dbc, principalId); 7568 } 7569 7570 /** 7571 * Returns the latest historical project entry with the given id.<p> 7572 * 7573 * @param dbc the current database context 7574 * @param projectId the project id 7575 * 7576 * @return the requested historical project entry 7577 * 7578 * @throws CmsException if something goes wrong 7579 */ 7580 public CmsHistoryProject readHistoryProject(CmsDbContext dbc, CmsUUID projectId) throws CmsException { 7581 7582 return getHistoryDriver(dbc).readProject(dbc, projectId); 7583 } 7584 7585 /** 7586 * Returns a historical project entry.<p> 7587 * 7588 * @param dbc the current database context 7589 * @param publishTag the publish tag of the project 7590 * 7591 * @return the requested historical project entry 7592 * 7593 * @throws CmsException if something goes wrong 7594 */ 7595 public CmsHistoryProject readHistoryProject(CmsDbContext dbc, int publishTag) throws CmsException { 7596 7597 return getHistoryDriver(dbc).readProject(dbc, publishTag); 7598 } 7599 7600 /** 7601 * Reads the list of all <code>{@link CmsProperty}</code> objects that belongs to the given historical resource.<p> 7602 * 7603 * @param dbc the current database context 7604 * @param historyResource the historical resource to read the properties for 7605 * 7606 * @return the list of <code>{@link CmsProperty}</code> objects 7607 * 7608 * @throws CmsException if something goes wrong 7609 */ 7610 public List<CmsProperty> readHistoryPropertyObjects(CmsDbContext dbc, I_CmsHistoryResource historyResource) 7611 throws CmsException { 7612 7613 return getHistoryDriver(dbc).readProperties(dbc, historyResource); 7614 } 7615 7616 /** 7617 * Reads the structure id which is mapped to a given URL name.<p> 7618 * 7619 * @param dbc the current database context 7620 * @param name the name for which the mapped structure id should be looked up 7621 * 7622 * @return the structure id which is mapped to the given name, or null if there is no such id 7623 * 7624 * @throws CmsDataAccessException if something goes wrong 7625 */ 7626 public CmsUUID readIdForUrlName(CmsDbContext dbc, String name) throws CmsDataAccessException { 7627 7628 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7629 dbc, 7630 dbc.currentProject().isOnlineProject(), 7631 CmsUrlNameMappingFilter.ALL.filterName(name)); 7632 if (entries.isEmpty()) { 7633 return null; 7634 } 7635 return entries.get(0).getStructureId(); 7636 } 7637 7638 /** 7639 * Reads the locks that were saved to the database in the previous run of OpenCms.<p> 7640 * 7641 * @param dbc the current database context 7642 * 7643 * @throws CmsException if something goes wrong 7644 */ 7645 public void readLocks(CmsDbContext dbc) throws CmsException { 7646 7647 m_lockManager.readLocks(dbc); 7648 } 7649 7650 /** 7651 * Reads the manager group of a project.<p> 7652 * 7653 * @param dbc the current database context 7654 * @param project the project to read from 7655 * 7656 * @return the group of a resource 7657 */ 7658 public CmsGroup readManagerGroup(CmsDbContext dbc, CmsProject project) { 7659 7660 try { 7661 return readGroup(dbc, project.getManagerGroupId()); 7662 } catch (CmsException exc) { 7663 // the group does not exist any more - return a dummy-group 7664 return new CmsGroup( 7665 CmsUUID.getNullUUID(), 7666 CmsUUID.getNullUUID(), 7667 project.getManagerGroupId() + "", 7668 "deleted group", 7669 0); 7670 } 7671 } 7672 7673 /** 7674 * Reads the URL name which has been most recently mapped to the given structure id, or null 7675 * if no URL name is mapped to the id.<p> 7676 * 7677 * @param dbc the current database context 7678 * @param id a structure id 7679 * @return the name which has been most recently mapped to the given structure id 7680 * 7681 * @throws CmsDataAccessException if something goes wrong 7682 */ 7683 public String readNewestUrlNameForId(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 7684 7685 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7686 dbc, 7687 dbc.currentProject().isOnlineProject(), 7688 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 7689 if (entries.isEmpty()) { 7690 return null; 7691 } 7692 7693 Collections.sort(entries, new UrlNameMappingComparator()); 7694 CmsUrlNameMappingEntry lastEntry = entries.get(entries.size() - 1); 7695 return lastEntry.getName(); 7696 } 7697 7698 /** 7699 * Reads an organizational Unit based on its fully qualified name.<p> 7700 * 7701 * @param dbc the current db context 7702 * @param ouFqn the fully qualified name of the organizational Unit to be read 7703 * 7704 * @return the organizational Unit that with the provided fully qualified name 7705 * 7706 * @throws CmsException if something goes wrong 7707 */ 7708 public CmsOrganizationalUnit readOrganizationalUnit(CmsDbContext dbc, String ouFqn) throws CmsException { 7709 7710 CmsOrganizationalUnit organizationalUnit = null; 7711 // try to read organizational unit from cache 7712 organizationalUnit = m_monitor.getCachedOrgUnit(ouFqn); 7713 if (organizationalUnit == null) { 7714 organizationalUnit = getUserDriver(dbc).readOrganizationalUnit(dbc, ouFqn); 7715 m_monitor.cacheOrgUnit(organizationalUnit); 7716 } 7717 return organizationalUnit; 7718 } 7719 7720 /** 7721 * Reads the owner of a project.<p> 7722 * 7723 * @param dbc the current database context 7724 * @param project the project to get the owner from 7725 * 7726 * @return the owner of a resource 7727 * @throws CmsException if something goes wrong 7728 */ 7729 public CmsUser readOwner(CmsDbContext dbc, CmsProject project) throws CmsException { 7730 7731 return readUser(dbc, project.getOwnerId()); 7732 } 7733 7734 /** 7735 * Reads the parent folder to a given structure id.<p> 7736 * 7737 * @param dbc the current database context 7738 * @param structureId the structure id of the child 7739 * 7740 * @return the parent folder resource 7741 * 7742 * @throws CmsDataAccessException if something goes wrong 7743 */ 7744 public CmsResource readParentFolder(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException { 7745 7746 return getVfsDriver(dbc).readParentFolder(dbc, dbc.currentProject().getUuid(), structureId); 7747 } 7748 7749 /** 7750 * Builds a list of resources for a given path.<p> 7751 * 7752 * @param dbc the current database context 7753 * @param path the requested path 7754 * @param filter a filter object (only "includeDeleted" information is used!) 7755 * 7756 * @return list of <code>{@link CmsResource}</code>s 7757 * 7758 * @throws CmsException if something goes wrong 7759 */ 7760 public List<CmsResource> readPath(CmsDbContext dbc, String path, CmsResourceFilter filter) throws CmsException { 7761 7762 // splits the path into folder and filename tokens 7763 List<String> tokens = CmsStringUtil.splitAsList(path, '/'); 7764 7765 // the root folder is no token in the path but a resource which has to be added to the path 7766 int count = tokens.size() + 1; 7767 // holds the CmsResource instances in the path 7768 List<CmsResource> pathList = new ArrayList<CmsResource>(count); 7769 7770 // true if the path doesn't end with a folder 7771 boolean lastResourceIsFile = false; 7772 // number of folders in the path 7773 int folderCount = count; 7774 if (!path.endsWith("/")) { 7775 folderCount--; 7776 lastResourceIsFile = true; 7777 } 7778 7779 // read the root folder, because it's ID is required to read any sub-resources 7780 String currentResourceName = "/"; 7781 StringBuffer currentPath = new StringBuffer(64); 7782 currentPath.append('/'); 7783 7784 String cp = currentPath.toString(); 7785 CmsUUID projectId = getProjectIdForContext(dbc); 7786 7787 // key to cache the resources 7788 String cacheKey = getCacheKey(null, false, projectId, cp); 7789 // the current resource 7790 CmsResource currentResource = m_monitor.getCachedResource(cacheKey); 7791 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7792 currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp); 7793 if (dbc.getProjectId().isNullUUID()) { 7794 m_monitor.cacheResource(cacheKey, currentResource); 7795 } 7796 } 7797 7798 pathList.add(0, currentResource); 7799 7800 if (count == 1) { 7801 // the root folder was requested- no further operations required 7802 return pathList; 7803 } 7804 7805 Iterator<String> it = tokens.iterator(); 7806 currentResourceName = it.next(); 7807 7808 // read the folder resources in the path /a/b/c/ 7809 int i = 0; 7810 for (i = 1; i < folderCount; i++) { 7811 currentPath.append(currentResourceName); 7812 currentPath.append('/'); 7813 // read the folder 7814 cp = currentPath.toString(); 7815 cacheKey = getCacheKey(null, false, projectId, cp); 7816 currentResource = m_monitor.getCachedResource(cacheKey); 7817 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7818 currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp); 7819 if (dbc.getProjectId().isNullUUID()) { 7820 m_monitor.cacheResource(cacheKey, currentResource); 7821 } 7822 } 7823 7824 pathList.add(i, currentResource); 7825 7826 if (i < (folderCount - 1)) { 7827 currentResourceName = it.next(); 7828 } 7829 } 7830 7831 // read the (optional) last file resource in the path /x.html 7832 if (lastResourceIsFile) { 7833 if (it.hasNext()) { 7834 // this will only be false if a resource in the 7835 // top level root folder (e.g. "/index.html") was requested 7836 currentResourceName = it.next(); 7837 } 7838 currentPath.append(currentResourceName); 7839 7840 // read the file 7841 cp = currentPath.toString(); 7842 cacheKey = getCacheKey(null, false, projectId, cp); 7843 currentResource = m_monitor.getCachedResource(cacheKey); 7844 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7845 currentResource = getVfsDriver(dbc).readResource(dbc, projectId, cp, filter.includeDeleted()); 7846 if (dbc.getProjectId().isNullUUID()) { 7847 m_monitor.cacheResource(cacheKey, currentResource); 7848 } 7849 } 7850 7851 pathList.add(i, currentResource); 7852 } 7853 7854 return pathList; 7855 } 7856 7857 /** 7858 * Reads a project given the projects id.<p> 7859 * 7860 * @param dbc the current database context 7861 * @param id the id of the project 7862 * 7863 * @return the project read 7864 * 7865 * @throws CmsDataAccessException if something goes wrong 7866 */ 7867 public CmsProject readProject(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 7868 7869 CmsProject project = null; 7870 project = m_monitor.getCachedProject(id.toString()); 7871 if (project == null) { 7872 project = getProjectDriver(dbc).readProject(dbc, id); 7873 m_monitor.cacheProject(project); 7874 } 7875 return project; 7876 } 7877 7878 /** 7879 * Reads a project.<p> 7880 * 7881 * Important: Since a project name can be used multiple times, this is NOT the most efficient 7882 * way to read the project. This is only a convenience for front end developing. 7883 * Reading a project by name will return the first project with that name. 7884 * All core classes must use the id version {@link #readProject(CmsDbContext, CmsUUID)} to ensure the right project is read.<p> 7885 * 7886 * @param dbc the current database context 7887 * @param name the name of the project 7888 * 7889 * @return the project read 7890 * 7891 * @throws CmsException if something goes wrong 7892 */ 7893 public CmsProject readProject(CmsDbContext dbc, String name) throws CmsException { 7894 7895 CmsProject project = null; 7896 project = m_monitor.getCachedProject(name); 7897 if (project == null) { 7898 project = getProjectDriver(dbc).readProject(dbc, name); 7899 m_monitor.cacheProject(project); 7900 } 7901 return project; 7902 } 7903 7904 /** 7905 * Returns the list of all resource names that define the "view" of the given project.<p> 7906 * 7907 * @param dbc the current database context 7908 * @param project the project to get the project resources for 7909 * 7910 * @return the list of all resources, as <code>{@link String}</code> objects 7911 * that define the "view" of the given project. 7912 * 7913 * @throws CmsException if something goes wrong 7914 */ 7915 public List<String> readProjectResources(CmsDbContext dbc, CmsProject project) throws CmsException { 7916 7917 return getProjectDriver(dbc).readProjectResources(dbc, project); 7918 } 7919 7920 /** 7921 * Reads all resources of a project that match a given state from the VFS.<p> 7922 * 7923 * Possible values for the <code>state</code> parameter are:<br> 7924 * <ul> 7925 * <li><code>{@link CmsResource#STATE_CHANGED}</code>: Read all "changed" resources in the project</li> 7926 * <li><code>{@link CmsResource#STATE_NEW}</code>: Read all "new" resources in the project</li> 7927 * <li><code>{@link CmsResource#STATE_DELETED}</code>: Read all "deleted" resources in the project</li> 7928 * <li><code>{@link CmsResource#STATE_KEEP}</code>: Read all resources either "changed", "new" or "deleted" in the project</li> 7929 * </ul><p> 7930 * 7931 * @param dbc the current database context 7932 * @param projectId the id of the project to read the file resources for 7933 * @param state the resource state to match 7934 * 7935 * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria 7936 * 7937 * @throws CmsException if something goes wrong 7938 * 7939 * @see CmsObject#readProjectView(CmsUUID, CmsResourceState) 7940 */ 7941 public List<CmsResource> readProjectView(CmsDbContext dbc, CmsUUID projectId, CmsResourceState state) 7942 throws CmsException { 7943 7944 List<CmsResource> resources; 7945 if (state.isNew() || state.isChanged() || state.isDeleted()) { 7946 // get all resources form the database that match the selected state 7947 resources = getVfsDriver(dbc).readResources(dbc, projectId, state, CmsDriverManager.READMODE_MATCHSTATE); 7948 } else { 7949 // get all resources form the database that are somehow changed (i.e. not unchanged) 7950 resources = getVfsDriver( 7951 dbc).readResources(dbc, projectId, CmsResource.STATE_UNCHANGED, CmsDriverManager.READMODE_UNMATCHSTATE); 7952 } 7953 7954 // filter the permissions 7955 List<CmsResource> result = filterPermissions(dbc, resources, CmsResourceFilter.ALL); 7956 // sort the result 7957 Collections.sort(result); 7958 // set the full resource names 7959 return updateContextDates(dbc, result); 7960 } 7961 7962 /** 7963 * Reads a property definition.<p> 7964 * 7965 * If no property definition with the given name is found, 7966 * <code>null</code> is returned.<p> 7967 * 7968 * @param dbc the current database context 7969 * @param name the name of the property definition to read 7970 * 7971 * @return the property definition that was read 7972 * 7973 * @throws CmsException a CmsDbEntryNotFoundException is thrown if the property definition does not exist 7974 */ 7975 public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 7976 7977 return getVfsDriver(dbc).readPropertyDefinition(dbc, name, dbc.currentProject().getUuid()); 7978 } 7979 7980 /** 7981 * Reads a property object from a resource specified by a property name.<p> 7982 * 7983 * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p> 7984 * 7985 * @param dbc the current database context 7986 * @param resource the resource where the property is read from 7987 * @param key the property key name 7988 * @param search if <code>true</code>, the property is searched on all parent folders of the resource. 7989 * if it's not found attached directly to the resource. 7990 * 7991 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 7992 * 7993 * @throws CmsException if something goes wrong 7994 */ 7995 public CmsProperty readPropertyObject(CmsDbContext dbc, CmsResource resource, String key, boolean search) 7996 throws CmsException { 7997 7998 // NOTE: Do not call readPropertyObject(dbc, resource, key, search, null) for performance reasons 7999 8000 // use the list reading method to obtain all properties for the resource 8001 List<CmsProperty> properties = readPropertyObjects(dbc, resource, search); 8002 8003 int i = properties.indexOf(new CmsProperty(key, null, null)); 8004 if (i >= 0) { 8005 // property has been found in the map 8006 CmsProperty result = properties.get(i); 8007 // ensure the result value is not frozen 8008 return result.cloneAsProperty(); 8009 } 8010 return CmsProperty.getNullProperty(); 8011 8012 } 8013 8014 /** 8015 * Reads a property object from a resource specified by a property name.<p> 8016 * 8017 * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p> 8018 * 8019 * @param dbc the current database context 8020 * @param resource the resource where the property is read from 8021 * @param key the property key name 8022 * @param search if <code>true</code>, the property is searched on all parent folders of the resource. 8023 * if it's not found attached directly to the resource. 8024 * @param locale the locale for which the property should be read. 8025 * 8026 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 8027 * 8028 * @throws CmsException if something goes wrong 8029 */ 8030 public CmsProperty readPropertyObject( 8031 CmsDbContext dbc, 8032 CmsResource resource, 8033 String key, 8034 boolean search, 8035 Locale locale) 8036 throws CmsException { 8037 8038 // use the list reading method to obtain all properties for the resource 8039 List<CmsProperty> properties = readPropertyObjects(dbc, resource, search); 8040 // create a lookup property object and look this up in the result map 8041 CmsProperty result = null; 8042 // handle the case without locale separately to improve performance 8043 for (String localizedKey : CmsLocaleManager.getLocaleVariants(key, locale, true, false)) { 8044 int i = properties.indexOf(new CmsProperty(localizedKey, null, null)); 8045 if (i >= 0) { 8046 // property has been found in the map 8047 result = properties.get(i); 8048 // ensure the result value is not frozen 8049 return result.cloneAsProperty(); 8050 } 8051 } 8052 return CmsProperty.getNullProperty(); 8053 } 8054 8055 /** 8056 * Reads all property objects mapped to a specified resource from the database.<p> 8057 * 8058 * All properties in the result List will be in frozen (read only) state, so you can't change the values.<p> 8059 * 8060 * Returns an empty list if no properties are found at all.<p> 8061 * 8062 * @param dbc the current database context 8063 * @param resource the resource where the properties are read from 8064 * @param search true, if the properties should be searched on all parent folders if not found on the resource 8065 * 8066 * @return a list of CmsProperty objects containing the structure and/or resource value 8067 * 8068 * @throws CmsException if something goes wrong 8069 * 8070 * @see CmsObject#readPropertyObjects(String, boolean) 8071 */ 8072 public List<CmsProperty> readPropertyObjects(CmsDbContext dbc, CmsResource resource, boolean search) 8073 throws CmsException { 8074 8075 // check if we have the result already cached 8076 CmsUUID projectId = getProjectIdForContext(dbc); 8077 String cacheKey = getCacheKey(CACHE_ALL_PROPERTIES, search, projectId, resource.getRootPath()); 8078 8079 List<CmsProperty> properties = m_monitor.getCachedPropertyList(cacheKey); 8080 8081 if ((properties == null) || !dbc.getProjectId().isNullUUID()) { 8082 // result not cached, let's look it up in the DB 8083 if (search) { 8084 boolean cont; 8085 properties = new ArrayList<CmsProperty>(); 8086 List<CmsProperty> parentProperties = null; 8087 8088 do { 8089 try { 8090 parentProperties = readPropertyObjects(dbc, resource, false); 8091 8092 // make sure properties from lower folders "overwrite" properties from upper folders 8093 parentProperties.removeAll(properties); 8094 parentProperties.addAll(properties); 8095 8096 properties.clear(); 8097 properties.addAll(parentProperties); 8098 8099 cont = resource.getRootPath().length() > 1; 8100 } catch (CmsSecurityException se) { 8101 // a security exception (probably no read permission) we return the current result 8102 cont = false; 8103 } 8104 if (cont) { 8105 // no permission check on parent folder is required since we must have "read" 8106 // permissions to read the child resource anyway 8107 resource = readResource( 8108 dbc, 8109 CmsResource.getParentFolder(resource.getRootPath()), 8110 CmsResourceFilter.ALL); 8111 } 8112 } while (cont); 8113 } else { 8114 properties = getVfsDriver(dbc).readPropertyObjects(dbc, dbc.currentProject(), resource); 8115 // for (CmsProperty prop : properties) { 8116 // prop.setOrigin(resource.getRootPath()); 8117 // } 8118 } 8119 8120 // set all properties in the result list as frozen 8121 CmsProperty.setFrozen(properties); 8122 if (dbc.getProjectId().isNullUUID()) { 8123 // store the result in the cache if needed 8124 m_monitor.cachePropertyList(cacheKey, properties); 8125 } 8126 } 8127 8128 return new ArrayList<CmsProperty>(properties); 8129 } 8130 8131 /** 8132 * Reads the resources that were published in a publish task for a given publish history ID.<p> 8133 * 8134 * @param dbc the current database context 8135 * @param publishHistoryId unique int ID to identify each publish task in the publish history 8136 * 8137 * @return a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects 8138 * 8139 * @throws CmsException if something goes wrong 8140 */ 8141 public List<CmsPublishedResource> readPublishedResources(CmsDbContext dbc, CmsUUID publishHistoryId) 8142 throws CmsException { 8143 8144 String cacheKey = publishHistoryId.toString(); 8145 List<CmsPublishedResource> resourceList = m_monitor.getCachedPublishedResources(cacheKey); 8146 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8147 resourceList = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId); 8148 // store the result in the cache 8149 if (dbc.getProjectId().isNullUUID()) { 8150 m_monitor.cachePublishedResources(cacheKey, resourceList); 8151 } 8152 } 8153 return resourceList; 8154 } 8155 8156 /** 8157 * Reads a single publish job identified by its publish history id.<p> 8158 * 8159 * @param dbc the current database context 8160 * @param publishHistoryId unique id to identify the publish job in the publish history 8161 * @return an object of type <code>{@link CmsPublishJobInfoBean}</code> 8162 * 8163 * @throws CmsException if something goes wrong 8164 */ 8165 public CmsPublishJobInfoBean readPublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8166 8167 return getProjectDriver(dbc).readPublishJob(dbc, publishHistoryId); 8168 } 8169 8170 /** 8171 * Reads all available publish jobs.<p> 8172 * 8173 * @param dbc the current database context 8174 * @param startTime the start of the time range for finish time 8175 * @param endTime the end of the time range for finish time 8176 * @return a list of objects of type <code>{@link CmsPublishJobInfoBean}</code> 8177 * 8178 * @throws CmsException if something goes wrong 8179 */ 8180 public List<CmsPublishJobInfoBean> readPublishJobs(CmsDbContext dbc, long startTime, long endTime) 8181 throws CmsException { 8182 8183 return getProjectDriver(dbc).readPublishJobs(dbc, startTime, endTime); 8184 } 8185 8186 /** 8187 * Reads the publish list assigned to a publish job.<p> 8188 * 8189 * @param dbc the current database context 8190 * @param publishHistoryId the history id identifying the publish job 8191 * @return the assigned publish list 8192 * @throws CmsException if something goes wrong 8193 */ 8194 public CmsPublishList readPublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8195 8196 return getProjectDriver(dbc).readPublishList(dbc, publishHistoryId); 8197 } 8198 8199 /** 8200 * Reads the publish report assigned to a publish job.<p> 8201 * 8202 * @param dbc the current database context 8203 * @param publishHistoryId the history id identifying the publish job 8204 * @return the content of the assigned publish report 8205 * @throws CmsException if something goes wrong 8206 */ 8207 public byte[] readPublishReportContents(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8208 8209 return getProjectDriver(dbc).readPublishReportContents(dbc, publishHistoryId); 8210 } 8211 8212 /** 8213 * Reads an historical resource entry for the given resource and with the given version number.<p> 8214 * 8215 * @param dbc the current db context 8216 * @param resource the resource to be read 8217 * @param version the version number to retrieve 8218 * 8219 * @return the resource that was read 8220 * 8221 * @throws CmsException if the resource could not be read for any reason 8222 * 8223 * @see CmsObject#restoreResourceVersion(CmsUUID, int) 8224 * @see CmsObject#readResource(CmsUUID, int) 8225 */ 8226 public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException { 8227 8228 Iterator<I_CmsHistoryResource> itVersions = getHistoryDriver(dbc).readAllAvailableVersions( 8229 dbc, 8230 resource.getStructureId()).iterator(); 8231 while (itVersions.hasNext()) { 8232 I_CmsHistoryResource histRes = itVersions.next(); 8233 if (histRes.getVersion() == version) { 8234 return histRes; 8235 } 8236 } 8237 throw new CmsVfsResourceNotFoundException( 8238 org.opencms.db.generic.Messages.get().container( 8239 org.opencms.db.generic.Messages.ERR_HISTORY_FILE_NOT_FOUND_1, 8240 resource.getStructureId())); 8241 } 8242 8243 /** 8244 * Reads a resource from the VFS, using the specified resource filter.<p> 8245 * 8246 * @param dbc the current database context 8247 * @param structureID the structure id of the resource to read 8248 * @param filter the resource filter to use while reading 8249 * 8250 * @return the resource that was read 8251 * 8252 * @throws CmsDataAccessException if something goes wrong 8253 * 8254 * @see CmsObject#readResource(CmsUUID, CmsResourceFilter) 8255 * @see CmsObject#readResource(CmsUUID) 8256 */ 8257 public CmsResource readResource(CmsDbContext dbc, CmsUUID structureID, CmsResourceFilter filter) 8258 throws CmsDataAccessException { 8259 8260 CmsUUID projectId = getProjectIdForContext(dbc); 8261 // please note: the filter will be applied in the security manager later 8262 CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, structureID, filter.includeDeleted()); 8263 8264 // context dates need to be updated 8265 updateContextDates(dbc, resource); 8266 8267 // return the resource 8268 return resource; 8269 } 8270 8271 /** 8272 * Reads a resource from the VFS, using the specified resource filter.<p> 8273 * 8274 * @param dbc the current database context 8275 * @param resourcePath the name of the resource to read (full path) 8276 * @param filter the resource filter to use while reading 8277 * 8278 * @return the resource that was read 8279 * 8280 * @throws CmsDataAccessException if something goes wrong 8281 * 8282 * @see CmsObject#readResource(String, CmsResourceFilter) 8283 * @see CmsObject#readResource(String) 8284 * @see CmsObject#readFile(CmsResource) 8285 */ 8286 public CmsResource readResource(CmsDbContext dbc, String resourcePath, CmsResourceFilter filter) 8287 throws CmsDataAccessException { 8288 8289 CmsUUID projectId = getProjectIdForContext(dbc); 8290 // please note: the filter will be applied in the security manager later 8291 CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, resourcePath, filter.includeDeleted()); 8292 8293 // context dates need to be updated 8294 updateContextDates(dbc, resource); 8295 8296 // return the resource 8297 return resource; 8298 } 8299 8300 /** 8301 * Reads all resources below the given path matching the filter criteria, 8302 * including the full tree below the path only in case the <code>readTree</code> 8303 * parameter is <code>true</code>.<p> 8304 * 8305 * @param dbc the current database context 8306 * @param parent the parent path to read the resources from 8307 * @param filter the filter 8308 * @param readTree <code>true</code> to read all subresources 8309 * 8310 * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria 8311 * 8312 * @throws CmsDataAccessException if the bare reading of the resources fails 8313 * @throws CmsException if security and permission checks for the resources read fail 8314 */ 8315 public List<CmsResource> readResources( 8316 CmsDbContext dbc, 8317 CmsResource parent, 8318 CmsResourceFilter filter, 8319 boolean readTree) 8320 throws CmsException, CmsDataAccessException { 8321 8322 // try to get the sub resources from the cache 8323 String cacheKey = getCacheKey( 8324 new String[] {dbc.currentUser().getName(), filter.getCacheId(), readTree ? "+" : "-", parent.getRootPath()}, 8325 dbc); 8326 8327 List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey); 8328 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8329 // read the result from the database 8330 resourceList = getVfsDriver(dbc).readResourceTree( 8331 dbc, 8332 dbc.currentProject().getUuid(), 8333 (readTree ? parent.getRootPath() : parent.getStructureId().toString()), 8334 filter.getType(), 8335 filter.getState(), 8336 filter.getModifiedAfter(), 8337 filter.getModifiedBefore(), 8338 filter.getReleaseAfter(), 8339 filter.getReleaseBefore(), 8340 filter.getExpireAfter(), 8341 filter.getExpireBefore(), 8342 (readTree ? CmsDriverManager.READMODE_INCLUDE_TREE : CmsDriverManager.READMODE_EXCLUDE_TREE) 8343 | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0) 8344 | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0) 8345 | ((filter.getOnlyFolders() != null) 8346 ? (filter.getOnlyFolders().booleanValue() 8347 ? CmsDriverManager.READMODE_ONLY_FOLDERS 8348 : CmsDriverManager.READMODE_ONLY_FILES) 8349 : 0)); 8350 8351 // HACK: do not take care of permissions if reading organizational units 8352 if (!parent.getRootPath().startsWith("/system/orgunits/")) { 8353 // apply permission filter 8354 resourceList = filterPermissions(dbc, resourceList, filter); 8355 } 8356 // store the result in the resourceList cache 8357 if (dbc.getProjectId().isNullUUID()) { 8358 m_monitor.cacheResourceList(cacheKey, resourceList); 8359 } 8360 } 8361 // we must always apply the result filter and update the context dates 8362 return updateContextDates(dbc, resourceList, filter); 8363 } 8364 8365 /** 8366 * Returns the resources that were visited by a user set in the filter.<p> 8367 * 8368 * @param dbc the database context 8369 * @param poolName the name of the database pool to use 8370 * @param filter the filter that is used to get the visited resources 8371 * 8372 * @return the resources that were visited by a user set in the filter 8373 * 8374 * @throws CmsException if something goes wrong 8375 */ 8376 public List<CmsResource> readResourcesVisitedBy(CmsDbContext dbc, String poolName, CmsVisitedByFilter filter) 8377 throws CmsException { 8378 8379 List<CmsResource> result = getSubscriptionDriver().readResourcesVisitedBy(dbc, poolName, filter); 8380 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 8381 return result; 8382 } 8383 8384 /** 8385 * Reads all resources that have a value (containing the given value string) set 8386 * for the specified property (definition) in the given path.<p> 8387 * 8388 * Both individual and shared properties of a resource are checked.<p> 8389 * 8390 * If the <code>value</code> parameter is <code>null</code>, all resources having the 8391 * given property set are returned.<p> 8392 * 8393 * @param dbc the current database context 8394 * @param folder the folder to get the resources with the property from 8395 * @param propertyDefinition the name of the property (definition) to check for 8396 * @param value the string to search in the value of the property 8397 * @param filter the resource filter to apply to the result set 8398 * 8399 * @return a list of all <code>{@link CmsResource}</code> objects 8400 * that have a value set for the specified property. 8401 * 8402 * @throws CmsException if something goes wrong 8403 */ 8404 public List<CmsResource> readResourcesWithProperty( 8405 CmsDbContext dbc, 8406 CmsResource folder, 8407 String propertyDefinition, 8408 String value, 8409 CmsResourceFilter filter) 8410 throws CmsException { 8411 8412 String cacheKey; 8413 if (value == null) { 8414 cacheKey = getCacheKey( 8415 new String[] { 8416 dbc.currentUser().getName(), 8417 folder.getRootPath(), 8418 propertyDefinition, 8419 filter.getCacheId()}, 8420 dbc); 8421 } else { 8422 cacheKey = getCacheKey( 8423 new String[] { 8424 dbc.currentUser().getName(), 8425 folder.getRootPath(), 8426 propertyDefinition, 8427 value, 8428 filter.getCacheId()}, 8429 dbc); 8430 } 8431 List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey); 8432 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8433 8434 CmsPropertyDefinition propDef = null; 8435 try { 8436 // first read the property definition 8437 propDef = readPropertyDefinition(dbc, propertyDefinition); 8438 } catch (CmsDbEntryNotFoundException e) { 8439 LOG.debug(e.getLocalizedMessage(), e); 8440 } 8441 if (propDef != null) { 8442 // now read the list of resources that have a value set for the property definition 8443 resourceList = getVfsDriver(dbc).readResourcesWithProperty( 8444 dbc, 8445 dbc.currentProject().getUuid(), 8446 propDef.getId(), 8447 folder.getRootPath(), 8448 value); 8449 // apply permission filter 8450 resourceList = filterPermissions(dbc, resourceList, filter); 8451 } else { 8452 resourceList = new ArrayList<>(); 8453 } 8454 // store the result in the resourceList cache 8455 if (dbc.getProjectId().isNullUUID()) { 8456 m_monitor.cacheResourceList(cacheKey, resourceList); 8457 } 8458 } 8459 // we must always apply the result filter and update the context dates 8460 return updateContextDates(dbc, resourceList, filter); 8461 } 8462 8463 /** 8464 * Returns the set of users that are responsible for a specific resource.<p> 8465 * 8466 * @param dbc the current database context 8467 * @param resource the resource to get the responsible users from 8468 * 8469 * @return the set of users that are responsible for a specific resource 8470 * 8471 * @throws CmsException if something goes wrong 8472 */ 8473 public Set<I_CmsPrincipal> readResponsiblePrincipals(CmsDbContext dbc, CmsResource resource) throws CmsException { 8474 8475 Set<I_CmsPrincipal> result = new HashSet<I_CmsPrincipal>(); 8476 Iterator<CmsAccessControlEntry> aces = getAccessControlEntries(dbc, resource, true).iterator(); 8477 while (aces.hasNext()) { 8478 CmsAccessControlEntry ace = aces.next(); 8479 if (ace.isResponsible()) { 8480 I_CmsPrincipal p = lookupPrincipal(dbc, ace.getPrincipal()); 8481 if (p != null) { 8482 result.add(p); 8483 } 8484 } 8485 } 8486 return result; 8487 } 8488 8489 /** 8490 * Returns the set of users that are responsible for a specific resource.<p> 8491 * 8492 * @param dbc the current database context 8493 * @param resource the resource to get the responsible users from 8494 * 8495 * @return the set of users that are responsible for a specific resource 8496 * 8497 * @throws CmsException if something goes wrong 8498 */ 8499 public Set<CmsUser> readResponsibleUsers(CmsDbContext dbc, CmsResource resource) throws CmsException { 8500 8501 Set<CmsUser> result = new HashSet<CmsUser>(); 8502 Iterator<I_CmsPrincipal> principals = readResponsiblePrincipals(dbc, resource).iterator(); 8503 while (principals.hasNext()) { 8504 I_CmsPrincipal principal = principals.next(); 8505 if (principal.isGroup()) { 8506 try { 8507 result.addAll(getUsersOfGroup(dbc, principal.getName(), true, false, false)); 8508 } catch (CmsException e) { 8509 if (LOG.isInfoEnabled()) { 8510 LOG.info(e.getLocalizedMessage(), e); 8511 } 8512 } 8513 } else { 8514 result.add((CmsUser)principal); 8515 } 8516 } 8517 return result; 8518 } 8519 8520 /** 8521 * Returns a List of all siblings of the specified resource, 8522 * the specified resource being always part of the result set.<p> 8523 * 8524 * The result is a list of <code>{@link CmsResource}</code> objects.<p> 8525 * 8526 * @param dbc the current database context 8527 * @param resource the resource to read the siblings for 8528 * @param filter a filter object 8529 * 8530 * @return a list of <code>{@link CmsResource}</code> Objects that 8531 * are siblings to the specified resource, 8532 * including the specified resource itself 8533 * 8534 * @throws CmsException if something goes wrong 8535 */ 8536 public List<CmsResource> readSiblings(CmsDbContext dbc, CmsResource resource, CmsResourceFilter filter) 8537 throws CmsException { 8538 8539 List<CmsResource> siblings = getVfsDriver( 8540 dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, filter.includeDeleted()); 8541 8542 // important: there is no permission check done on the returned list of siblings 8543 // this is because of possible issues with the "publish all siblings" option, 8544 // moreover the user has read permission for the content through 8545 // the selected sibling anyway 8546 return updateContextDates(dbc, siblings, filter); 8547 } 8548 8549 /** 8550 * Returns the parameters of a resource in the table of all published template resources.<p> 8551 * 8552 * @param dbc the current database context 8553 * @param rfsName the rfs name of the resource 8554 * 8555 * @return the parameter string of the requested resource 8556 * 8557 * @throws CmsException if something goes wrong 8558 */ 8559 public String readStaticExportPublishedResourceParameters(CmsDbContext dbc, String rfsName) throws CmsException { 8560 8561 return getProjectDriver(dbc).readStaticExportPublishedResourceParameters(dbc, rfsName); 8562 } 8563 8564 /** 8565 * Returns a list of all template resources which must be processed during a static export.<p> 8566 * 8567 * @param dbc the current database context 8568 * @param parameterResources flag for reading resources with parameters (1) or without (0) 8569 * @param timestamp for reading the data from the db 8570 * 8571 * @return a list of template resources as <code>{@link String}</code> objects 8572 * 8573 * @throws CmsException if something goes wrong 8574 */ 8575 public List<String> readStaticExportResources(CmsDbContext dbc, int parameterResources, long timestamp) 8576 throws CmsException { 8577 8578 return getProjectDriver(dbc).readStaticExportResources(dbc, parameterResources, timestamp); 8579 } 8580 8581 /** 8582 * Reads information about where the file content is stored without loading the content bytes.<p> 8583 * 8584 * @param dbc the current database context 8585 * @param resource the base file resource 8586 * 8587 * @return the stored content info 8588 * 8589 * @throws CmsException if operation was not successful 8590 */ 8591 public CmsStoredContentInfo readStoredContentInfo(CmsDbContext dbc, CmsResource resource) throws CmsException { 8592 8593 checkFileResource(dbc, resource); 8594 if (resource instanceof I_CmsHistoryResource) { 8595 return new CmsStoredContentInfo(resource, null, null, resource.getLength()); 8596 } 8597 CmsUUID projectId = dbc.currentProject().getUuid(); 8598 return getVfsDriver(dbc).readStoredContentInfo(dbc, projectId, resource); 8599 } 8600 8601 /** 8602 * Returns the subscribed history resources that were deleted.<p> 8603 * 8604 * @param dbc the database context 8605 * @param poolName the name of the database pool to use 8606 * @param user the user that subscribed to the resource 8607 * @param groups the groups to check subscribed resources for 8608 * @param parent the parent resource (folder) of the deleted resources, if <code>null</code> all deleted resources will be returned 8609 * @param includeSubFolders indicates if the sub folders of the specified folder path should be considered, too 8610 * @param deletedFrom the time stamp from which the resources should have been deleted 8611 * 8612 * @return the subscribed history resources that were deleted 8613 * 8614 * @throws CmsException if something goes wrong 8615 */ 8616 public List<I_CmsHistoryResource> readSubscribedDeletedResources( 8617 CmsDbContext dbc, 8618 String poolName, 8619 CmsUser user, 8620 List<CmsGroup> groups, 8621 CmsResource parent, 8622 boolean includeSubFolders, 8623 long deletedFrom) 8624 throws CmsException { 8625 8626 List<I_CmsHistoryResource> result = getSubscriptionDriver().readSubscribedDeletedResources( 8627 dbc, 8628 poolName, 8629 user, 8630 groups, 8631 parent, 8632 includeSubFolders, 8633 deletedFrom); 8634 8635 return result; 8636 } 8637 8638 /** 8639 * Returns the resources that were subscribed by a user or group set in the filter.<p> 8640 * 8641 * @param dbc the database context 8642 * @param poolName the name of the database pool to use 8643 * @param filter the filter that is used to get the subscribed resources 8644 * 8645 * @return the resources that were subscribed by a user or group set in the filter 8646 * 8647 * @throws CmsException if something goes wrong 8648 */ 8649 public List<CmsResource> readSubscribedResources(CmsDbContext dbc, String poolName, CmsSubscriptionFilter filter) 8650 throws CmsException { 8651 8652 List<CmsResource> result = getSubscriptionDriver().readSubscribedResources(dbc, poolName, filter); 8653 8654 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 8655 return result; 8656 } 8657 8658 /** 8659 * Reads URL name mapping entries which match the given filter.<p> 8660 * 8661 * @param dbc the database context 8662 * @param online if true, read online URL name mappings, else offline ones 8663 * @param filter the filter for matching the URL name entries 8664 * 8665 * @return the list of URL name mapping entries which match the given filter 8666 * 8667 * @throws CmsDataAccessException if something goes wrong 8668 */ 8669 public List<CmsUrlNameMappingEntry> readUrlNameMappingEntries( 8670 CmsDbContext dbc, 8671 boolean online, 8672 CmsUrlNameMappingFilter filter) 8673 throws CmsDataAccessException { 8674 8675 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 8676 return vfsDriver.readUrlNameMappingEntries(dbc, online, filter); 8677 } 8678 8679 /** 8680 * Reads the URL name mappings matching the given filter.<p> 8681 * 8682 * @param dbc the DB context to use 8683 * @param filter the filter used to select the mapping entries 8684 * @return the entries matching the given filter 8685 * 8686 * @throws CmsDataAccessException if something goes wrong 8687 */ 8688 public List<CmsUrlNameMappingEntry> readUrlNameMappings(CmsDbContext dbc, CmsUrlNameMappingFilter filter) 8689 throws CmsDataAccessException { 8690 8691 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 8692 dbc, 8693 dbc.currentProject().isOnlineProject(), 8694 filter); 8695 return entries; 8696 } 8697 8698 /** 8699 * Reads the newest URL names of a resource for all locales.<p> 8700 * 8701 * @param dbc the database context 8702 * @param id the resource's structure id 8703 * 8704 * @return the url names for the locales 8705 * 8706 * @throws CmsDataAccessException if the database operation failed 8707 */ 8708 public List<String> readUrlNamesForAllLocales(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 8709 8710 List<String> result = new ArrayList<String>(); 8711 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 8712 dbc, 8713 dbc.currentProject().isOnlineProject(), 8714 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 8715 ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create(); 8716 for (CmsUrlNameMappingEntry entry : entries) { 8717 String localeKey = entry.getLocale(); 8718 entriesByLocale.put(localeKey, entry); 8719 } 8720 8721 for (String localeKey : entriesByLocale.keySet()) { 8722 List<CmsUrlNameMappingEntry> entrs = entriesByLocale.get(localeKey); 8723 CmsUrlNameMappingEntry maxEntryForLocale = Collections.max(entrs, new UrlNameMappingComparator()); 8724 result.add(maxEntryForLocale.getName()); 8725 } 8726 return result; 8727 } 8728 8729 /** 8730 * Returns a user object based on the id of a user.<p> 8731 * 8732 * @param dbc the current database context 8733 * @param id the id of the user to read 8734 * 8735 * @return the user read 8736 * 8737 * @throws CmsException if something goes wrong 8738 */ 8739 public CmsUser readUser(CmsDbContext dbc, CmsUUID id) throws CmsException { 8740 8741 CmsUser user = m_monitor.getCachedUser(id.toString()); 8742 if (user == null) { 8743 user = getUserDriver(dbc).readUser(dbc, id); 8744 m_monitor.cacheUser(user); 8745 } 8746 // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects 8747 return user.clone(); 8748 } 8749 8750 /** 8751 * Returns a user object.<p> 8752 * 8753 * @param dbc the current database context 8754 * @param username the name of the user that is to be read 8755 * 8756 * @return user read 8757 * 8758 * @throws CmsDataAccessException if operation was not successful 8759 */ 8760 public CmsUser readUser(CmsDbContext dbc, String username) throws CmsDataAccessException { 8761 8762 CmsUser user = m_monitor.getCachedUser(username); 8763 if (user == null) { 8764 user = getUserDriver(dbc).readUser(dbc, username); 8765 m_monitor.cacheUser(user); 8766 } 8767 // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects 8768 return user.clone(); 8769 } 8770 8771 /** 8772 * Returns a user object if the password for the user is correct.<p> 8773 * 8774 * If the user/pwd pair is not valid a <code>{@link CmsException}</code> is thrown.<p> 8775 * 8776 * @param dbc the current database context 8777 * @param username the username of the user that is to be read 8778 * @param password the password of the user that is to be read 8779 * 8780 * @return user read 8781 * 8782 * @throws CmsException if operation was not successful 8783 */ 8784 public CmsUser readUser(CmsDbContext dbc, String username, String password) throws CmsException { 8785 8786 // don't read user from cache here because password may have changed 8787 CmsUser user = getUserDriver(dbc).readUser(dbc, username, password, null); 8788 m_monitor.cacheUser(user); 8789 return user; 8790 } 8791 8792 /** 8793 * Removes an access control entry for a given resource and principal.<p> 8794 * 8795 * @param dbc the current database context 8796 * @param resource the resource 8797 * @param principal the id of the principal to remove the the access control entry for 8798 * 8799 * @throws CmsException if something goes wrong 8800 */ 8801 public void removeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) 8802 throws CmsException { 8803 8804 // remove the ace 8805 getUserDriver(dbc).removeAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal); 8806 8807 // log it 8808 log( 8809 dbc, 8810 new CmsLogEntry( 8811 dbc, 8812 resource.getStructureId(), 8813 CmsLogEntryType.RESOURCE_PERMISSIONS, 8814 new String[] {resource.getRootPath()}), 8815 false); 8816 8817 // update the "last modified" information 8818 setDateLastModified(dbc, resource, resource.getDateLastModified()); 8819 8820 // clear the cache 8821 m_monitor.clearAccessControlListCache(); 8822 8823 // fire a resource modification event 8824 Map<String, Object> data = new HashMap<String, Object>(2); 8825 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 8826 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 8827 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 8828 } 8829 8830 /** 8831 * Removes a resource from the given organizational unit.<p> 8832 * 8833 * @param dbc the current db context 8834 * @param orgUnit the organizational unit to remove the resource from 8835 * @param resource the resource that is to be removed from the organizational unit 8836 * 8837 * @throws CmsException if something goes wrong 8838 * 8839 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 8840 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 8841 */ 8842 public void removeResourceFromOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource) 8843 throws CmsException { 8844 8845 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 8846 getUserDriver(dbc).removeResourceFromOrganizationalUnit(dbc, orgUnit, resource); 8847 } 8848 8849 /** 8850 * Removes a resource from the current project of the user.<p> 8851 * 8852 * @param dbc the current database context 8853 * @param resource the resource to apply this operation to 8854 * 8855 * @throws CmsException if something goes wrong 8856 * 8857 * @see CmsObject#copyResourceToProject(String) 8858 * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource) 8859 */ 8860 public void removeResourceFromProject(CmsDbContext dbc, CmsResource resource) throws CmsException { 8861 8862 // remove the resource to the project only if the resource is already in the project 8863 if (isInsideCurrentProject(dbc, resource.getRootPath())) { 8864 // check if there are already any subfolders of this resource 8865 I_CmsProjectDriver projectDriver = getProjectDriver(dbc); 8866 if (resource.isFolder()) { 8867 List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject()); 8868 for (int i = 0; i < projectResources.size(); i++) { 8869 String resname = projectResources.get(i); 8870 if (resname.startsWith(resource.getRootPath())) { 8871 // delete the existing project resource first 8872 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname); 8873 } 8874 } 8875 } 8876 try { 8877 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath()); 8878 } catch (CmsException exc) { 8879 // if the subfolder exists already - all is ok 8880 } finally { 8881 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 8882 8883 OpenCms.fireCmsEvent( 8884 new CmsEvent( 8885 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 8886 Collections.<String, Object> singletonMap("project", dbc.currentProject()))); 8887 } 8888 } 8889 } 8890 8891 /** 8892 * Removes the given resource to the given user's publish list.<p> 8893 * 8894 * @param dbc the database context 8895 * @param userId the user's id 8896 * @param structureIds the collection of structure IDs to remove 8897 * 8898 * @throws CmsDataAccessException if something goes wrong 8899 */ 8900 public void removeResourceFromUsersPubList(CmsDbContext dbc, CmsUUID userId, Collection<CmsUUID> structureIds) 8901 throws CmsDataAccessException { 8902 8903 for (CmsUUID structureId : structureIds) { 8904 CmsLogEntry entry = new CmsLogEntry( 8905 userId, 8906 System.currentTimeMillis(), 8907 structureId, 8908 CmsLogEntryType.RESOURCE_HIDDEN, 8909 new String[] {readResource(dbc, structureId, CmsResourceFilter.ALL).getRootPath()}); 8910 log(dbc, entry, true); 8911 } 8912 } 8913 8914 /** 8915 * Removes a user from a group.<p> 8916 * 8917 * @param dbc the current database context 8918 * @param username the name of the user that is to be removed from the group 8919 * @param groupname the name of the group 8920 * @param readRoles if to read roles or groups 8921 * 8922 * @throws CmsException if operation was not successful 8923 * @throws CmsIllegalArgumentException if the given user was not member in the given group 8924 * @throws CmsDbEntryNotFoundException if the given group was not found 8925 * @throws CmsSecurityException if the given user was <b>read as 'null' from the database</b> 8926 * 8927 * @see #addUserToGroup(CmsDbContext, String, String, boolean) 8928 */ 8929 public void removeUserFromGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 8930 throws CmsException, CmsIllegalArgumentException, CmsDbEntryNotFoundException, CmsSecurityException { 8931 8932 CmsGroup group = readGroup(dbc, groupname); 8933 //check if group exists 8934 if (group == null) { 8935 // the group does not exists 8936 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 8937 } 8938 if (group.isVirtual() && !readRoles) { 8939 // if removing a user from a virtual role treat it as removing the user from the role 8940 removeUserFromGroup(dbc, username, CmsRole.valueOf(group).getGroupName(), true); 8941 return; 8942 } 8943 if (group.isVirtual()) { 8944 // this is an hack so to prevent a unlimited recursive calls 8945 readRoles = false; 8946 } 8947 if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) { 8948 // we want a role but we got a group, or the other way 8949 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 8950 } 8951 8952 boolean skipRemove = false; 8953 // test if this user is existing in the group 8954 if (!userInGroup(dbc, username, groupname, readRoles)) { 8955 if (readRoles) { 8956 // Sometimes users can end up with the default groups corresponding to roles (Administrators, Users) without the actual roles. 8957 // When trying to remove the user from such a group, we end up here in a recursive call of this method with readRoles = true. We do not 8958 // want to throw an exception then, because it would prevent the code that actually removes the user from the group from running. 8959 LOG.warn( 8960 "Trying to remove user from role that they are not a member of (user: " 8961 + username 8962 + ", group: " 8963 + groupname 8964 + ")"); 8965 skipRemove = true; 8966 } else { 8967 // user is not in the group, throw exception 8968 throw new CmsIllegalArgumentException( 8969 Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname)); 8970 } 8971 } 8972 8973 CmsUser user = readUser(dbc, username); 8974 //check if the user exists 8975 if (user == null) { 8976 // the user does not exists 8977 throw new CmsIllegalArgumentException( 8978 Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname)); 8979 } 8980 8981 if (readRoles) { 8982 CmsRole role = CmsRole.valueOf(group); 8983 // update virtual groups 8984 Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator(); 8985 while (it.hasNext()) { 8986 CmsGroup virtualGroup = it.next(); 8987 if (userInGroup(dbc, username, virtualGroup.getName(), false)) { 8988 // here we say readroles = true, to prevent an unlimited recursive calls 8989 removeUserFromGroup(dbc, username, virtualGroup.getName(), true); 8990 } 8991 } 8992 } 8993 if (!skipRemove) { 8994 getUserDriver(dbc).deleteUserInGroup(dbc, user.getId(), group.getId()); 8995 } 8996 8997 // flush relevant caches 8998 if (readRoles) { 8999 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 9000 } 9001 m_monitor.flushUserGroups(user.getId()); 9002 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 9003 9004 if (!dbc.getProjectId().isNullUUID()) { 9005 // user modified event is not needed 9006 return; 9007 } 9008 // fire user modified event 9009 Map<String, Object> eventData = new HashMap<String, Object>(); 9010 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9011 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 9012 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 9013 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 9014 eventData.put( 9015 I_CmsEventListener.KEY_USER_ACTION, 9016 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP); 9017 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9018 9019 } 9020 9021 /** 9022 * Repairs broken categories.<p> 9023 * 9024 * @param dbc the database context 9025 * @param projectId the project id 9026 * @param resource the resource to repair the categories for 9027 * 9028 * @throws CmsException if something goes wrong 9029 */ 9030 public void repairCategories(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsException { 9031 9032 CmsObject cms = OpenCms.initCmsObject(new CmsObject(getSecurityManager(), dbc.getRequestContext())); 9033 cms.getRequestContext().setSiteRoot(""); 9034 cms.getRequestContext().setCurrentProject(readProject(dbc, projectId)); 9035 CmsCategoryService.getInstance().repairRelations(cms, resource); 9036 } 9037 9038 /** 9039 * Replaces the content, type and properties of a resource.<p> 9040 * 9041 * @param dbc the current database context 9042 * @param resource the name of the resource to apply this operation to 9043 * @param type the new type of the resource 9044 * @param content the new content of the resource 9045 * @param properties the new properties of the resource 9046 * 9047 * @throws CmsException if something goes wrong 9048 * 9049 * @see CmsObject#replaceResource(String, int, byte[], List) 9050 * @see I_CmsResourceType#replaceResource(CmsObject, CmsSecurityManager, CmsResource, int, byte[], List) 9051 */ 9052 @SuppressWarnings("javadoc") 9053 public void replaceResource( 9054 CmsDbContext dbc, 9055 CmsResource resource, 9056 int type, 9057 byte[] content, 9058 List<CmsProperty> properties) 9059 throws CmsException { 9060 9061 // replace the existing with the new file content 9062 getVfsDriver(dbc).replaceResource(dbc, resource, content, type); 9063 9064 if ((properties != null) && !properties.isEmpty()) { 9065 // write the properties 9066 getVfsDriver(dbc).writePropertyObjects(dbc, dbc.currentProject(), resource, properties); 9067 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 9068 } 9069 9070 // update the resource state 9071 if (resource.getState().isUnchanged()) { 9072 resource.setState(CmsResource.STATE_CHANGED); 9073 } 9074 resource.setUserLastModified(dbc.currentUser().getId()); 9075 9076 // log it 9077 log( 9078 dbc, 9079 new CmsLogEntry( 9080 dbc, 9081 resource.getStructureId(), 9082 CmsLogEntryType.RESOURCE_CONTENT_MODIFIED, 9083 new String[] {resource.getRootPath()}), 9084 false); 9085 9086 setDateLastModified(dbc, resource, System.currentTimeMillis()); 9087 9088 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false); 9089 9090 deleteRelationsWithSiblings(dbc, resource); 9091 9092 // clear the cache 9093 m_monitor.clearResourceCache(); 9094 9095 if ((properties != null) && !properties.isEmpty()) { 9096 // resource and properties were modified 9097 OpenCms.fireCmsEvent( 9098 new CmsEvent( 9099 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 9100 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource))); 9101 } else { 9102 // only the resource was modified 9103 Map<String, Object> data = new HashMap<String, Object>(2); 9104 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9105 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9106 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9107 } 9108 } 9109 9110 /** 9111 * Resets the password for a specified user.<p> 9112 * 9113 * @param dbc the current database context 9114 * @param username the name of the user 9115 * @param oldPassword the old password 9116 * @param secondFactor the second factor data used for 2FA 9117 * @param newPassword the new password 9118 * 9119 * @throws CmsException if the user data could not be read from the database 9120 * @throws CmsSecurityException if the specified username and old password could not be verified 9121 */ 9122 public void resetPassword( 9123 CmsDbContext dbc, 9124 String username, 9125 String oldPassword, 9126 CmsSecondFactorInfo secondFactor, 9127 String newPassword) 9128 throws CmsException, CmsSecurityException { 9129 9130 if ((oldPassword != null) && (newPassword != null)) { 9131 9132 CmsUser user = null; 9133 9134 if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) { 9135 validatePassword(newPassword); 9136 } 9137 9138 // read the user as a system user to verify that the specified old password is correct 9139 try { 9140 user = getUserDriver(dbc).readUser(dbc, username, oldPassword, null); 9141 } catch (CmsDbEntryNotFoundException e) { 9142 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), e); 9143 } 9144 9145 if ((user == null) || user.isManaged()) { 9146 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username)); 9147 } 9148 9149 CmsTwoFactorAuthenticationHandler twoFactorHandler = OpenCms.getTwoFactorAuthenticationHandler(); 9150 if (twoFactorHandler.needsTwoFactorAuthentication(user) && twoFactorHandler.hasSecondFactor(user)) { 9151 if (!twoFactorHandler.verifySecondFactor(user, secondFactor)) { 9152 throw new CmsDataAccessException( 9153 Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), 9154 new RuntimeException("Verification code mismatch")); 9155 } 9156 } 9157 9158 getUserDriver(dbc).writePassword(dbc, username, oldPassword, newPassword); 9159 user.getAdditionalInfo().put( 9160 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 9161 "" + System.currentTimeMillis()); 9162 user.deleteAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_PASSWORD_RESET); 9163 getUserDriver(dbc).writeUser(dbc, user); 9164 9165 if (!dbc.getProjectId().isNullUUID()) { 9166 // user modified event is not needed 9167 return; 9168 } 9169 // fire user modified event 9170 Map<String, Object> eventData = new HashMap<String, Object>(); 9171 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9172 eventData.put( 9173 I_CmsEventListener.KEY_USER_ACTION, 9174 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD); 9175 eventData.put( 9176 I_CmsEventListener.KEY_USER_CHANGES, 9177 Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA)); 9178 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9179 9180 } else if (CmsStringUtil.isEmpty(oldPassword)) { 9181 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_OLD_MISSING_0)); 9182 } else if (CmsStringUtil.isEmpty(newPassword)) { 9183 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_NEW_MISSING_0)); 9184 } 9185 } 9186 9187 /** 9188 * Restores a deleted resource identified by its structure id from the historical archive.<p> 9189 * 9190 * @param dbc the current database context 9191 * @param structureId the structure id of the resource to restore 9192 * 9193 * @throws CmsException if something goes wrong 9194 * 9195 * @see CmsObject#restoreDeletedResource(CmsUUID) 9196 */ 9197 public CmsResource restoreDeletedResource(CmsDbContext dbc, CmsUUID structureId) throws CmsException { 9198 9199 // get the last version, which should be the deleted one 9200 int version = getHistoryDriver(dbc).readLastVersion(dbc, structureId); 9201 // get that version 9202 I_CmsHistoryResource histRes = getHistoryDriver(dbc).readResource(dbc, structureId, version); 9203 9204 // check the parent path 9205 CmsResource parent; 9206 try { 9207 // try to read the parent resource by id 9208 parent = getVfsDriver(dbc).readResource(dbc, dbc.currentProject().getUuid(), histRes.getParentId(), true); 9209 } catch (CmsVfsResourceNotFoundException e) { 9210 // if not found try to read the parent resource by name 9211 try { 9212 // try to read the parent resource by id 9213 parent = getVfsDriver(dbc).readResource( 9214 dbc, 9215 dbc.currentProject().getUuid(), 9216 CmsResource.getParentFolder(histRes.getRootPath()), 9217 true); 9218 } catch (CmsVfsResourceNotFoundException e1) { 9219 // if not found try to restore the parent resource 9220 restoreDeletedResource(dbc, histRes.getParentId()); 9221 parent = readResource(dbc, histRes.getParentId(), CmsResourceFilter.IGNORE_EXPIRATION); 9222 } 9223 } 9224 // check write permissions 9225 m_securityManager.checkPermissions( 9226 dbc, 9227 parent, 9228 CmsPermissionSet.ACCESS_WRITE, 9229 false, 9230 CmsResourceFilter.IGNORE_EXPIRATION); 9231 9232 // check the name 9233 String path = parent.getRootPath(); 9234 String resName = CmsResource.getName(histRes.getRootPath()); // name 9235 String ext = ""; 9236 if (resName.charAt(resName.length() - 1) == '/') { 9237 resName = resName.substring(0, resName.length() - 1); 9238 } else { 9239 ext = CmsFileUtil.getExtension(resName); // extension 9240 } 9241 String nameWOExt = resName.substring(0, resName.length() - ext.length()); // name without extension 9242 for (int i = 1; true; i++) { 9243 try { 9244 readResource(dbc, path + resName, CmsResourceFilter.ALL); 9245 resName = nameWOExt + "_" + i + ext; 9246 // try the next resource name with following schema: path/name_{i}.ext 9247 } catch (CmsVfsResourceNotFoundException e) { 9248 // ok, we found a not used resource name 9249 break; 9250 } 9251 } 9252 9253 // check structure id 9254 CmsUUID id = structureId; 9255 if (getVfsDriver(dbc).validateStructureIdExists(dbc, dbc.currentProject().getUuid(), structureId)) { 9256 // should never happen, but if already exists create a new one 9257 id = new CmsUUID(); 9258 } 9259 9260 byte[] contents = null; 9261 boolean isFolder = true; 9262 9263 // do we need the contents? 9264 if (histRes instanceof CmsFile) { 9265 contents = ((CmsFile)histRes).getContents(); 9266 if ((contents == null) || (contents.length == 0)) { 9267 contents = getHistoryDriver(dbc).readContent(dbc, histRes.getResourceId(), histRes.getPublishTag()); 9268 } 9269 isFolder = false; 9270 } 9271 9272 // now read the historical properties 9273 List<CmsProperty> properties = getHistoryDriver(dbc).readProperties(dbc, histRes); 9274 9275 // create the object to create 9276 CmsResource newResource = new CmsResource( 9277 id, 9278 histRes.getResourceId(), 9279 path + resName, 9280 histRes.getTypeId(), 9281 isFolder, 9282 histRes.getFlags(), 9283 dbc.currentProject().getUuid(), 9284 CmsResource.STATE_NEW, 9285 histRes.getDateCreated(), 9286 histRes.getUserCreated(), 9287 histRes.getDateLastModified(), 9288 dbc.currentUser().getId(), 9289 histRes.getDateReleased(), 9290 histRes.getDateExpired(), 9291 histRes.getSiblingCount(), 9292 histRes.getLength(), 9293 histRes.getDateContent(), 9294 histRes.getVersion()); 9295 9296 // log it 9297 log( 9298 dbc, 9299 new CmsLogEntry( 9300 dbc, 9301 newResource.getStructureId(), 9302 CmsLogEntryType.RESOURCE_RESTORE_DELETED, 9303 new String[] {newResource.getRootPath()}), 9304 false); 9305 9306 // prevent the date last modified is set to the current time 9307 newResource.setDateLastModified(newResource.getDateLastModified()); 9308 // restore the resource! 9309 CmsResource resource = createResource(dbc, path + resName, newResource, contents, properties, true); 9310 // set resource state to changed 9311 newResource.setState(CmsResource.STATE_CHANGED); 9312 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), newResource, UPDATE_RESOURCE_STATE, false); 9313 newResource.setState(CmsResource.STATE_NEW); 9314 // fire the event 9315 Map<String, Object> data = new HashMap<String, Object>(2); 9316 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9317 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9318 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9319 return newResource; 9320 } 9321 9322 /** 9323 * Restores a resource in the current project with a version from the historical archive.<p> 9324 * 9325 * @param dbc the current database context 9326 * @param resource the resource to restore from the archive 9327 * @param version the version number to restore from the archive 9328 * 9329 * @throws CmsException if something goes wrong 9330 * 9331 * @see CmsObject#restoreResourceVersion(CmsUUID, int) 9332 * @see I_CmsResourceType#restoreResource(CmsObject, CmsSecurityManager, CmsResource, int) 9333 */ 9334 public CmsResource restoreResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException { 9335 9336 I_CmsHistoryResource historyResource = readResource(dbc, resource, version); 9337 CmsResourceState state = CmsResource.STATE_CHANGED; 9338 if (resource.getState().isNew()) { 9339 state = CmsResource.STATE_NEW; 9340 } 9341 int newVersion = resource.getVersion(); 9342 if (resource.getState().isUnchanged()) { 9343 newVersion++; 9344 } 9345 CmsResource newResource = null; 9346 // is the resource a file? 9347 if (historyResource instanceof CmsFile) { 9348 // get the historical up flags 9349 int flags = historyResource.getFlags(); 9350 if (resource.isLabeled()) { 9351 // set the flag for labeled links on the restored file 9352 flags |= CmsResource.FLAG_LABELED; 9353 } 9354 CmsFile newFile = new CmsFile( 9355 resource.getStructureId(), 9356 resource.getResourceId(), 9357 resource.getRootPath(), 9358 historyResource.getTypeId(), 9359 flags, 9360 dbc.currentProject().getUuid(), 9361 state, 9362 resource.getDateCreated(), 9363 historyResource.getUserCreated(), 9364 resource.getDateLastModified(), 9365 dbc.currentUser().getId(), 9366 historyResource.getDateReleased(), 9367 historyResource.getDateExpired(), 9368 resource.getSiblingCount(), 9369 historyResource.getLength(), 9370 historyResource.getDateContent(), 9371 newVersion, 9372 readFile(dbc, (CmsHistoryFile)historyResource).getContents()); 9373 9374 // log it 9375 log( 9376 dbc, 9377 new CmsLogEntry( 9378 dbc, 9379 newFile.getStructureId(), 9380 CmsLogEntryType.RESOURCE_HISTORY, 9381 new String[] {newFile.getRootPath()}), 9382 false); 9383 9384 newResource = writeFile(dbc, newFile); 9385 } else { 9386 // it is a folder! 9387 newResource = new CmsFolder( 9388 resource.getStructureId(), 9389 resource.getResourceId(), 9390 resource.getRootPath(), 9391 historyResource.getTypeId(), 9392 historyResource.getFlags(), 9393 dbc.currentProject().getUuid(), 9394 state, 9395 resource.getDateCreated(), 9396 historyResource.getUserCreated(), 9397 resource.getDateLastModified(), 9398 dbc.currentUser().getId(), 9399 historyResource.getDateReleased(), 9400 historyResource.getDateExpired(), 9401 newVersion); 9402 9403 // log it 9404 log( 9405 dbc, 9406 new CmsLogEntry( 9407 dbc, 9408 newResource.getStructureId(), 9409 CmsLogEntryType.RESOURCE_HISTORY, 9410 new String[] {newResource.getRootPath()}), 9411 false); 9412 9413 writeResource(dbc, newResource); 9414 } 9415 if (newResource != null) { 9416 // now read the historical properties 9417 List<CmsProperty> historyProperties = getHistoryDriver(dbc).readProperties(dbc, historyResource); 9418 // remove all properties 9419 deleteAllProperties(dbc, newResource.getRootPath()); 9420 // write them to the restored resource 9421 writePropertyObjects(dbc, newResource, historyProperties, false); 9422 9423 m_monitor.clearResourceCache(); 9424 } 9425 9426 Map<String, Object> data = new HashMap<String, Object>(2); 9427 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9428 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9429 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9430 return newResource; 9431 } 9432 9433 /** 9434 * Saves a list of aliases for the same structure id, replacing any aliases for the same structure id.<p> 9435 * 9436 * @param dbc the current database context 9437 * @param project the current project 9438 * @param structureId the structure id for which the aliases should be saved 9439 * @param aliases the list of aliases to save 9440 * 9441 * @throws CmsException if something goes wrong 9442 */ 9443 public void saveAliases(CmsDbContext dbc, CmsProject project, CmsUUID structureId, List<CmsAlias> aliases) 9444 throws CmsException { 9445 9446 for (CmsAlias alias : aliases) { 9447 if (!structureId.equals(alias.getStructureId())) { 9448 throw new IllegalArgumentException("Aliases to replace must have the same structure id!"); 9449 } 9450 } 9451 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 9452 vfsDriver.deleteAliases(dbc, project, new CmsAliasFilter(null, null, structureId)); 9453 for (CmsAlias alias : aliases) { 9454 String aliasPath = alias.getAliasPath(); 9455 if (CmsAlias.ALIAS_PATTERN.matcher(aliasPath).matches()) { 9456 vfsDriver.insertAlias(dbc, project, alias); 9457 } else { 9458 LOG.error("Invalid alias path: " + aliasPath); 9459 } 9460 } 9461 } 9462 9463 /** 9464 * Replaces the complete list of rewrite aliases for a given site root.<p> 9465 * 9466 * @param dbc the current database context 9467 * @param siteRoot the site root for which the rewrite aliases should be replaced 9468 * @param newAliases the new aliases for the given site root 9469 * @throws CmsException if something goes wrong 9470 */ 9471 public void saveRewriteAliases(CmsDbContext dbc, String siteRoot, List<CmsRewriteAlias> newAliases) 9472 throws CmsException { 9473 9474 CmsRewriteAliasFilter filter = new CmsRewriteAliasFilter().setSiteRoot(siteRoot); 9475 getVfsDriver(dbc).deleteRewriteAliases(dbc, filter); 9476 getVfsDriver(dbc).insertRewriteAliases(dbc, newAliases); 9477 } 9478 9479 /** 9480 * Searches for users which fit the given criteria.<p> 9481 * 9482 * @param dbc the database context 9483 * @param searchParams the search criteria 9484 * 9485 * @return the users which fit the search criteria 9486 * 9487 * @throws CmsDataAccessException if something goes wrong 9488 */ 9489 public List<CmsUser> searchUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams 9490 9491 ) throws CmsDataAccessException { 9492 9493 return getUserDriver(dbc).searchUsers(dbc, searchParams); 9494 } 9495 9496 /** 9497 * Changes the "expire" date of a resource.<p> 9498 * 9499 * @param dbc the current database context 9500 * @param resource the resource to touch 9501 * @param dateExpired the new expire date of the resource 9502 * 9503 * @throws CmsDataAccessException if something goes wrong 9504 * 9505 * @see CmsObject#setDateExpired(String, long, boolean) 9506 * @see I_CmsResourceType#setDateExpired(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9507 */ 9508 public void setDateExpired(CmsDbContext dbc, CmsResource resource, long dateExpired) throws CmsDataAccessException { 9509 9510 resource.setDateExpired(dateExpired); 9511 if (resource.getState().isUnchanged()) { 9512 resource.setState(CmsResource.STATE_CHANGED); 9513 } 9514 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false); 9515 9516 // modify the last modified project reference 9517 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false); 9518 // log 9519 log( 9520 dbc, 9521 new CmsLogEntry( 9522 dbc, 9523 resource.getStructureId(), 9524 CmsLogEntryType.RESOURCE_DATE_EXPIRED, 9525 new String[] {resource.getRootPath()}), 9526 false); 9527 9528 // clear the cache 9529 m_monitor.clearResourceCache(); 9530 9531 // fire the event 9532 Map<String, Object> data = new HashMap<String, Object>(2); 9533 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9534 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_TIMEFRAME)); 9535 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9536 } 9537 9538 /** 9539 * Changes the "last modified" timestamp of a resource.<p> 9540 * 9541 * @param dbc the current database context 9542 * @param resource the resource to touch 9543 * @param dateLastModified the new last modified date of the resource 9544 * 9545 * @throws CmsDataAccessException if something goes wrong 9546 * 9547 * @see CmsObject#setDateLastModified(String, long, boolean) 9548 * @see I_CmsResourceType#setDateLastModified(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9549 */ 9550 public void setDateLastModified(CmsDbContext dbc, CmsResource resource, long dateLastModified) 9551 throws CmsDataAccessException { 9552 9553 // modify the last modification date 9554 resource.setDateLastModified(dateLastModified); 9555 if (resource.getState().isUnchanged()) { 9556 resource.setState(CmsResource.STATE_CHANGED); 9557 } else if (resource.getState().isNew() && (resource.getSiblingCount() > 1)) { 9558 // in case of new resources with siblings make sure the state is correct 9559 resource.setState(CmsResource.STATE_CHANGED); 9560 } 9561 resource.setUserLastModified(dbc.currentUser().getId()); 9562 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false); 9563 9564 log( 9565 dbc, 9566 new CmsLogEntry( 9567 dbc, 9568 resource.getStructureId(), 9569 CmsLogEntryType.RESOURCE_TOUCHED, 9570 new String[] {resource.getRootPath()}), 9571 false); 9572 9573 // clear the cache 9574 m_monitor.clearResourceCache(); 9575 9576 // fire the event 9577 Map<String, Object> data = new HashMap<String, Object>(2); 9578 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9579 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_LASTMODIFIED)); 9580 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9581 } 9582 9583 /** 9584 * Changes the "release" date of a resource.<p> 9585 * 9586 * @param dbc the current database context 9587 * @param resource the resource to touch 9588 * @param dateReleased the new release date of the resource 9589 * 9590 * @throws CmsDataAccessException if something goes wrong 9591 * 9592 * @see CmsObject#setDateReleased(String, long, boolean) 9593 * @see I_CmsResourceType#setDateReleased(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9594 */ 9595 public void setDateReleased(CmsDbContext dbc, CmsResource resource, long dateReleased) 9596 throws CmsDataAccessException { 9597 9598 // modify the last modification date 9599 resource.setDateReleased(dateReleased); 9600 if (resource.getState().isUnchanged()) { 9601 resource.setState(CmsResource.STATE_CHANGED); 9602 } 9603 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false); 9604 9605 // modify the last modified project reference 9606 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false); 9607 // log it 9608 log( 9609 dbc, 9610 new CmsLogEntry( 9611 dbc, 9612 resource.getStructureId(), 9613 CmsLogEntryType.RESOURCE_DATE_RELEASED, 9614 new String[] {resource.getRootPath()}), 9615 false); 9616 9617 // clear the cache 9618 m_monitor.clearResourceCache(); 9619 9620 // fire the event 9621 Map<String, Object> data = new HashMap<String, Object>(2); 9622 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9623 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_TIMEFRAME)); 9624 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9625 } 9626 9627 /** 9628 * Sets a new parent group for an already existing group.<p> 9629 * 9630 * @param dbc the current database context 9631 * @param groupName the name of the group that should be written 9632 * @param parentGroupName the name of the parent group to set, 9633 * or <code>null</code> if the parent 9634 * group should be deleted. 9635 * 9636 * @throws CmsException if operation was not successful 9637 * @throws CmsDataAccessException if the group with <code>groupName</code> could not be read from VFS 9638 */ 9639 public void setParentGroup(CmsDbContext dbc, String groupName, String parentGroupName) 9640 throws CmsException, CmsDataAccessException { 9641 9642 CmsGroup group = readGroup(dbc, groupName); 9643 CmsUUID parentGroupId = CmsUUID.getNullUUID(); 9644 9645 // if the group exists, use its id, else set to unknown. 9646 if (parentGroupName != null) { 9647 parentGroupId = readGroup(dbc, parentGroupName).getId(); 9648 } 9649 9650 group.setParentId(parentGroupId); 9651 9652 // write the changes to the cms 9653 writeGroup(dbc, group); 9654 } 9655 9656 /** 9657 * Sets the password for a user.<p> 9658 * 9659 * @param dbc the current database context 9660 * @param username the name of the user 9661 * @param newPassword the new password 9662 * 9663 * @throws CmsException if operation was not successful 9664 * @throws CmsIllegalArgumentException if the user with the <code>username</code> was not found 9665 */ 9666 public void setPassword(CmsDbContext dbc, String username, String newPassword) 9667 throws CmsException, CmsIllegalArgumentException { 9668 9669 if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) { 9670 validatePassword(newPassword); 9671 } 9672 9673 // read the user as a system user to verify that the specified old password is correct 9674 CmsUser user = getUserDriver(dbc).readUser(dbc, username); 9675 // only continue if not found and read user from web might succeed 9676 getUserDriver(dbc).writePassword(dbc, username, null, newPassword); 9677 user.getAdditionalInfo().put( 9678 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 9679 "" + System.currentTimeMillis()); 9680 getUserDriver(dbc).writeUser(dbc, user); 9681 9682 // fire user modified event 9683 Map<String, Object> eventData = new HashMap<String, Object>(); 9684 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9685 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD); 9686 eventData.put( 9687 I_CmsEventListener.KEY_USER_CHANGES, 9688 Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA)); 9689 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9690 } 9691 9692 /** 9693 * Marks a subscribed resource as deleted.<p> 9694 * 9695 * @param dbc the database context 9696 * @param poolName the name of the database pool to use 9697 * @param resource the subscribed resource to mark as deleted 9698 * 9699 * @throws CmsException if something goes wrong 9700 */ 9701 public void setSubscribedResourceAsDeleted(CmsDbContext dbc, String poolName, CmsResource resource) 9702 throws CmsException { 9703 9704 getSubscriptionDriver().setSubscribedResourceAsDeleted(dbc, poolName, resource); 9705 } 9706 9707 /** 9708 * Moves an user to the given organizational unit.<p> 9709 * 9710 * @param dbc the current db context 9711 * @param orgUnit the organizational unit to add the resource to 9712 * @param user the user that is to be moved to the organizational unit 9713 * 9714 * @throws CmsException if something goes wrong 9715 * 9716 * @see org.opencms.security.CmsOrgUnitManager#setUsersOrganizationalUnit(CmsObject, String, String) 9717 */ 9718 public void setUsersOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsUser user) 9719 throws CmsException { 9720 9721 if (!getGroupsOfUser(dbc, user.getName(), false).isEmpty()) { 9722 throw new CmsDbConsistencyException( 9723 Messages.get().container(Messages.ERR_ORGUNIT_MOVE_USER_2, orgUnit.getName(), user.getName())); 9724 } 9725 9726 // move the principal 9727 getUserDriver(dbc).setUsersOrganizationalUnit(dbc, orgUnit, user); 9728 // remove the principal from cache 9729 m_monitor.clearUserCache(user); 9730 9731 if (!dbc.getProjectId().isNullUUID()) { 9732 // user modified event is not needed 9733 return; 9734 } 9735 // fire user modified event 9736 Map<String, Object> eventData = new HashMap<String, Object>(); 9737 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9738 eventData.put(I_CmsEventListener.KEY_OU_NAME, user.getOuFqn()); 9739 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU); 9740 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9741 } 9742 9743 /** 9744 * Subscribes the user or group to the resource.<p> 9745 * 9746 * @param dbc the database context 9747 * @param poolName the name of the database pool to use 9748 * @param principal the principal that subscribes to the resource 9749 * @param resource the resource to subscribe to 9750 * 9751 * @throws CmsException if something goes wrong 9752 */ 9753 public void subscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource) 9754 throws CmsException { 9755 9756 getSubscriptionDriver().subscribeResourceFor(dbc, poolName, principal, resource); 9757 } 9758 9759 /** 9760 * Undelete the resource.<p> 9761 * 9762 * @param dbc the current database context 9763 * @param resource the name of the resource to apply this operation to 9764 * 9765 * @throws CmsException if something goes wrong 9766 * 9767 * @see CmsObject#undeleteResource(String, boolean) 9768 * @see I_CmsResourceType#undelete(CmsObject, CmsSecurityManager, CmsResource, boolean) 9769 */ 9770 public void undelete(CmsDbContext dbc, CmsResource resource) throws CmsException { 9771 9772 if (!resource.getState().isDeleted()) { 9773 throw new CmsVfsException( 9774 Messages.get().container( 9775 Messages.ERR_UNDELETE_FOR_RESOURCE_DELETED_1, 9776 dbc.removeSiteRoot(resource.getRootPath()))); 9777 } 9778 9779 // set the state to changed 9780 resource.setState(CmsResourceState.STATE_CHANGED); 9781 // perform the changes 9782 updateState(dbc, resource, false); 9783 // log it 9784 log( 9785 dbc, 9786 new CmsLogEntry( 9787 dbc, 9788 resource.getStructureId(), 9789 CmsLogEntryType.RESOURCE_UNDELETED, 9790 new String[] {resource.getRootPath()}), 9791 false); 9792 // clear the cache 9793 m_monitor.clearResourceCache(); 9794 9795 // fire change event 9796 Map<String, Object> data = new HashMap<String, Object>(2); 9797 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9798 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE)); 9799 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9800 } 9801 9802 /** 9803 * Undos all changes in the resource by restoring the version from the 9804 * online project to the current offline project.<p> 9805 * 9806 * @param dbc the current database context 9807 * @param resource the name of the resource to apply this operation to 9808 * @param mode the undo mode, one of the <code>{@link org.opencms.file.CmsResource.CmsResourceUndoMode}#UNDO_XXX</code> constants 9809 * please note that the recursive flag is ignored at this level 9810 * 9811 * @throws CmsException if something goes wrong 9812 * 9813 * @see CmsObject#undoChanges(String, CmsResource.CmsResourceUndoMode) 9814 * @see I_CmsResourceType#undoChanges(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode) 9815 */ 9816 public void undoChanges(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceUndoMode mode) 9817 throws CmsException { 9818 9819 if (resource.getState().isNew()) { 9820 // undo changes is impossible on a new resource 9821 throw new CmsVfsException(Messages.get().container(Messages.ERR_UNDO_CHANGES_FOR_RESOURCE_NEW_0)); 9822 } 9823 9824 // we need this for later use 9825 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 9826 // read the resource from the online project 9827 CmsResource onlineResource = getVfsDriver( 9828 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getStructureId(), true); 9829 9830 CmsResource onlineResourceByPath = null; 9831 try { 9832 // this is needed to figure out if a moved resource overwrote a deleted one 9833 onlineResourceByPath = getVfsDriver( 9834 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getRootPath(), true); 9835 9836 // force undo move operation if needed 9837 if (!mode.isUndoMove() && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) { 9838 mode = mode.includeMove(); 9839 } 9840 } catch (Exception e) { 9841 // ok 9842 } 9843 9844 boolean moved = !onlineResource.getRootPath().equals(resource.getRootPath()); 9845 // undo move operation if required 9846 if (moved && mode.isUndoMove()) { 9847 moveResource(dbc, resource, onlineResource.getRootPath(), true); 9848 if ((onlineResourceByPath != null) 9849 && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) { 9850 // was moved over deleted, so the deleted file has to be undone 9851 undoContentChanges(dbc, onlineProject, null, onlineResourceByPath, CmsResource.STATE_UNCHANGED, true); 9852 } 9853 } 9854 // undo content changes 9855 CmsResourceState newState = CmsResource.STATE_UNCHANGED; 9856 if (moved && !mode.isUndoMove()) { 9857 newState = CmsResource.STATE_CHANGED; 9858 } 9859 undoContentChanges(dbc, onlineProject, resource, onlineResource, newState, moved && mode.isUndoMove()); 9860 // because undoContentChanges deletes the offline resource internally, we have 9861 // to write an entry to the log table to prevent the resource from appearing in the 9862 // user's publish list. 9863 log( 9864 dbc, 9865 new CmsLogEntry( 9866 dbc, 9867 resource.getStructureId(), 9868 CmsLogEntryType.RESOURCE_CHANGES_UNDONE, 9869 new String[] {resource.getRootPath()}), 9870 true); 9871 9872 } 9873 9874 /** 9875 * Unlocks all resources in the given project.<p> 9876 * 9877 * @param project the project to unlock the resources in 9878 */ 9879 public void unlockProject(CmsProject project) { 9880 9881 // unlock all resources in the project 9882 m_lockManager.removeResourcesInProject(project.getUuid(), false); 9883 m_monitor.clearResourceCache(); 9884 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT, CmsMemoryMonitor.CacheType.PERMISSION); 9885 } 9886 9887 /** 9888 * Unlocks a resource.<p> 9889 * 9890 * @param dbc the current database context 9891 * @param resource the resource to unlock 9892 * @param force <code>true</code>, if a resource is forced to get unlocked, no matter by which user and in which project the resource is currently locked 9893 * @param removeSystemLock <code>true</code>, if you also want to remove system locks 9894 * 9895 * @throws CmsException if something goes wrong 9896 * 9897 * @see CmsObject#unlockResource(String) 9898 * @see I_CmsResourceType#unlockResource(CmsObject, CmsSecurityManager, CmsResource) 9899 */ 9900 public void unlockResource(CmsDbContext dbc, CmsResource resource, boolean force, boolean removeSystemLock) 9901 throws CmsException { 9902 9903 // update the resource cache 9904 m_monitor.clearResourceCache(); 9905 9906 // now update lock status 9907 m_lockManager.removeResource(dbc, resource, force, removeSystemLock); 9908 9909 // we must also clear the permission cache 9910 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION); 9911 9912 // fire resource modification event 9913 Map<String, Object> data = new HashMap<String, Object>(2); 9914 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9915 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(NOTHING_CHANGED)); 9916 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9917 } 9918 9919 /** 9920 * Unsubscribes all deleted resources that were deleted before the specified time stamp.<p> 9921 * 9922 * @param dbc the database context 9923 * @param poolName the name of the database pool to use 9924 * @param deletedTo the time stamp to which the resources have been deleted 9925 * 9926 * @throws CmsException if something goes wrong 9927 */ 9928 public void unsubscribeAllDeletedResources(CmsDbContext dbc, String poolName, long deletedTo) throws CmsException { 9929 9930 getSubscriptionDriver().unsubscribeAllDeletedResources(dbc, poolName, deletedTo); 9931 } 9932 9933 /** 9934 * Unsubscribes the principal from all resources.<p> 9935 * 9936 * @param dbc the database context 9937 * @param poolName the name of the database pool to use 9938 * @param principal the principal that unsubscribes from all resources 9939 * 9940 * @throws CmsException if something goes wrong 9941 */ 9942 public void unsubscribeAllResourcesFor(CmsDbContext dbc, String poolName, CmsPrincipal principal) 9943 throws CmsException { 9944 9945 getSubscriptionDriver().unsubscribeAllResourcesFor(dbc, poolName, principal); 9946 9947 } 9948 9949 /** 9950 * Unsubscribes the principal from the resource.<p> 9951 * 9952 * @param dbc the database context 9953 * @param poolName the name of the database pool to use 9954 * @param principal the principal that unsubscribes from the resource 9955 * @param resource the resource to unsubscribe from 9956 * 9957 * @throws CmsException if something goes wrong 9958 */ 9959 public void unsubscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource) 9960 throws CmsException { 9961 9962 getSubscriptionDriver().unsubscribeResourceFor(dbc, poolName, principal, resource); 9963 } 9964 9965 /** 9966 * Unsubscribes all groups and users from the resource.<p> 9967 * 9968 * @param dbc the database context 9969 * @param poolName the name of the database pool to use 9970 * @param resource the resource to unsubscribe all groups and users from 9971 * 9972 * @throws CmsException if something goes wrong 9973 */ 9974 public void unsubscribeResourceForAll(CmsDbContext dbc, String poolName, CmsResource resource) throws CmsException { 9975 9976 getSubscriptionDriver().unsubscribeResourceForAll(dbc, poolName, resource); 9977 } 9978 9979 /** 9980 * Update the export points.<p> 9981 * 9982 * All files and folders "inside" an export point are written.<p> 9983 * 9984 * @param dbc the current database context 9985 */ 9986 public void updateExportPoints(CmsDbContext dbc) { 9987 9988 try { 9989 // read the export points and return immediately if there are no export points at all 9990 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 9991 exportPoints.addAll(OpenCms.getExportPoints()); 9992 exportPoints.addAll(OpenCms.getModuleManager().getExportPoints()); 9993 if (exportPoints.size() == 0) { 9994 if (LOG.isWarnEnabled()) { 9995 LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0)); 9996 } 9997 return; 9998 } 9999 10000 // create the driver to write the export points 10001 I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver( 10002 exportPoints); 10003 10004 // the export point hash table contains RFS export paths keyed by their internal VFS paths 10005 Iterator<String> i = exportPointDriver.getExportPointPaths().iterator(); 10006 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10007 while (i.hasNext()) { 10008 String currentExportPoint = i.next(); 10009 10010 // print some report messages 10011 if (LOG.isInfoEnabled()) { 10012 LOG.info(Messages.get().getBundle().key(Messages.LOG_WRITE_EXPORT_POINT_1, currentExportPoint)); 10013 } 10014 10015 try { 10016 CmsResourceFilter filter = CmsResourceFilter.DEFAULT; 10017 List<CmsResource> resources = vfsDriver.readResourceTree( 10018 dbc, 10019 CmsProject.ONLINE_PROJECT_ID, 10020 currentExportPoint, 10021 filter.getType(), 10022 filter.getState(), 10023 filter.getModifiedAfter(), 10024 filter.getModifiedBefore(), 10025 filter.getReleaseAfter(), 10026 filter.getReleaseBefore(), 10027 filter.getExpireAfter(), 10028 filter.getExpireBefore(), 10029 CmsDriverManager.READMODE_INCLUDE_TREE 10030 | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0) 10031 | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0)); 10032 10033 Iterator<CmsResource> j = resources.iterator(); 10034 while (j.hasNext()) { 10035 CmsResource currentResource = j.next(); 10036 10037 if (currentResource.isFolder()) { 10038 // export the folder 10039 exportPointDriver.createFolder(currentResource.getRootPath(), currentExportPoint); 10040 } else { 10041 // try to create the exportpoint folder 10042 exportPointDriver.createFolder(currentExportPoint, currentExportPoint); 10043 byte[] onlineContent = vfsDriver.readContent( 10044 dbc, 10045 CmsProject.ONLINE_PROJECT_ID, 10046 currentResource.getResourceId()); 10047 // export the file content online 10048 exportPointDriver.writeFile( 10049 currentResource.getRootPath(), 10050 currentExportPoint, 10051 onlineContent); 10052 } 10053 } 10054 } catch (CmsException e) { 10055 // there might exist export points without corresponding resources in the VFS 10056 // -> ignore exceptions which are not "resource not found" exception quiet here 10057 if (e instanceof CmsVfsResourceNotFoundException) { 10058 if (LOG.isErrorEnabled()) { 10059 LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e); 10060 } 10061 } 10062 } 10063 } 10064 } catch (Exception e) { 10065 if (LOG.isErrorEnabled()) { 10066 LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e); 10067 } 10068 } 10069 } 10070 10071 /** 10072 * Updates the last login date on the given user to the current time.<p> 10073 * 10074 * @param dbc the current database context 10075 * @param user the user to be updated 10076 * 10077 * @throws CmsException if operation was not successful 10078 */ 10079 public void updateLastLoginDate(CmsDbContext dbc, CmsUser user) throws CmsException { 10080 10081 m_monitor.clearUserCache(user); 10082 // set the last login time to the current time 10083 user.setLastlogin(System.currentTimeMillis()); 10084 dbc.setAttribute(ATTRIBUTE_LOGIN, user.getName()); 10085 getUserDriver(dbc).writeUser(dbc, user); 10086 // update cache 10087 m_monitor.cacheUser(user); 10088 10089 // invalidate all user dependent caches 10090 m_monitor.flushCache( 10091 CmsMemoryMonitor.CacheType.ACL, 10092 CmsMemoryMonitor.CacheType.GROUP, 10093 CmsMemoryMonitor.CacheType.ORG_UNIT, 10094 CmsMemoryMonitor.CacheType.USER_LIST, 10095 CmsMemoryMonitor.CacheType.PERMISSION, 10096 CmsMemoryMonitor.CacheType.RESOURCE_LIST); 10097 10098 // fire user modified event 10099 Map<String, Object> eventData = new HashMap<String, Object>(); 10100 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 10101 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 10102 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 10103 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(CmsUser.FLAG_LAST_LOGIN)); 10104 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 10105 } 10106 10107 /** 10108 * Logs everything that has not been written to DB jet.<p> 10109 * 10110 * @param dbc the current db context 10111 * 10112 * @throws CmsDataAccessException if something goes wrong 10113 */ 10114 public void updateLog(CmsDbContext dbc) throws CmsDataAccessException { 10115 10116 synchronized (m_publishListUpdateLock) { 10117 10118 if (m_log.isEmpty()) { 10119 return; 10120 } 10121 10122 List<CmsLogEntry> log = new ArrayList<CmsLogEntry>(m_log); 10123 m_log.clear(); 10124 String logTableEnabledStr = (String)OpenCms.getRuntimeProperty(PARAM_LOG_TABLE_ENABLED); 10125 if (Boolean.parseBoolean(logTableEnabledStr)) { // defaults to 'false' if value not set 10126 m_projectDriver.log(dbc, log); 10127 } 10128 A_CmsLogPublishListConverter converter = null; 10129 switch (OpenCms.getPublishManager().getPublishListRemoveMode()) { 10130 case currentUser: 10131 converter = new CmsLogPublishListConverterCurrentUser(); 10132 break; 10133 case allUsers: 10134 default: 10135 converter = new CmsLogPublishListConverterAllUsers(); 10136 break; 10137 } 10138 for (CmsLogEntry entry : log) { 10139 converter.add(entry); 10140 } 10141 converter.writeChangesToDatabase(dbc, m_projectDriver); 10142 } 10143 } 10144 10145 /** 10146 * Updates/Creates the given relations for the given resource.<p> 10147 * 10148 * @param dbc the db context 10149 * @param resource the resource to update the relations for 10150 * @param links the links to consider for updating 10151 * @param updateSiblingState if true, sets the state of siblings whose relations have changed to 'changed' (unless they are new or deleted) 10152 * 10153 * @throws CmsException if something goes wrong 10154 * 10155 * @see CmsSecurityManager#updateRelationsForResource(CmsRequestContext, CmsResource, List, boolean) 10156 */ 10157 public void updateRelationsForResource( 10158 CmsDbContext dbc, 10159 CmsResource resource, 10160 List<CmsLink> links, 10161 boolean updateSiblingState) 10162 throws CmsException { 10163 10164 if (links == null) { 10165 links = new ArrayList<>(); 10166 } 10167 10168 // create new relation information 10169 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10170 Iterator<CmsLink> itLinks = links.iterator(); 10171 Set<CmsRelation> relationsForOriginalResource = new HashSet<>(); 10172 while (itLinks.hasNext()) { 10173 CmsLink link = itLinks.next(); 10174 if (link.isInternal()) { // only update internal links 10175 if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) { 10176 // only an anchor 10177 continue; 10178 } 10179 CmsUUID targetId = link.getStructureId(); 10180 String destPath = link.getTarget(); 10181 10182 if (targetId != null) { 10183 // the link target may not be a VFS path even if the link id is a structure id, 10184 // so if possible, we read the resource for the id and set the relation target to its 10185 // real root path. 10186 try { 10187 CmsResource destRes = readResource(dbc, targetId, CmsResourceFilter.ALL); 10188 destPath = destRes.getRootPath(); 10189 } catch (CmsVfsResourceNotFoundException e) { 10190 // ignore 10191 } 10192 } 10193 10194 CmsRelation originalRelation = new CmsRelation( 10195 resource.getStructureId(), 10196 resource.getRootPath(), 10197 link.getStructureId(), 10198 destPath, 10199 link.getType()); 10200 relationsForOriginalResource.add(originalRelation); 10201 } 10202 } 10203 List<CmsResource> siblings = resource.getSiblingCount() == 1 10204 ? Arrays.asList(resource) 10205 : readSiblings(dbc, resource, CmsResourceFilter.ALL); 10206 10207 for (CmsResource sibling : siblings) { 10208 // For each sibling, we determine which 'defined in content' relations it SHOULD have, 10209 // and only update the relations if that set differs from the ones it actually has. 10210 // If the updateSiblingState flag is set, then for siblings, we update the structure 10211 // state to changed (unless the state was 'deleted' or 'new'). 10212 // This is so that even if the user later publishes only one sibling, the other sibling will still 10213 // show up as changed in the GUI, so the user can publish it separately (with its updated relations). 10214 Set<CmsRelation> relationsForSibling = relationsForOriginalResource.stream().map( 10215 relation -> new CmsRelation( 10216 sibling.getStructureId(), 10217 sibling.getRootPath(), 10218 relation.getTargetId(), 10219 relation.getTargetPath(), 10220 relation.getType())).collect(Collectors.toSet()); 10221 Set<CmsRelation> existingRelations = new HashSet<>( 10222 vfsDriver.readRelations( 10223 dbc, 10224 dbc.currentProject().getUuid(), 10225 sibling, 10226 CmsRelationFilter.TARGETS.filterDefinedInContent())); 10227 if (!existingRelations.equals(relationsForSibling)) { 10228 vfsDriver.deleteRelations( 10229 dbc, 10230 dbc.currentProject().getUuid(), 10231 sibling, 10232 CmsRelationFilter.TARGETS.filterDefinedInContent()); 10233 for (CmsRelation relation : relationsForSibling) { 10234 vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation); 10235 } 10236 if (!sibling.getState().isDeleted() 10237 && !sibling.getState().isNew() 10238 && (siblings.size() > 1) 10239 && updateSiblingState) { 10240 sibling.setState(CmsResource.STATE_CHANGED); 10241 vfsDriver.writeResourceState( 10242 dbc, 10243 dbc.currentProject(), 10244 sibling, 10245 CmsDriverManager.UPDATE_STRUCTURE_STATE, 10246 false); 10247 } 10248 } 10249 } 10250 } 10251 10252 /** 10253 * Returns <code>true</code> if a user is member of the given group.<p> 10254 * 10255 * @param dbc the current database context 10256 * @param username the name of the user to check 10257 * @param groupname the name of the group to check 10258 * @param readRoles if to read roles or groups 10259 * 10260 * @return <code>true</code>, if the user is in the group, <code>false</code> otherwise 10261 * 10262 * @throws CmsException if something goes wrong 10263 */ 10264 public boolean userInGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 10265 throws CmsException { 10266 10267 List<CmsGroup> groups = getGroupsOfUser(dbc, username, readRoles); 10268 for (int i = 0; i < groups.size(); i++) { 10269 CmsGroup group = groups.get(i); 10270 if (groupname.equals(group.getName()) || groupname.substring(1).equals(group.getName())) { 10271 return true; 10272 } 10273 } 10274 return false; 10275 } 10276 10277 /** 10278 * This method checks if a new password follows the rules for 10279 * new passwords, which are defined by a Class implementing the 10280 * <code>{@link org.opencms.security.I_CmsPasswordHandler}</code> 10281 * interface and configured in the opencms.properties file.<p> 10282 * 10283 * If this method throws no exception the password is valid.<p> 10284 * 10285 * @param password the new password that has to be checked 10286 * 10287 * @throws CmsSecurityException if the password is not valid 10288 */ 10289 public void validatePassword(String password) throws CmsSecurityException { 10290 10291 OpenCms.getPasswordHandler().validatePassword(password); 10292 } 10293 10294 /** 10295 * Validates the relations for the given resources.<p> 10296 * 10297 * @param dbc the database context 10298 * @param publishList the resources to validate during publishing 10299 * @param report a report to write the messages to 10300 * 10301 * @return a map with lists of invalid links 10302 * (<code>{@link org.opencms.relations.CmsRelation}}</code> objects) 10303 * keyed by root paths 10304 * 10305 * @throws Exception if something goes wrong 10306 */ 10307 public Map<String, List<CmsRelation>> validateRelations( 10308 CmsDbContext dbc, 10309 CmsPublishList publishList, 10310 I_CmsReport report) 10311 throws Exception { 10312 10313 return m_htmlLinkValidator.validateResources(dbc, publishList, report); 10314 } 10315 10316 /** 10317 * Writes an access control entries to a given resource.<p> 10318 * 10319 * @param dbc the current database context 10320 * @param resource the resource 10321 * @param ace the entry to write 10322 * 10323 * @throws CmsException if something goes wrong 10324 */ 10325 public void writeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsAccessControlEntry ace) 10326 throws CmsException { 10327 10328 // write the new ace 10329 getUserDriver(dbc).writeAccessControlEntry(dbc, dbc.currentProject(), ace); 10330 10331 // log it 10332 log( 10333 dbc, 10334 new CmsLogEntry( 10335 dbc, 10336 resource.getStructureId(), 10337 CmsLogEntryType.RESOURCE_PERMISSIONS, 10338 new String[] {resource.getRootPath()}), 10339 false); 10340 10341 // update the "last modified" information 10342 setDateLastModified(dbc, resource, resource.getDateLastModified()); 10343 10344 // clear the cache 10345 m_monitor.clearAccessControlListCache(); 10346 10347 // fire a resource modification event 10348 Map<String, Object> data = new HashMap<String, Object>(2); 10349 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10350 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 10351 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10352 } 10353 10354 /** 10355 * Writes all export points into the file system for the publish task 10356 * specified by trhe given publish history ID.<p> 10357 * 10358 * @param dbc the current database context 10359 * @param report an I_CmsReport instance to print output message, or null to write messages to the log file 10360 * @param publishHistoryId ID to identify the publish task in the publish history 10361 */ 10362 public void writeExportPoints(CmsDbContext dbc, I_CmsReport report, CmsUUID publishHistoryId) { 10363 10364 boolean printReportHeaders = false; 10365 List<CmsPublishedResource> publishedResources = null; 10366 try { 10367 // read the "published resources" for the specified publish history ID 10368 publishedResources = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId); 10369 } catch (CmsException e) { 10370 if (LOG.isErrorEnabled()) { 10371 LOG.error( 10372 Messages.get().getBundle().key(Messages.ERR_READ_PUBLISHED_RESOURCES_FOR_ID_1, publishHistoryId), 10373 e); 10374 } 10375 } 10376 if ((publishedResources == null) || publishedResources.isEmpty()) { 10377 if (LOG.isWarnEnabled()) { 10378 LOG.warn(Messages.get().getBundle().key(Messages.LOG_EMPTY_PUBLISH_HISTORY_1, publishHistoryId)); 10379 } 10380 return; 10381 } 10382 10383 // read the export points and return immediately if there are no export points at all 10384 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 10385 exportPoints.addAll(OpenCms.getExportPoints()); 10386 exportPoints.addAll(OpenCms.getModuleManager().getExportPoints()); 10387 if (exportPoints.size() == 0) { 10388 if (LOG.isWarnEnabled()) { 10389 LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0)); 10390 } 10391 return; 10392 } 10393 10394 // create the driver to write the export points 10395 I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver( 10396 exportPoints); 10397 10398 // the report may be null if the export point write was started by an event 10399 if (report == null) { 10400 if (dbc.getRequestContext() != null) { 10401 report = new CmsLogReport(dbc.getRequestContext().getLocale(), getClass()); 10402 } else { 10403 report = new CmsLogReport(CmsLocaleManager.getDefaultLocale(), getClass()); 10404 } 10405 } 10406 10407 // iterate over all published resources to export them 10408 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10409 Iterator<CmsPublishedResource> i = publishedResources.iterator(); 10410 while (i.hasNext()) { 10411 CmsPublishedResource currentPublishedResource = i.next(); 10412 String currentExportPoint = exportPointDriver.getExportPoint(currentPublishedResource.getRootPath()); 10413 10414 if (currentExportPoint != null) { 10415 if (!printReportHeaders) { 10416 report.println( 10417 Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_BEGIN_0), 10418 I_CmsReport.FORMAT_HEADLINE); 10419 printReportHeaders = true; 10420 } 10421 10422 // print report message 10423 if (currentPublishedResource.getState().isDeleted()) { 10424 report.print( 10425 Messages.get().container(Messages.RPT_EXPORT_POINTS_DELETE_0), 10426 I_CmsReport.FORMAT_NOTE); 10427 } else { 10428 report.print(Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_0), I_CmsReport.FORMAT_NOTE); 10429 } 10430 report.print( 10431 org.opencms.report.Messages.get().container( 10432 org.opencms.report.Messages.RPT_ARGUMENT_1, 10433 currentPublishedResource.getRootPath())); 10434 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 10435 10436 if (currentPublishedResource.isFolder()) { 10437 // export the folder 10438 if (currentPublishedResource.getState().isDeleted()) { 10439 exportPointDriver.deleteResource(currentPublishedResource.getRootPath(), currentExportPoint); 10440 } else { 10441 exportPointDriver.createFolder(currentPublishedResource.getRootPath(), currentExportPoint); 10442 } 10443 report.println( 10444 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 10445 I_CmsReport.FORMAT_OK); 10446 } else { 10447 // export the file 10448 try { 10449 if (currentPublishedResource.getState().isDeleted()) { 10450 exportPointDriver.deleteResource( 10451 currentPublishedResource.getRootPath(), 10452 currentExportPoint); 10453 } else { 10454 // read the file content online 10455 byte[] onlineContent = vfsDriver.readContent( 10456 dbc, 10457 CmsProject.ONLINE_PROJECT_ID, 10458 currentPublishedResource.getResourceId()); 10459 exportPointDriver.writeFile( 10460 currentPublishedResource.getRootPath(), 10461 currentExportPoint, 10462 onlineContent); 10463 } 10464 report.println( 10465 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 10466 I_CmsReport.FORMAT_OK); 10467 } catch (CmsException e) { 10468 if (LOG.isErrorEnabled()) { 10469 LOG.error( 10470 Messages.get().getBundle().key( 10471 Messages.LOG_WRITE_EXPORT_POINT_ERROR_1, 10472 currentPublishedResource.getRootPath()), 10473 e); 10474 } 10475 report.println( 10476 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 10477 I_CmsReport.FORMAT_ERROR); 10478 } 10479 } 10480 } 10481 } 10482 if (printReportHeaders) { 10483 report.println( 10484 Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_END_0), 10485 I_CmsReport.FORMAT_HEADLINE); 10486 } 10487 } 10488 10489 /** 10490 * Writes a resource to the OpenCms VFS, including it's content.<p> 10491 * 10492 * Applies only to resources of type <code>{@link CmsFile}</code> 10493 * i.e. resources that have a binary content attached.<p> 10494 * 10495 * Certain resource types might apply content validation or transformation rules 10496 * before the resource is actually written to the VFS. The returned result 10497 * might therefore be a modified version from the provided original.<p> 10498 * 10499 * @param dbc the current database context 10500 * @param resource the resource to apply this operation to 10501 * 10502 * @return the written resource (may have been modified) 10503 * 10504 * @throws CmsException if something goes wrong 10505 * 10506 * @see CmsObject#writeFile(CmsFile) 10507 * @see I_CmsResourceType#writeFile(CmsObject, CmsSecurityManager, CmsFile) 10508 */ 10509 public CmsFile writeFile(CmsDbContext dbc, CmsFile resource) throws CmsException { 10510 10511 resource.setUserLastModified(dbc.currentUser().getId()); 10512 resource.setContents(resource.getContents()); // to be sure the content date is updated 10513 10514 getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), resource, UPDATE_RESOURCE_STATE); 10515 10516 byte[] contents = resource.getContents(); 10517 getVfsDriver(dbc).writeContent(dbc, resource, contents); 10518 // log it 10519 log( 10520 dbc, 10521 new CmsLogEntry( 10522 dbc, 10523 resource.getStructureId(), 10524 CmsLogEntryType.RESOURCE_CONTENT_MODIFIED, 10525 new String[] {resource.getRootPath()}), 10526 false); 10527 10528 // read the file back from db 10529 resource = new CmsFile(readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL)); 10530 resource.setContents(contents); 10531 10532 deleteRelationsWithSiblings(dbc, resource); 10533 10534 // update the cache 10535 m_monitor.clearResourceCache(); 10536 10537 Map<String, Object> data = new HashMap<String, Object>(2); 10538 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10539 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_CONTENT)); 10540 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10541 10542 return resource; 10543 } 10544 10545 /** 10546 * Writes an already existing group.<p> 10547 * 10548 * The group id has to be a valid OpenCms group id.<br> 10549 * 10550 * The group with the given id will be completely overridden 10551 * by the given data.<p> 10552 * 10553 * @param dbc the current database context 10554 * @param group the group that should be written 10555 * 10556 * @throws CmsException if operation was not successful 10557 */ 10558 public void writeGroup(CmsDbContext dbc, CmsGroup group) throws CmsException { 10559 10560 CmsGroup oldGroup = readGroup(dbc, group.getName()); 10561 m_monitor.uncacheGroup(oldGroup); 10562 getUserDriver(dbc).writeGroup(dbc, group); 10563 m_monitor.cacheGroup(group); 10564 10565 if (!dbc.getProjectId().isNullUUID()) { 10566 // group modified event is not needed 10567 return; 10568 } 10569 // fire group modified event 10570 Map<String, Object> eventData = new HashMap<String, Object>(); 10571 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 10572 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, oldGroup.getName()); 10573 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_WRITE); 10574 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 10575 } 10576 10577 /** 10578 * Creates an historical entry of the current project.<p> 10579 * 10580 * @param dbc the current database context 10581 * @param publishTag the version 10582 * @param publishDate the date of publishing 10583 * 10584 * @throws CmsDataAccessException if operation was not successful 10585 */ 10586 public void writeHistoryProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException { 10587 10588 getHistoryDriver(dbc).writeProject(dbc, publishTag, publishDate); 10589 } 10590 10591 /** 10592 * Writes the locks that are currently stored in-memory to the database to allow restoring them 10593 * in future server startups.<p> 10594 * 10595 * This overwrites the locks previously stored in the underlying database table.<p> 10596 * 10597 * @param dbc the current database context 10598 * 10599 * @throws CmsException if something goes wrong 10600 */ 10601 public void writeLocks(CmsDbContext dbc) throws CmsException { 10602 10603 m_lockManager.writeLocks(dbc); 10604 } 10605 10606 /** 10607 * Writes an already existing organizational unit.<p> 10608 * 10609 * The organizational unit id has to be a valid OpenCms organizational unit id.<br> 10610 * 10611 * The organizational unit with the given id will be completely overridden 10612 * by the given data.<p> 10613 * 10614 * @param dbc the current db context 10615 * @param organizationalUnit the organizational unit that should be written 10616 * 10617 * @throws CmsException if operation was not successful 10618 * 10619 * @see org.opencms.security.CmsOrgUnitManager#writeOrganizationalUnit(CmsObject, CmsOrganizationalUnit) 10620 */ 10621 public void writeOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) 10622 throws CmsException { 10623 10624 m_monitor.uncacheOrgUnit(organizationalUnit); 10625 getUserDriver(dbc).writeOrganizationalUnit(dbc, organizationalUnit); 10626 10627 // create a publish list for the 'virtual' publish event 10628 CmsResource ouRes = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT); 10629 CmsPublishList pl = new CmsPublishList(ouRes, false); 10630 pl.add(ouRes, false); 10631 10632 getProjectDriver(dbc).writePublishHistory( 10633 dbc, 10634 pl.getPublishHistoryId(), 10635 new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW)); 10636 10637 // fire the 'virtual' publish event 10638 Map<String, Object> eventData = new HashMap<String, Object>(); 10639 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 10640 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 10641 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 10642 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 10643 OpenCms.fireCmsEvent(afterPublishEvent); 10644 10645 m_monitor.cacheOrgUnit(organizationalUnit); 10646 } 10647 10648 /** 10649 * Writes an already existing project.<p> 10650 * 10651 * The project id has to be a valid OpenCms project id.<br> 10652 * 10653 * The project with the given id will be completely overridden 10654 * by the given data.<p> 10655 * 10656 * @param dbc the current database context 10657 * @param project the project that should be written 10658 * 10659 * @throws CmsException if operation was not successful 10660 */ 10661 public void writeProject(CmsDbContext dbc, CmsProject project) throws CmsException { 10662 10663 m_monitor.uncacheProject(project); 10664 getProjectDriver(dbc).writeProject(dbc, project); 10665 m_monitor.cacheProject(project); 10666 } 10667 10668 /** 10669 * Writes a new project into the PROJECT_LASTMODIFIED field of a resource record.<p> 10670 * 10671 * @param dbc the current database context 10672 * @param resource the resource which should be modified 10673 * @param projectId the project id to write 10674 * 10675 * @throws CmsDataAccessException if the database access fails 10676 */ 10677 public void writeProjectLastModified(CmsDbContext dbc, CmsResource resource, CmsUUID projectId) 10678 throws CmsDataAccessException { 10679 10680 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10681 vfsDriver.writeLastModifiedProjectId(dbc, dbc.currentProject(), projectId, resource); 10682 } 10683 10684 /** 10685 * Writes a property for a specified resource.<p> 10686 * 10687 * @param dbc the current database context 10688 * @param resource the resource to write the property for 10689 * @param property the property to write 10690 * 10691 * @throws CmsException if something goes wrong 10692 * 10693 * @see CmsObject#writePropertyObject(String, CmsProperty) 10694 * @see I_CmsResourceType#writePropertyObject(CmsObject, CmsSecurityManager, CmsResource, CmsProperty) 10695 */ 10696 public void writePropertyObject(CmsDbContext dbc, CmsResource resource, CmsProperty property) throws CmsException { 10697 10698 try { 10699 if (property == CmsProperty.getNullProperty()) { 10700 // skip empty or null properties 10701 return; 10702 } 10703 10704 // test if and what state should be updated 10705 // 0: none, 1: structure, 2: resource 10706 int updateState = getUpdateState(dbc, resource, Collections.singletonList(property)); 10707 10708 // write the property 10709 getVfsDriver(dbc).writePropertyObject(dbc, dbc.currentProject(), resource, property); 10710 10711 if (updateState > 0) { 10712 updateState(dbc, resource, updateState == 2); 10713 } 10714 // log it 10715 log( 10716 dbc, 10717 new CmsLogEntry( 10718 dbc, 10719 resource.getStructureId(), 10720 CmsLogEntryType.RESOURCE_PROPERTIES, 10721 new String[] {resource.getRootPath()}), 10722 false); 10723 10724 } finally { 10725 // update the driver manager cache 10726 m_monitor.clearResourceCache(); 10727 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 10728 10729 // fire an event that a property of a resource has been modified 10730 Map<String, Object> data = new HashMap<String, Object>(); 10731 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10732 data.put("property", property); 10733 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_PROPERTY_MODIFIED, data)); 10734 } 10735 } 10736 10737 /** 10738 * Writes a list of properties for a specified resource.<p> 10739 * 10740 * Code calling this method has to ensure that the no properties 10741 * <code>a, b</code> are contained in the specified list so that <code>a.equals(b)</code>, 10742 * otherwise an exception is thrown.<p> 10743 * 10744 * @param dbc the current database context 10745 * @param resource the resource to write the properties for 10746 * @param properties the list of properties to write 10747 * @param updateState if <code>true</code> the state of the resource will be updated 10748 * 10749 * @throws CmsException if something goes wrong 10750 * 10751 * @see CmsObject#writePropertyObjects(String, List) 10752 * @see I_CmsResourceType#writePropertyObjects(CmsObject, CmsSecurityManager, CmsResource, List) 10753 */ 10754 public void writePropertyObjects( 10755 CmsDbContext dbc, 10756 CmsResource resource, 10757 List<CmsProperty> properties, 10758 boolean updateState) 10759 throws CmsException { 10760 10761 if ((properties == null) || (properties.size() == 0)) { 10762 // skip empty or null lists 10763 return; 10764 } 10765 10766 try { 10767 // the specified list must not contain two or more equal property objects 10768 for (int i = 0, n = properties.size(); i < n; i++) { 10769 Set<String> keyValidationSet = new HashSet<String>(); 10770 CmsProperty property = properties.get(i); 10771 if (!keyValidationSet.contains(property.getName())) { 10772 keyValidationSet.add(property.getName()); 10773 } else { 10774 throw new CmsVfsException( 10775 Messages.get().container(Messages.ERR_VFS_INVALID_PROPERTY_LIST_1, property.getName())); 10776 } 10777 } 10778 10779 // test if and what state should be updated 10780 // 0: none, 1: structure, 2: resource 10781 int updateStateValue = 0; 10782 if (updateState) { 10783 updateStateValue = getUpdateState(dbc, resource, properties); 10784 } 10785 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10786 for (int i = 0; i < properties.size(); i++) { 10787 // write the property 10788 CmsProperty property = properties.get(i); 10789 vfsDriver.writePropertyObject(dbc, dbc.currentProject(), resource, property); 10790 } 10791 10792 if (updateStateValue > 0) { 10793 // update state 10794 updateState(dbc, resource, (updateStateValue == 2)); 10795 } 10796 10797 if (updateState) { 10798 // log it 10799 log( 10800 dbc, 10801 new CmsLogEntry( 10802 dbc, 10803 resource.getStructureId(), 10804 CmsLogEntryType.RESOURCE_PROPERTIES, 10805 new String[] {resource.getRootPath()}), 10806 false); 10807 } 10808 } finally { 10809 // update the driver manager cache 10810 m_monitor.clearResourceCache(); 10811 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 10812 10813 // fire an event that the properties of a resource have been modified 10814 OpenCms.fireCmsEvent( 10815 new CmsEvent( 10816 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 10817 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource))); 10818 } 10819 } 10820 10821 /** 10822 * Updates a publish job.<p> 10823 * 10824 * @param dbc the current database context 10825 * @param publishJob the publish job to update 10826 * 10827 * @throws CmsException if something goes wrong 10828 */ 10829 public void writePublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 10830 10831 getProjectDriver(dbc).writePublishJob(dbc, publishJob); 10832 } 10833 10834 /** 10835 * Writes the publish report for a publish job.<p> 10836 * 10837 * @param dbc the current database context 10838 * @param publishJob the publish job 10839 * @throws CmsException if something goes wrong 10840 */ 10841 public void writePublishReport(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 10842 10843 CmsPublishReport report = (CmsPublishReport)publishJob.removePublishReport(); 10844 10845 if (report != null) { 10846 getProjectDriver(dbc).writePublishReport(dbc, publishJob.getPublishHistoryId(), report.getContents()); 10847 } 10848 } 10849 10850 /** 10851 * Writes a resource to the OpenCms VFS.<p> 10852 * 10853 * @param dbc the current database context 10854 * @param resource the resource to write 10855 * 10856 * @throws CmsException if something goes wrong 10857 */ 10858 public void writeResource(CmsDbContext dbc, CmsResource resource) throws CmsException { 10859 10860 // access was granted - write the resource 10861 resource.setUserLastModified(dbc.currentUser().getId()); 10862 CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) 10863 ? dbc.currentProject().getUuid() 10864 : dbc.getProjectId(); 10865 10866 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE); 10867 10868 // make sure the written resource has the state correctly set 10869 if (resource.getState().isUnchanged()) { 10870 resource.setState(CmsResource.STATE_CHANGED); 10871 } 10872 10873 // delete in content relations if the new type is not parseable 10874 if (!(OpenCms.getResourceManager().getResourceType(resource.getTypeId()) instanceof I_CmsLinkParseable)) { 10875 deleteRelationsWithSiblings(dbc, resource); 10876 } 10877 10878 // update the cache 10879 m_monitor.clearResourceCache(); 10880 Map<String, Object> data = new HashMap<String, Object>(2); 10881 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10882 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE)); 10883 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10884 } 10885 10886 /** 10887 * Inserts an entry in the published resource table.<p> 10888 * 10889 * This is done during static export.<p> 10890 * 10891 * @param dbc the current database context 10892 * @param resourceName The name of the resource to be added to the static export 10893 * @param linkType the type of resource exported (0= non-parameter, 1=parameter) 10894 * @param linkParameter the parameters added to the resource 10895 * @param timestamp a time stamp for writing the data into the db 10896 * 10897 * @throws CmsException if something goes wrong 10898 */ 10899 public void writeStaticExportPublishedResource( 10900 CmsDbContext dbc, 10901 String resourceName, 10902 int linkType, 10903 String linkParameter, 10904 long timestamp) 10905 throws CmsException { 10906 10907 getProjectDriver(dbc).writeStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter, timestamp); 10908 } 10909 10910 /** 10911 * Adds a new url name mapping for a structure id.<p> 10912 * 10913 * Instead of taking the name directly, this method takes an iterator of strings 10914 * which generates candidate URL names on-the-fly. The first generated name which is 10915 * not already mapped to another structure id will be chosen for the new URL name mapping. 10916 * 10917 * @param dbc the current database context 10918 * @param nameSeq the sequence of URL name candidates 10919 * @param structureId the structure id to which the url name should be mapped 10920 * @param locale the locale for which the mapping should be written 10921 * @param replaceOnPublish name mappings for which this is set will replace all other mappings for the same resource on publishing 10922 * 10923 * @return the actual name which was mapped to the structure id 10924 * 10925 * @throws CmsDataAccessException if something goes wrong 10926 */ 10927 public String writeUrlNameMapping( 10928 CmsDbContext dbc, 10929 Iterator<String> nameSeq, 10930 CmsUUID structureId, 10931 String locale, 10932 boolean replaceOnPublish) 10933 throws CmsDataAccessException { 10934 10935 String bestName = findBestNameForUrlNameMapping(dbc, nameSeq, structureId, locale); 10936 addOrReplaceUrlNameMapping(dbc, bestName, structureId, locale, replaceOnPublish); 10937 return bestName; 10938 } 10939 10940 /** 10941 * Updates the user information. <p> 10942 * 10943 * The user id has to be a valid OpenCms user id.<br> 10944 * 10945 * The user with the given id will be completely overridden 10946 * by the given data.<p> 10947 * 10948 * @param dbc the current database context 10949 * @param user the user to be updated 10950 * 10951 * @throws CmsException if operation was not successful 10952 */ 10953 public void writeUser(CmsDbContext dbc, CmsUser user) throws CmsException { 10954 10955 CmsUser oldUser = readUser(dbc, user.getId()); 10956 m_monitor.clearUserCache(oldUser); 10957 getUserDriver(dbc).writeUser(dbc, user); 10958 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 10959 10960 if (!dbc.getProjectId().isNullUUID()) { 10961 // user modified event is not needed 10962 return; 10963 } 10964 // fire user modified event 10965 Map<String, Object> eventData = new HashMap<String, Object>(); 10966 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 10967 eventData.put(I_CmsEventListener.KEY_USER_NAME, oldUser.getName()); 10968 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 10969 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(user.getChanges(oldUser))); 10970 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 10971 OpenCms.getTwoFactorAuthenticationHandler().trackUserChange(dbc.getRequestContext(), oldUser, user); 10972 } 10973 10974 /** 10975 * Adds or replaces a new url name mapping in the offline project.<p> 10976 * 10977 * @param dbc the current database context 10978 * @param name the URL name of the mapping 10979 * @param structureId the structure id of the mapping 10980 * @param locale the locale of the mapping 10981 * @param replaceOnPublish if the mapping shoudl replace previous URL name mappings when published 10982 * 10983 * @throws CmsDataAccessException if something goes wrong 10984 */ 10985 protected void addOrReplaceUrlNameMapping( 10986 CmsDbContext dbc, 10987 String name, 10988 CmsUUID structureId, 10989 String locale, 10990 boolean replaceOnPublish) 10991 throws CmsDataAccessException { 10992 10993 getVfsDriver(dbc).deleteUrlNameMappingEntries( 10994 dbc, 10995 false, 10996 CmsUrlNameMappingFilter.ALL.filterStructureId(structureId).filterLocale(locale).filterStates( 10997 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 10998 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 10999 CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry( 11000 name, 11001 structureId, 11002 replaceOnPublish 11003 ? CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH 11004 : CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 11005 System.currentTimeMillis(), 11006 locale); 11007 getVfsDriver(dbc).addUrlNameMappingEntry(dbc, false, newEntry); 11008 } 11009 11010 /** 11011 * Converts a resource to a folder (if possible).<p> 11012 * 11013 * @param resource the resource to convert 11014 * @return the converted resource 11015 * 11016 * @throws CmsVfsResourceNotFoundException if the resource is not a folder 11017 */ 11018 protected CmsFolder convertResourceToFolder(CmsResource resource) throws CmsVfsResourceNotFoundException { 11019 11020 if (resource.isFolder()) { 11021 return new CmsFolder(resource); 11022 } 11023 11024 throw new CmsVfsResourceNotFoundException( 11025 Messages.get().container(Messages.ERR_ACCESS_FILE_AS_FOLDER_1, resource.getRootPath())); 11026 } 11027 11028 /** 11029 * Helper method for creating a driver from configuration data.<p> 11030 * 11031 * @param dbc the db context 11032 * @param configManager the configuration manager 11033 * @param config the configuration 11034 * @param driverChainKey the configuration key under which the driver chain is stored 11035 * @param suffix the suffix to append to a driver chain entry to get the key for the driver class 11036 * 11037 * @return the newly created driver 11038 */ 11039 protected Object createDriver( 11040 CmsDbContext dbc, 11041 CmsConfigurationManager configManager, 11042 CmsParameterConfiguration config, 11043 String driverChainKey, 11044 String suffix) { 11045 11046 // read the vfs driver class properties and initialize a new instance 11047 List<String> drivers = config.getList(driverChainKey); 11048 String driverKey = drivers.get(0) + suffix; 11049 String driverName = config.get(driverKey); 11050 drivers = (drivers.size() > 1) ? drivers.subList(1, drivers.size()) : null; 11051 if (driverName == null) { 11052 CmsLog.INIT.error(Messages.get().getBundle().key(Messages.INIT_DRIVER_FAILED_1, driverKey)); 11053 } 11054 Object result = newDriverInstance(dbc, configManager, driverName, drivers); 11055 if ("true".equalsIgnoreCase(System.getProperty("opencms.profile.drivers"))) { 11056 result = wrapDriverInProfilingProxy(result); 11057 } 11058 return result; 11059 } 11060 11061 /** 11062 * Deletes all relations for the given resource and all its siblings.<p> 11063 * 11064 * @param dbc the current database context 11065 * @param resource the resource to delete the resource for 11066 * 11067 * @throws CmsException if something goes wrong 11068 */ 11069 protected void deleteRelationsWithSiblings(CmsDbContext dbc, CmsResource resource) throws CmsException { 11070 11071 // get all siblings 11072 List<CmsResource> siblings; 11073 if (resource.getSiblingCount() > 1) { 11074 siblings = readSiblings(dbc, resource, CmsResourceFilter.ALL); 11075 } else { 11076 siblings = new ArrayList<CmsResource>(); 11077 siblings.add(resource); 11078 } 11079 // clean the relations in content for all siblings 11080 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 11081 Iterator<CmsResource> it = siblings.iterator(); 11082 while (it.hasNext()) { 11083 CmsResource sibling = it.next(); 11084 // clean the relation information for this sibling 11085 vfsDriver.deleteRelations( 11086 dbc, 11087 dbc.currentProject().getUuid(), 11088 sibling, 11089 CmsRelationFilter.TARGETS.filterDefinedInContent()); 11090 } 11091 } 11092 11093 /** 11094 * Tries to add sub-resources of moved folders to the publish list and throws an exception if the publish list still does 11095 * not contain some sub-resources of the moved folders.<p> 11096 * 11097 * @param cms the current CMS context 11098 * @param dbc the current database context 11099 * @param pubList the publish list 11100 * @throws CmsException if something goes wrong 11101 */ 11102 protected void ensureSubResourcesOfMovedFoldersPublished(CmsObject cms, CmsDbContext dbc, CmsPublishList pubList) 11103 throws CmsException { 11104 11105 List<CmsResource> topMovedFolders = pubList.getTopMovedFolders(cms); 11106 Iterator<CmsResource> folderIt = topMovedFolders.iterator(); 11107 while (folderIt.hasNext()) { 11108 CmsResource folder = folderIt.next(); 11109 addSubResources(dbc, pubList, folder, resource -> !resource.getState().isNew()); 11110 } 11111 List<CmsResource> missingSubResources = pubList.getMissingSubResources(cms, topMovedFolders); 11112 if (missingSubResources.isEmpty()) { 11113 return; 11114 } 11115 11116 StringBuffer pathBuffer = new StringBuffer(); 11117 11118 for (CmsResource missing : missingSubResources) { 11119 pathBuffer.append(missing.getRootPath()); 11120 pathBuffer.append(" "); 11121 } 11122 throw new CmsVfsException( 11123 Messages.get().container(Messages.RPT_CHILDREN_OF_MOVED_FOLDER_NOT_PUBLISHED_1, pathBuffer.toString())); 11124 11125 } 11126 11127 /** 11128 * Tries to find the best name for an URL name mapping for the given structure id.<p> 11129 * 11130 * @param dbc the database context 11131 * @param nameSeq the sequence of name candidates 11132 * @param structureId the structure id to which an URL name should be mapped 11133 * @param locale the locale for which the URL name should be mapped 11134 * 11135 * @return the selected URL name candidate 11136 * 11137 * @throws CmsDataAccessException if something goes wrong 11138 */ 11139 protected String findBestNameForUrlNameMapping( 11140 CmsDbContext dbc, 11141 Iterator<String> nameSeq, 11142 CmsUUID structureId, 11143 String locale) 11144 throws CmsDataAccessException { 11145 11146 String newName; 11147 boolean alreadyInUse; 11148 do { 11149 newName = nameSeq.next(); 11150 alreadyInUse = false; 11151 CmsUrlNameMappingFilter filter = CmsUrlNameMappingFilter.ALL.filterName(newName); 11152 List<CmsUrlNameMappingEntry> entriesWithSameName = getVfsDriver(dbc).readUrlNameMappingEntries( 11153 dbc, 11154 false, 11155 filter); 11156 for (CmsUrlNameMappingEntry entry : entriesWithSameName) { 11157 boolean sameId = entry.getStructureId().equals(structureId); 11158 if (!sameId) { 11159 // name already used for other resource, or for different locale of the same resource 11160 alreadyInUse = true; 11161 break; 11162 } 11163 } 11164 } while (alreadyInUse); 11165 return newName; 11166 } 11167 11168 /** 11169 * Helper method for finding the 'best' URL name to use for a new URL name mapping.<p> 11170 * 11171 * Since the name given as a parameter may be already used, this method will try to append numeric suffixes 11172 * to the name to find a mapping name which is not used.<p> 11173 * 11174 * @param dbc the current database context 11175 * @param name the name of the mapping 11176 * @param structureId the structure id to which the name is mapped 11177 * 11178 * @return the best name which was found for the new mapping 11179 * 11180 * @throws CmsDataAccessException if something goes wrong 11181 */ 11182 protected String findBestNameForUrlNameMapping(CmsDbContext dbc, String name, CmsUUID structureId) 11183 throws CmsDataAccessException { 11184 11185 List<CmsUrlNameMappingEntry> entriesStartingWithName = getVfsDriver(dbc).readUrlNameMappingEntries( 11186 dbc, 11187 false, 11188 CmsUrlNameMappingFilter.ALL.filterNamePattern(name + "%").filterRejectStructureId(structureId)); 11189 Set<String> usedNames = new HashSet<String>(); 11190 for (CmsUrlNameMappingEntry entry : entriesStartingWithName) { 11191 usedNames.add(entry.getName()); 11192 } 11193 int counter = 0; 11194 String numberedName; 11195 do { 11196 numberedName = getNumberedName(name, counter); 11197 counter += 1; 11198 } while (usedNames.contains(numberedName)); 11199 return numberedName; 11200 } 11201 11202 /** 11203 * Returns the lock manager instance.<p> 11204 * 11205 * @return the lock manager instance 11206 */ 11207 protected CmsLockManager getLockManager() { 11208 11209 return m_lockManager; 11210 } 11211 11212 /** 11213 * Adds a numeric suffix to the end of a string, unless the number passed as a parameter is 0.<p> 11214 * 11215 * @param name the base name 11216 * @param number the number from which to form the suffix 11217 * 11218 * @return the concatenation of the base name and possibly the numeric suffix 11219 */ 11220 protected String getNumberedName(String name, int number) { 11221 11222 if (number == 0) { 11223 return name; 11224 } 11225 PrintfFormat fmt = new PrintfFormat("%0.6d"); 11226 return name + "_" + fmt.sprintf(number); 11227 } 11228 11229 /** 11230 * Resets the resources in a project to their online state.<p> 11231 * 11232 * @param dbc the database context 11233 * @param projectId the project id 11234 * @param modifiedFiles the modified files 11235 * @param modifiedFolders the modified folders 11236 * @throws CmsException if something goes wrong 11237 * @throws CmsSecurityException if we don't have the permissions 11238 * @throws CmsDataAccessException if something goes wrong with the database 11239 */ 11240 protected void resetResourcesInProject( 11241 CmsDbContext dbc, 11242 CmsUUID projectId, 11243 List<CmsResource> modifiedFiles, 11244 List<CmsResource> modifiedFolders) 11245 throws CmsException, CmsSecurityException, CmsDataAccessException { 11246 11247 // all resources inside the project have to be be reset to their online state. 11248 // 1. step: delete all new files 11249 for (int i = 0; i < modifiedFiles.size(); i++) { 11250 CmsResource currentFile = modifiedFiles.get(i); 11251 if (currentFile.getState().isNew()) { 11252 CmsLock lock = getLock(dbc, currentFile); 11253 if (lock.isNullLock()) { 11254 // lock the resource 11255 lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE); 11256 } else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) { 11257 changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE); 11258 } 11259 // delete the properties 11260 getVfsDriver(dbc).deletePropertyObjects( 11261 dbc, 11262 projectId, 11263 currentFile, 11264 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 11265 // delete the file 11266 getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentFile); 11267 // remove the access control entries 11268 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFile.getResourceId()); 11269 // fire the corresponding event 11270 OpenCms.fireCmsEvent( 11271 new CmsEvent( 11272 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11273 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile))); 11274 } 11275 } 11276 11277 // 2. step: delete all new folders 11278 for (int i = 0; i < modifiedFolders.size(); i++) { 11279 CmsResource currentFolder = modifiedFolders.get(i); 11280 if (currentFolder.getState().isNew()) { 11281 // delete the properties 11282 getVfsDriver(dbc).deletePropertyObjects( 11283 dbc, 11284 projectId, 11285 currentFolder, 11286 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 11287 // delete the folder 11288 getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentFolder); 11289 // remove the access control entries 11290 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFolder.getResourceId()); 11291 // fire the corresponding event 11292 OpenCms.fireCmsEvent( 11293 new CmsEvent( 11294 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11295 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder))); 11296 } 11297 } 11298 11299 // 3. step: undo changes on all changed or deleted folders 11300 for (int i = 0; i < modifiedFolders.size(); i++) { 11301 CmsResource currentFolder = modifiedFolders.get(i); 11302 if ((currentFolder.getState().isChanged()) || (currentFolder.getState().isDeleted())) { 11303 CmsLock lock = getLock(dbc, currentFolder); 11304 if (lock.isNullLock()) { 11305 // lock the resource 11306 lockResource(dbc, currentFolder, CmsLockType.EXCLUSIVE); 11307 } else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) { 11308 changeLock(dbc, currentFolder, CmsLockType.EXCLUSIVE); 11309 } 11310 // undo all changes in the folder 11311 undoChanges(dbc, currentFolder, CmsResource.UNDO_CONTENT); 11312 // fire the corresponding event 11313 OpenCms.fireCmsEvent( 11314 new CmsEvent( 11315 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11316 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder))); 11317 } 11318 } 11319 11320 // 4. step: undo changes on all changed or deleted files 11321 for (int i = 0; i < modifiedFiles.size(); i++) { 11322 CmsResource currentFile = modifiedFiles.get(i); 11323 if (currentFile.getState().isChanged() || currentFile.getState().isDeleted()) { 11324 CmsLock lock = getLock(dbc, currentFile); 11325 if (lock.isNullLock()) { 11326 // lock the resource 11327 lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE); 11328 } else if (!lock.isOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) { 11329 if (lock.isLockableBy(dbc.currentUser())) { 11330 changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE); 11331 } 11332 } 11333 // undo all changes in the file 11334 undoChanges(dbc, currentFile, CmsResource.UNDO_CONTENT); 11335 // fire the corresponding event 11336 OpenCms.fireCmsEvent( 11337 new CmsEvent( 11338 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11339 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile))); 11340 } 11341 } 11342 } 11343 11344 /** 11345 * Counts the total number of users which fit the given criteria.<p> 11346 * 11347 * @param dbc the database context 11348 * @param searchParams the user search criteria 11349 * 11350 * @return the total number of users matching the criteria 11351 * 11352 * @throws CmsDataAccessException if something goes wrong 11353 */ 11354 long countUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams) throws CmsDataAccessException { 11355 11356 return getUserDriver(dbc).countUsers(dbc, searchParams); 11357 } 11358 11359 /** 11360 * Adds a pool to the static pool map.<p> 11361 * 11362 * @param pool the pool to add 11363 */ 11364 private void addPool(CmsDbPoolV11 pool) { 11365 11366 m_pools.put(pool.getPoolUrl(), pool); 11367 } 11368 11369 /** 11370 * Adds all sub-resources of the given resource to the publish list.<p> 11371 * 11372 * @param dbc the database context 11373 * @param publishList the publish list 11374 * @param directPublishResource the resource to get the sub-resources for 11375 * @param additionalFilter an additional test for resources to pass before they are added to the publish list 11376 * 11377 * @throws CmsDataAccessException if something goes wrong accessing the database 11378 */ 11379 private void addSubResources( 11380 CmsDbContext dbc, 11381 CmsPublishList publishList, 11382 CmsResource directPublishResource, 11383 Predicate<CmsResource> additionalFilter) 11384 throws CmsDataAccessException { 11385 11386 int flags = CmsDriverManager.READMODE_INCLUDE_TREE | CmsDriverManager.READMODE_EXCLUDE_STATE; 11387 if (!directPublishResource.getState().isDeleted()) { 11388 // fix for org.opencms.file.TestPublishIssues#testPublishFolderWithDeletedFileFromOtherProject 11389 flags = flags | CmsDriverManager.READMODE_INCLUDE_PROJECT; 11390 } 11391 11392 // add all sub resources of the folder 11393 List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree( 11394 dbc, 11395 dbc.currentProject().getUuid(), 11396 directPublishResource.getRootPath(), 11397 CmsDriverManager.READ_IGNORE_TYPE, 11398 CmsResource.STATE_UNCHANGED, 11399 CmsDriverManager.READ_IGNORE_TIME, 11400 CmsDriverManager.READ_IGNORE_TIME, 11401 CmsDriverManager.READ_IGNORE_TIME, 11402 CmsDriverManager.READ_IGNORE_TIME, 11403 CmsDriverManager.READ_IGNORE_TIME, 11404 CmsDriverManager.READ_IGNORE_TIME, 11405 flags | CmsDriverManager.READMODE_ONLY_FOLDERS); 11406 11407 publishList.addAll( 11408 filterResources(dbc, publishList, folderList).stream().filter(additionalFilter).collect( 11409 Collectors.toList()), 11410 true); 11411 11412 List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree( 11413 dbc, 11414 dbc.currentProject().getUuid(), 11415 directPublishResource.getRootPath(), 11416 CmsDriverManager.READ_IGNORE_TYPE, 11417 CmsResource.STATE_UNCHANGED, 11418 CmsDriverManager.READ_IGNORE_TIME, 11419 CmsDriverManager.READ_IGNORE_TIME, 11420 CmsDriverManager.READ_IGNORE_TIME, 11421 CmsDriverManager.READ_IGNORE_TIME, 11422 CmsDriverManager.READ_IGNORE_TIME, 11423 CmsDriverManager.READ_IGNORE_TIME, 11424 flags | CmsDriverManager.READMODE_ONLY_FILES); 11425 11426 publishList.addAll( 11427 filterResources(dbc, publishList, fileList).stream().filter(additionalFilter).collect(Collectors.toList()), 11428 true); 11429 } 11430 11431 /** 11432 * Helper method to check whether we should bother with reading the group for a given role in a given OU.<p> 11433 * 11434 * This is important because webuser OUs don't have most role groups, and their absence is not cached, so we want to avoid reading them. 11435 * 11436 * @param ou the OU 11437 * @param role the role 11438 * @return true if we should read the role in the OU 11439 */ 11440 private boolean canReadRoleInOu(CmsOrganizationalUnit ou, CmsRole role) { 11441 11442 if (ou.hasFlagWebuser() && !role.getRoleName().equals(CmsRole.ACCOUNT_MANAGER.getRoleName())) { 11443 return false; 11444 } 11445 return true; 11446 } 11447 11448 /** 11449 * Checks the parent of a resource during publishing.<p> 11450 * 11451 * @param dbc the current database context 11452 * @param deletedFolders a list of deleted folders 11453 * @param res a resource to check the parent for 11454 * 11455 * @return <code>true</code> if the parent resource will be deleted during publishing 11456 */ 11457 private boolean checkDeletedParentFolder(CmsDbContext dbc, List<CmsResource> deletedFolders, CmsResource res) { 11458 11459 String parentPath = CmsResource.getParentFolder(res.getRootPath()); 11460 11461 if (parentPath == null) { 11462 // resource has no parent 11463 return false; 11464 } 11465 11466 CmsResource parent; 11467 try { 11468 parent = readResource(dbc, parentPath, CmsResourceFilter.ALL); 11469 } catch (Exception e) { 11470 // failure: if we cannot read the parent, we should not publish the resource 11471 return false; 11472 } 11473 11474 if (!parent.getState().isDeleted()) { 11475 // parent is not deleted 11476 return false; 11477 } 11478 11479 for (int j = 0; j < deletedFolders.size(); j++) { 11480 if ((deletedFolders.get(j)).getStructureId().equals(parent.getStructureId())) { 11481 // parent is deleted, and it will get published 11482 return true; 11483 } 11484 } 11485 11486 // parent is new, but it will not get published 11487 return false; 11488 } 11489 11490 /** 11491 * Checks that the given resource can be read as a file.<p> 11492 * 11493 * @param dbc the current database context 11494 * @param resource the resource to check 11495 * @throws CmsVfsResourceNotFoundException if the resource is a folder 11496 */ 11497 private void checkFileResource(CmsDbContext dbc, CmsResource resource) throws CmsVfsResourceNotFoundException { 11498 11499 if (resource.isFolder()) { 11500 throw new CmsVfsResourceNotFoundException( 11501 Messages.get().container( 11502 Messages.ERR_ACCESS_FOLDER_AS_FILE_1, 11503 dbc.removeSiteRoot(resource.getRootPath()))); 11504 } 11505 } 11506 11507 /** 11508 * Checks that no one of the resources to be published has a 'new' parent (that has not been published yet).<p> 11509 * 11510 * @param dbc the db context 11511 * @param publishList the publish list to check 11512 * 11513 * @throws CmsVfsException if there is a resource to be published with a 'new' parent 11514 */ 11515 private void checkParentFolders(CmsDbContext dbc, CmsPublishList publishList) throws CmsVfsException { 11516 11517 boolean directPublish = publishList.isDirectPublish(); 11518 // if we direct publish a file, check if all parent folders are already published 11519 if (directPublish) { 11520 // first get the names of all parent folders 11521 Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator(); 11522 List<String> parentFolderNames = new ArrayList<String>(); 11523 while (it.hasNext()) { 11524 CmsResource res = it.next(); 11525 String parentFolderName = CmsResource.getParentFolder(res.getRootPath()); 11526 if (parentFolderName != null) { 11527 parentFolderNames.add(parentFolderName); 11528 } 11529 } 11530 // remove duplicate parent folder names 11531 parentFolderNames = CmsFileUtil.removeRedundancies(parentFolderNames); 11532 String parentFolderName = null; 11533 try { 11534 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 11535 // now check all folders if they exist in the online project 11536 Iterator<String> parentIt = parentFolderNames.iterator(); 11537 while (parentIt.hasNext()) { 11538 parentFolderName = parentIt.next(); 11539 vfsDriver.readFolder(dbc, CmsProject.ONLINE_PROJECT_ID, parentFolderName); 11540 } 11541 } catch (CmsException e) { 11542 throw new CmsVfsException( 11543 Messages.get().container(Messages.RPT_PARENT_FOLDER_NOT_PUBLISHED_1, parentFolderName)); 11544 } 11545 } 11546 } 11547 11548 /** 11549 * Checks the parent of a resource during publishing.<p> 11550 * 11551 * @param dbc the current database context 11552 * @param folderList a list of folders 11553 * @param res a resource to check the parent for 11554 * 11555 * @return true if the resource should be published 11556 */ 11557 private boolean checkParentResource(CmsDbContext dbc, List<CmsResource> folderList, CmsResource res) { 11558 11559 String parentPath = CmsResource.getParentFolder(res.getRootPath()); 11560 11561 if (parentPath == null) { 11562 // resource has no parent 11563 return true; 11564 } 11565 11566 CmsResource parent; 11567 try { 11568 parent = readResource(dbc, parentPath, CmsResourceFilter.ALL); 11569 } catch (Exception e) { 11570 // failure: if we cannot read the parent, we should not publish the resource 11571 return false; 11572 } 11573 11574 if (!parent.getState().isNew()) { 11575 // parent is already published 11576 return true; 11577 } 11578 11579 for (int j = 0; j < folderList.size(); j++) { 11580 if (folderList.get(j).getStructureId().equals(parent.getStructureId())) { 11581 // parent is new, but it will get published 11582 return true; 11583 } 11584 } 11585 11586 // parent is new, but it will not get published 11587 return false; 11588 } 11589 11590 /** 11591 * Copies all relations from the source resource to the target resource.<p> 11592 * 11593 * @param dbc the database context 11594 * @param source the source 11595 * @param target the target 11596 * 11597 * @throws CmsException if something goes wrong 11598 */ 11599 private void copyRelations(CmsDbContext dbc, CmsResource source, CmsResource target) throws CmsException { 11600 11601 // copy relations all relations 11602 CmsObject cms = new CmsObject(getSecurityManager(), dbc.getRequestContext()); 11603 Iterator<CmsRelation> itRelations = getRelationsForResource( 11604 dbc, 11605 source, 11606 CmsRelationFilter.TARGETS.filterNotDefinedInContent()).iterator(); 11607 while (itRelations.hasNext()) { 11608 CmsRelation relation = itRelations.next(); 11609 if (relation.getType().getCopyBehavior() == CopyBehavior.copy) { 11610 try { 11611 CmsResource relTarget = relation.getTarget(cms, CmsResourceFilter.ALL); 11612 addRelationToResource(dbc, target, relTarget, relation.getType(), true); 11613 } catch (CmsVfsResourceNotFoundException e) { 11614 // ignore this broken relation 11615 if (LOG.isWarnEnabled()) { 11616 LOG.warn(e.getLocalizedMessage(), e); 11617 } 11618 } 11619 } 11620 } 11621 // repair categories 11622 repairCategories(dbc, getProjectIdForContext(dbc), target); 11623 } 11624 11625 /** 11626 * Filters the given list of resources, removes all resources where the current user 11627 * does not have READ permissions, plus the filter is applied.<p> 11628 * 11629 * @param dbc the current database context 11630 * @param resourceList a list of CmsResources 11631 * @param filter the resource filter to use 11632 * 11633 * @return the filtered list of resources 11634 * 11635 * @throws CmsException in case errors testing the permissions 11636 */ 11637 private List<CmsResource> filterPermissions( 11638 CmsDbContext dbc, 11639 List<CmsResource> resourceList, 11640 CmsResourceFilter filter) 11641 throws CmsException { 11642 11643 if (filter.requireTimerange()) { 11644 // never check time range here - this must be done later in #updateContextDates(...) 11645 filter = filter.addExcludeTimerange(); 11646 } 11647 ResourceListWithCacheability result = new ResourceListWithCacheability(); 11648 boolean nocacheWasSet = false; 11649 if (null == dbc.getAttribute(ATTR_PERMISSION_NOCACHE)) { 11650 // The attribute will be used by the permission handler to tell us that lists containing the resource 11651 // should not be cached. 11652 dbc.setAttribute(ATTR_PERMISSION_NOCACHE, new boolean[] {false}); 11653 // insurance against potential indirect recursive calls introduced by future code changes: 11654 // make sure we only remove the attribute later if we were the one who set it 11655 nocacheWasSet = true; 11656 } 11657 try { 11658 for (int i = 0; i < resourceList.size(); i++) { 11659 // check the permission of all resources 11660 CmsResource currentResource = resourceList.get(i); 11661 if (m_securityManager.hasPermissions( 11662 dbc, 11663 currentResource, 11664 CmsPermissionSet.ACCESS_READ, 11665 LockCheck.yes, 11666 filter).isAllowed()) { 11667 // only return resources where permission was granted 11668 result.add(currentResource); 11669 } 11670 } 11671 } finally { 11672 if (nocacheWasSet) { 11673 boolean[] nocache = (boolean[])dbc.getAttribute(ATTR_PERMISSION_NOCACHE); 11674 if (nocache != null) { 11675 dbc.removeAttribute(ATTR_PERMISSION_NOCACHE); 11676 if (nocache[0]) { 11677 result.setCacheable(false); 11678 } 11679 } 11680 } 11681 } 11682 // return the result 11683 return result; 11684 } 11685 11686 /** 11687 * Returns a filtered list of resources for publishing.<p> 11688 * Contains all resources, which are not locked 11689 * and which have a parent folder that is already published or will be published, too.<p> 11690 * 11691 * @param dbc the current database context 11692 * @param publishList the filling publish list 11693 * @param resourceList the list of resources to filter 11694 * 11695 * @return a filtered list of resources 11696 */ 11697 private List<CmsResource> filterResources( 11698 CmsDbContext dbc, 11699 CmsPublishList publishList, 11700 List<CmsResource> resourceList) { 11701 11702 List<CmsResource> result = new ArrayList<CmsResource>(); 11703 11704 // local folder list for adding new publishing subfolders 11705 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioD} problem. 11706 List<CmsResource> newFolderList = new ArrayList<CmsResource>( 11707 publishList == null ? resourceList : publishList.getFolderList()); 11708 11709 for (int i = 0; i < resourceList.size(); i++) { 11710 CmsResource res = resourceList.get(i); 11711 try { 11712 CmsLock lock = getLock(dbc, res); 11713 if (lock.isPublish()) { 11714 // if already enqueued 11715 continue; 11716 } 11717 if (!lock.isLockableBy(dbc.currentUser())) { 11718 // checks if there is a shared lock and if the resource is deleted 11719 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem. 11720 if (lock.isShared() && (publishList != null)) { 11721 if (!res.getState().isDeleted() 11722 || !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) { 11723 continue; 11724 } 11725 } else { 11726 // don't add locked resources 11727 continue; 11728 } 11729 } 11730 if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, newFolderList, res)) { 11731 continue; 11732 } 11733 // check permissions 11734 try { 11735 m_securityManager.checkPermissions( 11736 dbc, 11737 res, 11738 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 11739 false, 11740 CmsResourceFilter.ALL); 11741 } catch (CmsException e) { 11742 // skip if not enough permissions 11743 continue; 11744 } 11745 if (res.isFolder()) { 11746 newFolderList.add(res); 11747 } 11748 result.add(res); 11749 } catch (Exception e) { 11750 // should never happen 11751 LOG.error(e.getLocalizedMessage(), e); 11752 } 11753 } 11754 return result; 11755 } 11756 11757 /** 11758 * Returns a filtered list of sibling resources for publishing.<p> 11759 * 11760 * Contains all siblings of the given resources, which are not locked 11761 * and which have a parent folder that is already published or will be published, too.<p> 11762 * 11763 * @param dbc the current database context 11764 * @param publishList the unfinished publish list 11765 * @param resourceList the list of siblings to filter 11766 * 11767 * @return a filtered list of sibling resources for publishing 11768 */ 11769 private List<CmsResource> filterSiblings( 11770 CmsDbContext dbc, 11771 CmsPublishList publishList, 11772 Collection<CmsResource> resourceList) { 11773 11774 List<CmsResource> result = new ArrayList<CmsResource>(); 11775 11776 // removed internal extendible folder list, since iterated (sibling) resources are files in any case, never folders 11777 11778 for (CmsResource res : resourceList) { 11779 try { 11780 CmsLock lock = getLock(dbc, res); 11781 if (lock.isPublish()) { 11782 // if already enqueued 11783 continue; 11784 } 11785 if (!lock.isLockableBy(dbc.currentUser())) { 11786 // checks if there is a shared lock and if the resource is deleted 11787 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem. 11788 if (lock.isShared() && (publishList != null)) { 11789 if (!res.getState().isDeleted() 11790 || !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) { 11791 continue; 11792 } 11793 } else { 11794 // don't add locked resources 11795 continue; 11796 } 11797 } 11798 if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, publishList.getFolderList(), res)) { 11799 // don't add resources that have no parent in the online project 11800 continue; 11801 } 11802 // check permissions 11803 try { 11804 m_securityManager.checkPermissions( 11805 dbc, 11806 res, 11807 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 11808 false, 11809 CmsResourceFilter.ALL); 11810 } catch (CmsException e) { 11811 // skip if not enough permissions 11812 continue; 11813 } 11814 result.add(res); 11815 } catch (Exception e) { 11816 // should never happen 11817 LOG.error(e.getLocalizedMessage(), e); 11818 } 11819 } 11820 return result; 11821 } 11822 11823 /** 11824 * Returns the access control list of a given resource.<p> 11825 * 11826 * @param dbc the current database context 11827 * @param resource the resource 11828 * @param forFolder should be true if resource is a folder 11829 * @param depth the depth to include non-inherited access entries, also 11830 * @param inheritedOnly flag indicates to collect inherited permissions only 11831 * 11832 * @return the access control list of the resource 11833 * 11834 * @throws CmsException if something goes wrong 11835 */ 11836 private CmsAccessControlList getAccessControlList( 11837 CmsDbContext dbc, 11838 CmsResource resource, 11839 boolean inheritedOnly, 11840 boolean forFolder, 11841 int depth) 11842 throws CmsException { 11843 11844 String cacheKey = getCacheKey( 11845 new String[] { 11846 inheritedOnly ? "+" : "-", 11847 forFolder ? "+" : "-", 11848 Integer.toString(depth), 11849 resource.getStructureId().toString()}, 11850 dbc); 11851 11852 CmsAccessControlList acl = m_monitor.getCachedACL(cacheKey); 11853 11854 // return the cached acl if already available 11855 if ((acl != null) && dbc.getProjectId().isNullUUID()) { 11856 return acl; 11857 } 11858 11859 List<CmsAccessControlEntry> aces = getUserDriver(dbc).readAccessControlEntries( 11860 dbc, 11861 dbc.currentProject(), 11862 resource.getResourceId(), 11863 (depth > 1) || ((depth > 0) && forFolder)); 11864 11865 // sort the list of aces 11866 boolean overwriteAll = sortAceList(aces); 11867 11868 // if no 'overwrite all' ace was found 11869 if (!overwriteAll) { 11870 // get the acl of the parent 11871 CmsResource parentResource = null; 11872 try { 11873 // try to recurse over the id 11874 parentResource = getVfsDriver(dbc).readParentFolder( 11875 dbc, 11876 dbc.currentProject().getUuid(), 11877 resource.getStructureId()); 11878 } catch (CmsVfsResourceNotFoundException e) { 11879 // should never happen, but try with the path 11880 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 11881 if (parentPath != null) { 11882 parentResource = getVfsDriver(dbc).readFolder(dbc, dbc.currentProject().getUuid(), parentPath); 11883 } 11884 } 11885 if (parentResource != null) { 11886 acl = (CmsAccessControlList)getAccessControlList( 11887 dbc, 11888 parentResource, 11889 inheritedOnly, 11890 forFolder, 11891 depth + 1).clone(); 11892 } 11893 } 11894 if (acl == null) { 11895 acl = new CmsAccessControlList(); 11896 } 11897 11898 Set<CmsUUID> exclusiveAccessPrincipals = new HashSet<>(); 11899 if (!((depth == 0) && inheritedOnly)) { 11900 Iterator<CmsAccessControlEntry> itAces = aces.iterator(); 11901 while (itAces.hasNext()) { 11902 CmsAccessControlEntry acEntry = itAces.next(); 11903 if (depth > 0) { 11904 acEntry.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED); 11905 } 11906 if ((depth == 0) 11907 && resource.isFile() 11908 && (0 != (acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE))) { 11909 11910 // 'responsible' flag is only interpreted as exclusive access if it's not inherited and set directly on a file 11911 exclusiveAccessPrincipals.add(acEntry.getPrincipal()); 11912 } 11913 11914 acl.add(acEntry); 11915 11916 // if the overwrite flag is set, reset the allowed permissions to the permissions of this entry 11917 // denied permissions are kept or extended 11918 if ((acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE) > 0) { 11919 acl.setAllowedPermissions(acEntry); 11920 } 11921 } 11922 } 11923 if (exclusiveAccessPrincipals.size() > 0) { 11924 acl.setExclusiveAccessPrincipals(exclusiveAccessPrincipals); 11925 } 11926 11927 if (dbc.getProjectId().isNullUUID()) { 11928 m_monitor.cacheACL(cacheKey, acl); 11929 } 11930 return acl; 11931 } 11932 11933 /** 11934 * Return a cache key build from the provided information.<p> 11935 * 11936 * @param prefix a prefix for the key 11937 * @param flag a boolean flag for the key (only used if prefix is not null) 11938 * @param projectId the project for which to generate the key 11939 * @param resource the resource for which to generate the key 11940 * 11941 * @return String a cache key build from the provided information 11942 */ 11943 private String getCacheKey(String prefix, boolean flag, CmsUUID projectId, String resource) { 11944 11945 StringBuffer b = new StringBuffer(64); 11946 if (prefix != null) { 11947 b.append(prefix); 11948 b.append(flag ? '+' : '-'); 11949 } 11950 b.append(CmsProject.isOnlineProject(projectId) ? '+' : '-'); 11951 return b.append(resource).toString(); 11952 } 11953 11954 /** 11955 * Return a cache key build from the provided information.<p> 11956 * 11957 * @param keys an array of keys to generate the cache key from 11958 * @param dbc the database context for which to generate the key 11959 * 11960 * @return String a cache key build from the provided information 11961 */ 11962 private String getCacheKey(String[] keys, CmsDbContext dbc) { 11963 11964 if (!dbc.getProjectId().isNullUUID()) { 11965 return ""; 11966 } 11967 StringBuffer b = new StringBuffer(64); 11968 int len = keys.length; 11969 if (len > 0) { 11970 for (int i = 0; i < len; i++) { 11971 b.append(keys[i]); 11972 b.append('_'); 11973 } 11974 } 11975 if (dbc.currentProject().isOnlineProject()) { 11976 b.append("+"); 11977 } else { 11978 b.append("-"); 11979 } 11980 return b.toString(); 11981 } 11982 11983 /** 11984 * Gets the correct driver interface to use for proxying a specific driver instance.<p> 11985 * 11986 * @param obj the driver instance 11987 * @return the interface to use for proxying 11988 */ 11989 private Class<?> getDriverInterfaceForProxy(Object obj) { 11990 11991 for (Class<?> interfaceClass : new Class[] { 11992 I_CmsUserDriver.class, 11993 I_CmsVfsDriver.class, 11994 I_CmsProjectDriver.class, 11995 I_CmsHistoryDriver.class, 11996 I_CmsSubscriptionDriver.class}) { 11997 if (interfaceClass.isAssignableFrom(obj.getClass())) { 11998 return interfaceClass; 11999 } 12000 } 12001 return null; 12002 } 12003 12004 /** 12005 * Returns the correct project id.<p> 12006 * 12007 * @param dbc the database context 12008 * 12009 * @return the correct project id 12010 */ 12011 private CmsUUID getProjectIdForContext(CmsDbContext dbc) { 12012 12013 CmsUUID projectId = dbc.getProjectId(); 12014 if (projectId.isNullUUID()) { 12015 projectId = dbc.currentProject().getUuid(); 12016 } 12017 return projectId; 12018 } 12019 12020 /** 12021 * Returns if and what state needs to be updated.<p> 12022 * 12023 * @param dbc the db context 12024 * @param resource the resource 12025 * @param properties the properties to check 12026 * 12027 * @return 0: none, 1: structure, 2: resource 12028 * 12029 * @throws CmsDataAccessException if something goes wrong 12030 */ 12031 private int getUpdateState(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties) 12032 throws CmsDataAccessException { 12033 12034 int updateState = 0; 12035 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12036 Iterator<CmsProperty> it = properties.iterator(); 12037 while (it.hasNext() && (updateState < 2)) { 12038 CmsProperty property = it.next(); 12039 12040 // read existing property 12041 CmsProperty existingProperty = vfsDriver.readPropertyObject( 12042 dbc, 12043 property.getName(), 12044 dbc.currentProject(), 12045 resource); 12046 12047 // check the shared property 12048 if (property.getResourceValue() != null) { 12049 if (property.isDeleteResourceValue()) { 12050 if (existingProperty.getResourceValue() != null) { 12051 updateState = 2; // deleted 12052 } 12053 } else { 12054 if (existingProperty.getResourceValue() == null) { 12055 updateState = 2; // created 12056 } else { 12057 if (!property.getResourceValue().equals(existingProperty.getResourceValue())) { 12058 updateState = 2; // updated 12059 } 12060 } 12061 } 12062 } 12063 if (updateState == 0) { 12064 // check the individual property only if needed 12065 if (property.getStructureValue() != null) { 12066 if (property.isDeleteStructureValue()) { 12067 if (existingProperty.getStructureValue() != null) { 12068 updateState = 1; // deleted 12069 } 12070 } else { 12071 if (existingProperty.getStructureValue() == null) { 12072 updateState = 1; // created 12073 } else { 12074 if (!property.getStructureValue().equals(existingProperty.getStructureValue())) { 12075 updateState = 1; // updated 12076 } 12077 } 12078 } 12079 } 12080 } 12081 } 12082 return updateState; 12083 } 12084 12085 /** 12086 * Returns all groups that are virtualizing the given role in the given ou.<p> 12087 * 12088 * @param dbc the database context 12089 * @param role the role 12090 * 12091 * @return all groups that are virtualizing the given role (or a child of it) 12092 * 12093 * @throws CmsException if something goes wrong 12094 */ 12095 private List<CmsGroup> getVirtualGroupsForRole(CmsDbContext dbc, CmsRole role) throws CmsException { 12096 12097 Set<Integer> roleFlags = new HashSet<Integer>(); 12098 // add role flag 12099 Integer flags = Integer.valueOf(role.getVirtualGroupFlags()); 12100 roleFlags.add(flags); 12101 // collect all child role flags 12102 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 12103 while (itChildRoles.hasNext()) { 12104 CmsRole child = itChildRoles.next(); 12105 flags = Integer.valueOf(child.getVirtualGroupFlags()); 12106 roleFlags.add(flags); 12107 } 12108 // iterate all groups matching the flags 12109 List<CmsGroup> groups = new ArrayList<CmsGroup>(); 12110 Iterator<CmsGroup> it = getGroups(dbc, readOrganizationalUnit(dbc, role.getOuFqn()), false, false).iterator(); 12111 while (it.hasNext()) { 12112 CmsGroup group = it.next(); 12113 if (group.isVirtual()) { 12114 CmsRole r = CmsRole.valueOf(group); 12115 if (roleFlags.contains(Integer.valueOf(r.getVirtualGroupFlags()))) { 12116 groups.add(group); 12117 } 12118 } 12119 } 12120 return groups; 12121 } 12122 12123 /** 12124 * Returns a list of users in a group.<p> 12125 * 12126 * @param dbc the current database context 12127 * @param ouFqn the organizational unit to get the users from 12128 * @param groupname the name of the group to list users from 12129 * @param includeOtherOuUsers include users of other organizational units 12130 * @param directUsersOnly if set only the direct assigned users will be returned, 12131 * if not also indirect users, ie. members of parent roles, 12132 * this parameter only works with roles 12133 * @param readRoles if to read roles or groups 12134 * 12135 * @return all <code>{@link CmsUser}</code> objects in the group 12136 * 12137 * @throws CmsException if operation was not successful 12138 */ 12139 private List<CmsUser> internalUsersOfGroup( 12140 CmsDbContext dbc, 12141 String ouFqn, 12142 String groupname, 12143 boolean includeOtherOuUsers, 12144 boolean directUsersOnly, 12145 boolean readRoles) 12146 throws CmsException { 12147 12148 CmsGroup group = readGroup(dbc, groupname); // check that the group really exists 12149 if ((group == null) || (!((!readRoles && !group.isRole()) || (readRoles && group.isRole())))) { 12150 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 12151 } 12152 12153 String prefix = "_" + includeOtherOuUsers + "_" + directUsersOnly + "_" + ouFqn; 12154 String cacheKey = m_keyGenerator.getCacheKeyForGroupUsers(prefix, dbc, group); 12155 List<CmsUser> allUsers = m_monitor.getCachedUserList(cacheKey); 12156 if (allUsers == null) { 12157 Set<CmsUser> users = new HashSet<CmsUser>( 12158 getUserDriver(dbc).readUsersOfGroup(dbc, groupname, includeOtherOuUsers)); 12159 if (readRoles && !directUsersOnly) { 12160 CmsRole role = CmsRole.valueOf(group); 12161 if (role.getParentRole() != null) { 12162 try { 12163 String parentGroup = role.getParentRole().getGroupName(); 12164 readGroup(dbc, parentGroup); 12165 // iterate the parent roles 12166 users.addAll( 12167 internalUsersOfGroup( 12168 dbc, 12169 ouFqn, 12170 parentGroup, 12171 includeOtherOuUsers, 12172 directUsersOnly, 12173 readRoles)); 12174 } catch (CmsDbEntryNotFoundException e) { 12175 // ignore, this may happen while deleting an orgunit 12176 if (LOG.isDebugEnabled()) { 12177 LOG.debug(e.getLocalizedMessage(), e); 12178 } 12179 } 12180 } 12181 String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn()); 12182 if (parentOu != null) { 12183 // iterate the parent ou's 12184 users.addAll( 12185 internalUsersOfGroup( 12186 dbc, 12187 ouFqn, 12188 parentOu + group.getSimpleName(), 12189 includeOtherOuUsers, 12190 directUsersOnly, 12191 readRoles)); 12192 } 12193 } else if (!readRoles && !directUsersOnly) { 12194 List<CmsGroup> groups = getChildren(dbc, group, false); 12195 for (CmsGroup parentGroup : groups) { 12196 try { 12197 // iterate the parent groups 12198 users.addAll( 12199 internalUsersOfGroup( 12200 dbc, 12201 ouFqn, 12202 parentGroup.getName(), 12203 includeOtherOuUsers, 12204 directUsersOnly, 12205 readRoles)); 12206 } catch (CmsDbEntryNotFoundException e) { 12207 // ignore, this may happen while deleting an orgunit 12208 if (LOG.isDebugEnabled()) { 12209 LOG.debug(e.getLocalizedMessage(), e); 12210 } 12211 } 12212 } 12213 } 12214 // filter users from other ous 12215 if (!includeOtherOuUsers) { 12216 Iterator<CmsUser> itUsers = users.iterator(); 12217 while (itUsers.hasNext()) { 12218 CmsUser user = itUsers.next(); 12219 if (!user.getOuFqn().equals(ouFqn)) { 12220 itUsers.remove(); 12221 } 12222 } 12223 } 12224 12225 // make user list unmodifiable for caching 12226 allUsers = Collections.unmodifiableList(new ArrayList<CmsUser>(users)); 12227 if (dbc.getProjectId().isNullUUID()) { 12228 m_monitor.cacheUserList(cacheKey, allUsers); 12229 } 12230 } 12231 return allUsers; 12232 } 12233 12234 /** 12235 * Reads all resources that are inside and changed in a specified project.<p> 12236 * 12237 * @param dbc the current database context 12238 * @param projectId the ID of the project 12239 * @param mode one of the {@link CmsReadChangedProjectResourceMode} constants 12240 * 12241 * @return a List with all resources inside the specified project 12242 * 12243 * @throws CmsException if something goes wrong 12244 */ 12245 private List<CmsResource> readChangedResourcesInsideProject( 12246 CmsDbContext dbc, 12247 CmsUUID projectId, 12248 CmsReadChangedProjectResourceMode mode) 12249 throws CmsException { 12250 12251 String cacheKey = projectId + "_" + mode.toString(); 12252 List<CmsResource> result = m_monitor.getCachedProjectResources(cacheKey); 12253 if (result != null) { 12254 return result; 12255 } 12256 List<String> projectResources = readProjectResources(dbc, readProject(dbc, projectId)); 12257 result = new ArrayList<CmsResource>(); 12258 String currentProjectResource = null; 12259 List<CmsResource> resources = new ArrayList<CmsResource>(); 12260 CmsResource currentResource = null; 12261 CmsLock currentLock = null; 12262 12263 for (int i = 0; i < projectResources.size(); i++) { 12264 // read all resources that are inside the project by visiting each project resource 12265 currentProjectResource = projectResources.get(i); 12266 12267 try { 12268 currentResource = readResource(dbc, currentProjectResource, CmsResourceFilter.ALL); 12269 12270 if (currentResource.isFolder()) { 12271 resources.addAll(readResources(dbc, currentResource, CmsResourceFilter.ALL, true)); 12272 } else { 12273 resources.add(currentResource); 12274 } 12275 } catch (CmsException e) { 12276 // the project resource probably doesn't exist (anymore)... 12277 if (!(e instanceof CmsVfsResourceNotFoundException)) { 12278 throw e; 12279 } 12280 } 12281 } 12282 12283 for (int j = 0; j < resources.size(); j++) { 12284 currentResource = resources.get(j); 12285 currentLock = getLock(dbc, currentResource).getEditionLock(); 12286 12287 if (!currentResource.getState().isUnchanged()) { 12288 if ((currentLock.isNullLock() && (currentResource.getProjectLastModified().equals(projectId))) 12289 || (currentLock.isOwnedBy(dbc.currentUser()) && (currentLock.getProjectId().equals(projectId)))) { 12290 // add only resources that are 12291 // - inside the project, 12292 // - changed in the project, 12293 // - either unlocked, or locked for the current user in the project 12294 if ((mode == RCPRM_FILES_AND_FOLDERS_MODE) 12295 || (currentResource.isFolder() && (mode == RCPRM_FOLDERS_ONLY_MODE)) 12296 || (currentResource.isFile() && (mode == RCPRM_FILES_ONLY_MODE))) { 12297 result.add(currentResource); 12298 } 12299 } 12300 } 12301 } 12302 12303 resources.clear(); 12304 resources = null; 12305 12306 m_monitor.cacheProjectResources(cacheKey, result); 12307 return result; 12308 } 12309 12310 /** 12311 * Reads the content for a history resource.<p> 12312 * 12313 * @param dbc the current database context 12314 * @param resource the history resource 12315 * @return the history content 12316 * @throws CmsDataAccessException if the content can not be read 12317 */ 12318 private byte[] readHistoryContent(CmsDbContext dbc, CmsResource resource) throws CmsDataAccessException { 12319 12320 return getHistoryDriver(dbc).readContent( 12321 dbc, 12322 resource.getResourceId(), 12323 ((I_CmsHistoryResource)resource).getPublishTag()); 12324 } 12325 12326 /** 12327 * Sorts the given list of {@link CmsAccessControlEntry} objects.<p> 12328 * 12329 * The the 'all others' ace in first place, the 'overwrite all' ace in second.<p> 12330 * 12331 * @param aces the list of ACEs to sort 12332 * 12333 * @return <code>true</code> if the list contains the 'overwrite all' ace 12334 */ 12335 private boolean sortAceList(List<CmsAccessControlEntry> aces) { 12336 12337 // sort the list of entries 12338 Collections.sort(aces, CmsAccessControlEntry.COMPARATOR_ACE); 12339 // after sorting just the first 2 positions come in question 12340 for (int i = 0; i < Math.min(aces.size(), 2); i++) { 12341 CmsAccessControlEntry acEntry = aces.get(i); 12342 if (acEntry.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_ID)) { 12343 return true; 12344 } 12345 } 12346 return false; 12347 } 12348 12349 /** 12350 * All permissions and resources attributes of the principal 12351 * are transfered to a replacement principal.<p> 12352 * 12353 * @param dbc the current database context 12354 * @param project the current project 12355 * @param principalId the id of the principal to be replaced 12356 * @param replacementId the user to be transfered 12357 * @param withACEs flag to signal if the ACEs should also be transfered or just deleted 12358 * 12359 * @throws CmsException if operation was not successful 12360 */ 12361 private void transferPrincipalResources( 12362 CmsDbContext dbc, 12363 CmsProject project, 12364 CmsUUID principalId, 12365 CmsUUID replacementId, 12366 boolean withACEs) 12367 throws CmsException { 12368 12369 // get all resources for the given user including resources associated by ACEs or attributes 12370 I_CmsUserDriver userDriver = getUserDriver(dbc); 12371 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12372 Set<CmsResource> resources = getResourcesForPrincipal(dbc, project, principalId, null, true); 12373 Iterator<CmsResource> it = resources.iterator(); 12374 while (it.hasNext()) { 12375 CmsResource resource = it.next(); 12376 // check resource attributes 12377 boolean attrModified = false; 12378 CmsUUID createdUser = null; 12379 if (resource.getUserCreated().equals(principalId)) { 12380 createdUser = replacementId; 12381 attrModified = true; 12382 } 12383 CmsUUID lastModUser = null; 12384 if (resource.getUserLastModified().equals(principalId)) { 12385 lastModUser = replacementId; 12386 attrModified = true; 12387 } 12388 if (attrModified) { 12389 vfsDriver.transferResource(dbc, project, resource, createdUser, lastModUser); 12390 // clear the cache 12391 m_monitor.clearResourceCache(); 12392 } 12393 boolean aceModified = false; 12394 // check aces 12395 if (withACEs) { 12396 Iterator<CmsAccessControlEntry> itAces = userDriver.readAccessControlEntries( 12397 dbc, 12398 project, 12399 resource.getResourceId(), 12400 false).iterator(); 12401 while (itAces.hasNext()) { 12402 CmsAccessControlEntry ace = itAces.next(); 12403 if (ace.getPrincipal().equals(principalId)) { 12404 CmsAccessControlEntry newAce = new CmsAccessControlEntry( 12405 ace.getResource(), 12406 replacementId, 12407 ace.getAllowedPermissions(), 12408 ace.getDeniedPermissions(), 12409 ace.getFlags()); 12410 // write the new ace 12411 userDriver.writeAccessControlEntry(dbc, project, newAce); 12412 aceModified = true; 12413 } 12414 } 12415 if (aceModified) { 12416 // clear the cache 12417 m_monitor.clearAccessControlListCache(); 12418 } 12419 } 12420 if (attrModified || aceModified) { 12421 // fire the event 12422 Map<String, Object> data = new HashMap<String, Object>(2); 12423 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 12424 data.put( 12425 I_CmsEventListener.KEY_CHANGE, 12426 Integer.valueOf( 12427 ((attrModified) ? CHANGED_RESOURCE : 0) | ((aceModified) ? CHANGED_ACCESSCONTROL : 0))); 12428 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 12429 } 12430 } 12431 } 12432 12433 /** 12434 * Undoes all content changes of a resource.<p> 12435 * 12436 * @param dbc the database context 12437 * @param onlineProject the online project 12438 * @param offlineResource the offline resource, or <code>null</code> if deleted 12439 * @param onlineResource the online resource 12440 * @param newState the new resource state 12441 * @param moveUndone is a move operation on the same resource has been made 12442 * 12443 * @throws CmsException if something goes wrong 12444 */ 12445 private void undoContentChanges( 12446 CmsDbContext dbc, 12447 CmsProject onlineProject, 12448 CmsResource offlineResource, 12449 CmsResource onlineResource, 12450 CmsResourceState newState, 12451 boolean moveUndone) 12452 throws CmsException { 12453 12454 String path = ((moveUndone || (offlineResource == null)) 12455 ? onlineResource.getRootPath() 12456 : offlineResource.getRootPath()); 12457 12458 // change folder or file? 12459 I_CmsUserDriver userDriver = getUserDriver(dbc); 12460 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12461 if (onlineResource.isFolder()) { 12462 CmsFolder restoredFolder = new CmsFolder( 12463 onlineResource.getStructureId(), 12464 onlineResource.getResourceId(), 12465 path, 12466 onlineResource.getTypeId(), 12467 onlineResource.getFlags(), 12468 dbc.currentProject().getUuid(), 12469 newState, 12470 onlineResource.getDateCreated(), 12471 onlineResource.getUserCreated(), 12472 onlineResource.getDateLastModified(), 12473 onlineResource.getUserLastModified(), 12474 onlineResource.getDateReleased(), 12475 onlineResource.getDateExpired(), 12476 onlineResource.getVersion()); // version number does not matter since it will be computed later 12477 12478 // write the folder in the offline project 12479 // this sets a flag so that the folder date is not set to the current time 12480 restoredFolder.setDateLastModified(onlineResource.getDateLastModified()); 12481 12482 // write the folder 12483 vfsDriver.writeResource(dbc, dbc.currentProject().getUuid(), restoredFolder, NOTHING_CHANGED); 12484 12485 // restore the properties from the online project 12486 vfsDriver.deletePropertyObjects( 12487 dbc, 12488 dbc.currentProject().getUuid(), 12489 restoredFolder, 12490 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 12491 12492 List<CmsProperty> propertyInfos = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource); 12493 vfsDriver.writePropertyObjects(dbc, dbc.currentProject(), restoredFolder, propertyInfos); 12494 12495 // restore the access control entries from the online project 12496 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId()); 12497 ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries( 12498 dbc, 12499 onlineProject, 12500 onlineResource.getResourceId(), 12501 false).listIterator(); 12502 12503 while (aceList.hasNext()) { 12504 CmsAccessControlEntry ace = aceList.next(); 12505 userDriver.createAccessControlEntry( 12506 dbc, 12507 dbc.currentProject(), 12508 onlineResource.getResourceId(), 12509 ace.getPrincipal(), 12510 ace.getPermissions().getAllowedPermissions(), 12511 ace.getPermissions().getDeniedPermissions(), 12512 ace.getFlags()); 12513 } 12514 } else { 12515 byte[] onlineContent = vfsDriver.readContent( 12516 dbc, 12517 CmsProject.ONLINE_PROJECT_ID, 12518 onlineResource.getResourceId()); 12519 12520 CmsFile restoredFile = new CmsFile( 12521 onlineResource.getStructureId(), 12522 onlineResource.getResourceId(), 12523 path, 12524 onlineResource.getTypeId(), 12525 onlineResource.getFlags(), 12526 dbc.currentProject().getUuid(), 12527 newState, 12528 onlineResource.getDateCreated(), 12529 onlineResource.getUserCreated(), 12530 onlineResource.getDateLastModified(), 12531 onlineResource.getUserLastModified(), 12532 onlineResource.getDateReleased(), 12533 onlineResource.getDateExpired(), 12534 0, 12535 onlineResource.getLength(), 12536 onlineResource.getDateContent(), 12537 onlineResource.getVersion(), // version number does not matter since it will be computed later 12538 onlineContent); 12539 12540 // write the file in the offline project 12541 // this sets a flag so that the file date is not set to the current time 12542 restoredFile.setDateLastModified(onlineResource.getDateLastModified()); 12543 12544 // collect the old properties 12545 List<CmsProperty> properties = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource); 12546 12547 if (offlineResource != null) { 12548 // bug fix 1020: delete all properties (inclum_rejectStructureIdded shared), 12549 // shared properties will be recreated by the next call of #createResource(...) 12550 vfsDriver.deletePropertyObjects( 12551 dbc, 12552 dbc.currentProject().getUuid(), 12553 onlineResource, 12554 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 12555 12556 // implementation notes: 12557 // undo changes can become complex e.g. if a resource was deleted, and then 12558 // another resource was copied over the deleted file as a sibling 12559 // therefore we must "clean" delete the offline resource, and then create 12560 // an new resource with the create method 12561 // note that this does NOT apply to folders, since a folder cannot be replaced 12562 // like a resource anyway 12563 deleteResource(dbc, offlineResource, CmsResource.DELETE_PRESERVE_SIBLINGS); 12564 } 12565 CmsResource res = createResource( 12566 dbc, 12567 restoredFile.getRootPath(), 12568 restoredFile, 12569 restoredFile.getContents(), 12570 properties, 12571 false); 12572 12573 // copy the access control entries from the online project 12574 if (offlineResource != null) { 12575 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId()); 12576 } 12577 ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries( 12578 dbc, 12579 onlineProject, 12580 onlineResource.getResourceId(), 12581 false).listIterator(); 12582 12583 while (aceList.hasNext()) { 12584 CmsAccessControlEntry ace = aceList.next(); 12585 userDriver.createAccessControlEntry( 12586 dbc, 12587 dbc.currentProject(), 12588 res.getResourceId(), 12589 ace.getPrincipal(), 12590 ace.getPermissions().getAllowedPermissions(), 12591 ace.getPermissions().getDeniedPermissions(), 12592 ace.getFlags()); 12593 } 12594 12595 vfsDriver.deleteUrlNameMappingEntries( 12596 dbc, 12597 false, 12598 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates( 12599 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 12600 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 12601 // restore the state to unchanged 12602 res.setState(newState); 12603 m_vfsDriver.writeResourceState(dbc, dbc.currentProject(), res, UPDATE_ALL, false); 12604 } 12605 12606 // delete all offline relations 12607 if (offlineResource != null) { 12608 vfsDriver.deleteRelations(dbc, dbc.currentProject().getUuid(), offlineResource, CmsRelationFilter.TARGETS); 12609 } 12610 // get online relations 12611 List<CmsRelation> relations = vfsDriver.readRelations( 12612 dbc, 12613 CmsProject.ONLINE_PROJECT_ID, 12614 onlineResource, 12615 CmsRelationFilter.TARGETS); 12616 // write offline relations 12617 Iterator<CmsRelation> itRelations = relations.iterator(); 12618 while (itRelations.hasNext()) { 12619 CmsRelation relation = itRelations.next(); 12620 vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation); 12621 } 12622 12623 // update the cache 12624 m_monitor.clearResourceCache(); 12625 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 12626 12627 if ((offlineResource == null) || offlineResource.getRootPath().equals(onlineResource.getRootPath())) { 12628 log( 12629 dbc, 12630 new CmsLogEntry( 12631 dbc, 12632 onlineResource.getStructureId(), 12633 CmsLogEntryType.RESOURCE_RESTORED, 12634 new String[] {onlineResource.getRootPath()}), 12635 false); 12636 } else { 12637 log( 12638 dbc, 12639 new CmsLogEntry( 12640 dbc, 12641 offlineResource.getStructureId(), 12642 CmsLogEntryType.RESOURCE_MOVE_RESTORED, 12643 new String[] {offlineResource.getRootPath(), onlineResource.getRootPath()}), 12644 false); 12645 } 12646 if (offlineResource != null) { 12647 OpenCms.fireCmsEvent( 12648 new CmsEvent( 12649 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 12650 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, offlineResource))); 12651 } else { 12652 OpenCms.fireCmsEvent( 12653 new CmsEvent( 12654 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 12655 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, onlineResource))); 12656 } 12657 } 12658 12659 /** 12660 * Updates the current users context dates with the given resource.<p> 12661 * 12662 * This checks the date information of the resource based on 12663 * {@link CmsResource#getDateLastModified()} as well as 12664 * {@link CmsResource#getDateReleased()} and {@link CmsResource#getDateExpired()}. 12665 * The current users request context is updated with the the "latest" dates found.<p> 12666 * 12667 * This is required in order to ensure proper setting of <code>"last-modified"</code> http headers 12668 * and also for expiration of cached elements in the Flex cache. 12669 * Consider the following use case: Page A is generated from resources x, y and z. 12670 * If either x, y or z has an expiration / release date set, then page A must expire at a certain point 12671 * in time. This is ensured by the context date check here.<p> 12672 * 12673 * @param dbc the current database context 12674 * @param resource the resource to get the date information from 12675 */ 12676 private void updateContextDates(CmsDbContext dbc, CmsResource resource) { 12677 12678 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12679 if (info != null) { 12680 info.updateFromResource(resource); 12681 } 12682 } 12683 12684 /** 12685 * Updates the current users context dates with each {@link CmsResource} object in the given list.<p> 12686 * 12687 * The given input list is returned unmodified.<p> 12688 * 12689 * Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p> 12690 * 12691 * @param dbc the current database context 12692 * @param resourceList a list of {@link CmsResource} objects 12693 * 12694 * @return the original list of CmsResources with the full resource name set 12695 */ 12696 private List<CmsResource> updateContextDates(CmsDbContext dbc, List<CmsResource> resourceList) { 12697 12698 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12699 if (info != null) { 12700 for (int i = 0; i < resourceList.size(); i++) { 12701 CmsResource resource = resourceList.get(i); 12702 info.updateFromResource(resource); 12703 } 12704 } 12705 return resourceList; 12706 } 12707 12708 /** 12709 * Returns a List of {@link CmsResource} objects generated when applying the given filter to the given list, 12710 * also updates the current users context dates with each {@link CmsResource} object in the given list, 12711 * also applies the selected resource filter to all resources in the list and returns the remaining resources.<p> 12712 * 12713 * Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p> 12714 * 12715 * @param dbc the current database context 12716 * @param resourceList a list of {@link CmsResource} objects 12717 * @param filter the resource filter to use 12718 * 12719 * @return a List of {@link CmsResource} objects generated when applying the given filter to the given list 12720 */ 12721 private List<CmsResource> updateContextDates( 12722 CmsDbContext dbc, 12723 List<CmsResource> resourceList, 12724 CmsResourceFilter filter) { 12725 12726 if (CmsResourceFilter.ALL == filter) { 12727 // if there is no filter required, then use the simpler method that does not apply the filter 12728 return new ArrayList<CmsResource>(updateContextDates(dbc, resourceList)); 12729 } 12730 12731 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12732 List<CmsResource> result = new ArrayList<CmsResource>(resourceList.size()); 12733 for (int i = 0; i < resourceList.size(); i++) { 12734 CmsResource resource = resourceList.get(i); 12735 if (filter.isValid(dbc.getRequestContext(), resource)) { 12736 result.add(resource); 12737 } 12738 // must also include "invalid" resources for the update of context dates 12739 // since a resource may be invalid because of release / expiration date 12740 if (info != null) { 12741 info.updateFromResource(resource); 12742 } 12743 } 12744 return result; 12745 } 12746 12747 /** 12748 * Updates the state of a resource, depending on the <code>resourceState</code> parameter.<p> 12749 * 12750 * @param dbc the db context 12751 * @param resource the resource 12752 * @param resourceState if <code>true</code> the resource state will be updated, if not just the structure state. 12753 * 12754 * @throws CmsDataAccessException if something goes wrong 12755 */ 12756 private void updateState(CmsDbContext dbc, CmsResource resource, boolean resourceState) 12757 throws CmsDataAccessException { 12758 12759 CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) 12760 ? dbc.currentProject().getUuid() 12761 : dbc.getProjectId(); 12762 resource.setUserLastModified(dbc.currentUser().getId()); 12763 if (resourceState) { 12764 // update the whole resource state 12765 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE); 12766 } else { 12767 // update the structure state 12768 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_STRUCTURE_STATE); 12769 } 12770 } 12771 12772 /** 12773 * Wraps a driver object with a dynamic proxy that counts method calls and their durations.<p> 12774 * 12775 * @param newDriverInstance the driver instance to wrap 12776 * @return the proxy 12777 */ 12778 private Object wrapDriverInProfilingProxy(Object newDriverInstance) { 12779 12780 Class<?> cls = getDriverInterfaceForProxy(newDriverInstance); 12781 if (cls == null) { 12782 return newDriverInstance; 12783 } 12784 return Proxy.newProxyInstance( 12785 Thread.currentThread().getContextClassLoader(), 12786 new Class[] {cls}, 12787 new CmsProfilingInvocationHandler(newDriverInstance, CmsDefaultProfilingHandler.INSTANCE)); 12788 } 12789 12790}