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.timing.CmsDefaultProfilingHandler; 040import org.opencms.db.timing.CmsProfilingInvocationHandler; 041import org.opencms.db.urlname.CmsUrlNameMappingEntry; 042import org.opencms.db.urlname.CmsUrlNameMappingFilter; 043import org.opencms.db.userpublishlist.A_CmsLogPublishListConverter; 044import org.opencms.db.userpublishlist.CmsLogPublishListConverterAllUsers; 045import org.opencms.db.userpublishlist.CmsLogPublishListConverterCurrentUser; 046import org.opencms.file.CmsDataAccessException; 047import org.opencms.file.CmsFile; 048import org.opencms.file.CmsFolder; 049import org.opencms.file.CmsGroup; 050import org.opencms.file.CmsObject; 051import org.opencms.file.CmsProject; 052import org.opencms.file.CmsProperty; 053import org.opencms.file.CmsPropertyDefinition; 054import org.opencms.file.CmsRequestContext; 055import org.opencms.file.CmsResource; 056import org.opencms.file.CmsResourceFilter; 057import org.opencms.file.CmsUser; 058import org.opencms.file.CmsUserSearchParameters; 059import org.opencms.file.CmsVfsException; 060import org.opencms.file.CmsVfsResourceAlreadyExistsException; 061import org.opencms.file.CmsVfsResourceNotFoundException; 062import org.opencms.file.I_CmsResource; 063import org.opencms.file.history.CmsHistoryFile; 064import org.opencms.file.history.CmsHistoryFolder; 065import org.opencms.file.history.CmsHistoryPrincipal; 066import org.opencms.file.history.CmsHistoryProject; 067import org.opencms.file.history.I_CmsHistoryResource; 068import org.opencms.file.quota.CmsFolderSizeEntry; 069import org.opencms.file.quota.CmsFolderSizeOptions; 070import org.opencms.file.types.CmsResourceTypeFolder; 071import org.opencms.file.types.CmsResourceTypeJsp; 072import org.opencms.file.types.I_CmsResourceType; 073import org.opencms.flex.CmsFlexRequestContextInfo; 074import org.opencms.gwt.shared.alias.CmsAliasImportResult; 075import org.opencms.gwt.shared.alias.CmsAliasImportStatus; 076import org.opencms.gwt.shared.alias.CmsAliasMode; 077import org.opencms.i18n.CmsLocaleManager; 078import org.opencms.i18n.CmsMessageContainer; 079import org.opencms.jsp.CmsJspNavBuilder; 080import org.opencms.lock.CmsLock; 081import org.opencms.lock.CmsLockException; 082import org.opencms.lock.CmsLockFilter; 083import org.opencms.lock.CmsLockManager; 084import org.opencms.lock.CmsLockType; 085import org.opencms.main.CmsEvent; 086import org.opencms.main.CmsException; 087import org.opencms.main.CmsIllegalArgumentException; 088import org.opencms.main.CmsIllegalStateException; 089import org.opencms.main.CmsInitException; 090import org.opencms.main.CmsLog; 091import org.opencms.main.CmsMultiException; 092import org.opencms.main.I_CmsEventListener; 093import org.opencms.main.OpenCms; 094import org.opencms.module.CmsModule; 095import org.opencms.monitor.CmsMemoryMonitor; 096import org.opencms.monitor.CmsMemoryMonitor.CacheType; 097import org.opencms.publish.CmsPublishEngine; 098import org.opencms.publish.CmsPublishJobInfoBean; 099import org.opencms.publish.CmsPublishReport; 100import org.opencms.relations.CmsCategoryService; 101import org.opencms.relations.CmsLink; 102import org.opencms.relations.CmsRelation; 103import org.opencms.relations.CmsRelationFilter; 104import org.opencms.relations.CmsRelationSystemValidator; 105import org.opencms.relations.CmsRelationType; 106import org.opencms.relations.CmsRelationType.CopyBehavior; 107import org.opencms.relations.I_CmsLinkParseable; 108import org.opencms.report.CmsLogReport; 109import org.opencms.report.I_CmsReport; 110import org.opencms.security.CmsAccessControlEntry; 111import org.opencms.security.CmsAccessControlList; 112import org.opencms.security.CmsAuthentificationException; 113import org.opencms.security.CmsOrganizationalUnit; 114import org.opencms.security.CmsPasswordEncryptionException; 115import org.opencms.security.CmsPermissionSet; 116import org.opencms.security.CmsPermissionSetCustom; 117import org.opencms.security.CmsPrincipal; 118import org.opencms.security.CmsRole; 119import org.opencms.security.CmsSecurityException; 120import org.opencms.security.I_CmsPermissionHandler; 121import org.opencms.security.I_CmsPermissionHandler.LockCheck; 122import org.opencms.security.I_CmsPrincipal; 123import org.opencms.security.twofactor.CmsSecondFactorInfo; 124import org.opencms.security.twofactor.CmsSecondFactorSetupException; 125import org.opencms.security.twofactor.CmsTwoFactorAuthenticationHandler; 126import org.opencms.site.CmsSiteMatcher; 127import org.opencms.util.CmsFileUtil; 128import org.opencms.util.CmsPath; 129import org.opencms.util.CmsStringUtil; 130import org.opencms.util.CmsUUID; 131import org.opencms.util.PrintfFormat; 132import org.opencms.workflow.CmsDefaultWorkflowManager; 133import org.opencms.workplace.threads.A_CmsProgressThread; 134 135import java.lang.reflect.Proxy; 136import java.util.ArrayList; 137import java.util.Arrays; 138import java.util.Collection; 139import java.util.Collections; 140import java.util.Comparator; 141import java.util.Date; 142import java.util.HashMap; 143import java.util.HashSet; 144import java.util.Iterator; 145import java.util.List; 146import java.util.ListIterator; 147import java.util.Locale; 148import java.util.Map; 149import java.util.Map.Entry; 150import java.util.Set; 151import java.util.TreeSet; 152import java.util.concurrent.ConcurrentMap; 153import java.util.concurrent.ExecutionException; 154import java.util.concurrent.TimeUnit; 155import java.util.function.Predicate; 156import java.util.function.Supplier; 157import java.util.regex.Pattern; 158import java.util.regex.PatternSyntaxException; 159import java.util.stream.Collectors; 160 161import org.apache.commons.logging.Log; 162 163import com.google.common.cache.Cache; 164import com.google.common.cache.CacheBuilder; 165import com.google.common.collect.ArrayListMultimap; 166import com.google.common.collect.Maps; 167import com.google.common.collect.Multimap; 168 169/** 170 * The OpenCms driver manager.<p> 171 * 172 * @since 6.0.0 173 */ 174public final class CmsDriverManager implements I_CmsEventListener { 175 176 /** 177 * Enum for distinguishing between login modes. 178 */ 179 public static enum LoginUserMode { 180 /** 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). */ 181 checkOnly, 182 183 /** Normal login process. */ 184 standard 185 } 186 187 /** 188 * Resource list which additionally knows whether it should be cacheable in the resource list cache or not. 189 */ 190 public static class ResourceListWithCacheability extends ArrayList<CmsResource> { 191 192 /** Serial version id. */ 193 private static final long serialVersionUID = 1L; 194 195 /** True if the list should be cacheable. */ 196 private boolean m_cacheable = true; 197 198 /** 199 * Creates a new instance. 200 */ 201 public ResourceListWithCacheability() { 202 203 super(); 204 } 205 206 /** 207 * Creates a new instance. 208 * @param initialCapacity the initial capacity 209 */ 210 public ResourceListWithCacheability(int initialCapacity) { 211 212 super(initialCapacity); 213 } 214 215 /** 216 * Returns true if the resource list is cacheable. 217 * 218 * @return true if the list is cacheable 219 */ 220 public boolean isCacheable() { 221 222 return m_cacheable; 223 } 224 225 /** 226 * Enables/disables cacheability for the resource list. 227 * @param cacheable true if the list should be cacheable 228 */ 229 public void setCacheable(boolean cacheable) { 230 231 m_cacheable = cacheable; 232 } 233 234 } 235 236 /** 237 * Special key class for caching the resource OU data with a Guava LoadingCache.<p> 238 * 239 * In principle, the actual cache key is just the current project, but because of how cache loaders work, 240 * the key must contain everything that varies between calls and is required to load the value. So we also store the DB context 241 * for use by the cache loader. The project (offline/online) must still be stored, because the DB context gets invalidated 242 * eventually, i.e. its project id gets nulled. 243 */ 244 public static class ResourceOUCacheKey { 245 246 /** The actual cache key. */ 247 private String m_actualKey; 248 249 /** The DB context. */ 250 private CmsDbContext m_dbc; 251 252 /** The driver manager to use. */ 253 private CmsDriverManager m_driverManager; 254 255 /** 256 * Creates a new instance. 257 * 258 * @param driverManager the driver manager to use 259 * @param dbc the current DB context 260 */ 261 public ResourceOUCacheKey(CmsDriverManager driverManager, CmsDbContext dbc) { 262 263 m_dbc = dbc; 264 m_driverManager = driverManager; 265 m_actualKey = CmsProject.ONLINE_PROJECT_ID.equals(dbc.currentProject().getId()) ? "ONLINE" : "OFFLINE"; 266 } 267 268 /** 269 * @see java.lang.Object#equals(java.lang.Object) 270 */ 271 @Override 272 public boolean equals(Object obj) { 273 274 return (obj instanceof ResourceOUCacheKey) 275 && ((ResourceOUCacheKey)obj).getActualKey().equals(getActualKey()); 276 } 277 278 /** 279 * Gets the stored DB context.<p> 280 * 281 * Note that the DB contex returned by this may have been invalidated! 282 * 283 * @return the stored DB context 284 */ 285 public CmsDbContext getDbContext() { 286 287 return m_dbc; 288 } 289 290 /** 291 * Gets the current driver manager. 292 * 293 * @return the driver manager to use 294 **/ 295 public CmsDriverManager getDriverManager() { 296 297 return m_driverManager; 298 } 299 300 /** 301 * @see java.lang.Object#hashCode() 302 */ 303 @Override 304 public int hashCode() { 305 306 return getActualKey().hashCode(); 307 } 308 309 /** 310 * Gets the actual key data. 311 * 312 * @return the actual key data 313 */ 314 private String getActualKey() { 315 316 return m_actualKey; 317 } 318 319 } 320 321 /** 322 * Helper class used to store information about resources assigned to OUs in a cache. 323 */ 324 public static class ResourceOUMap { 325 326 /** Multimap from the paths of resources to the OUs to which they are assigned as OU resources. */ 327 private Multimap<CmsPath, CmsOrganizationalUnit> m_ousByAssignedResourcePaths = ArrayListMultimap.create(); 328 329 /** The organizational units, with their UUIDs as keys. */ 330 private Map<CmsUUID, CmsOrganizationalUnit> m_ousById = new HashMap<>(); 331 332 /** 333 * Gets the list of organizational units to which a given root path belongs, according to the cached 334 * OU resource assignments. 335 * 336 * @param rootPath the root path 337 * @return the organizational units to which the path belongs 338 */ 339 public List<CmsOrganizationalUnit> getResourceOrgUnits(String rootPath) { 340 341 Set<CmsOrganizationalUnit> result = new HashSet<>(); 342 String currentPath = rootPath; 343 while (currentPath != null) { 344 result.addAll(m_ousByAssignedResourcePaths.get(new CmsPath(currentPath))); 345 currentPath = CmsResource.getParentFolder(currentPath); 346 } 347 return new ArrayList<>(result); 348 } 349 350 /** 351 * Reads the OU resource data from the VFS and initializes this instance with it. 352 * 353 * @param driverManager the driver manager to use 354 * @param dbc the current DB context 355 * @throws CmsException if something goes wrong 356 */ 357 public void init(CmsDriverManager driverManager, CmsDbContext dbc) throws CmsException { 358 359 List<CmsRelation> relations = driverManager.getRelationsForResource( 360 dbc, 361 null, 362 CmsRelationFilter.ALL.filterType(CmsRelationType.OU_RESOURCE)); 363 CmsOrganizationalUnit root = driverManager.readOrganizationalUnit(dbc, ""); 364 List<CmsOrganizationalUnit> children = driverManager.getOrganizationalUnits(dbc, root, true); 365 366 Set<CmsOrganizationalUnit> ous = new HashSet<>(); 367 ous.add(root); 368 ous.addAll(children); 369 init(relations, ous); 370 371 } 372 373 /** 374 * Initializes the OU resource data. 375 * 376 * @param ouRelations the current list of OU relations 377 * @param ous the current list of OUs 378 */ 379 public void init(Collection<CmsRelation> ouRelations, Collection<CmsOrganizationalUnit> ous) { 380 381 m_ousById.clear(); 382 m_ousByAssignedResourcePaths.clear(); 383 for (CmsOrganizationalUnit ou : ous) { 384 m_ousById.put(ou.getId(), ou); 385 } 386 for (CmsRelation rel : ouRelations) { 387 CmsOrganizationalUnit ou = m_ousById.get(rel.getSourceId()); 388 if (ou != null) { 389 m_ousByAssignedResourcePaths.put(new CmsPath(rel.getTargetPath()), ou); 390 } 391 } 392 } 393 } 394 395 /** 396 * The comparator used for comparing url name mapping entries by date.<p> 397 */ 398 class UrlNameMappingComparator implements Comparator<CmsUrlNameMappingEntry> { 399 400 /** 401 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 402 */ 403 public int compare(CmsUrlNameMappingEntry o1, CmsUrlNameMappingEntry o2) { 404 405 long date1 = o1.getDateChanged(); 406 long date2 = o2.getDateChanged(); 407 if (date1 < date2) { 408 return -1; 409 } 410 if (date1 > date2) { 411 return +1; 412 } 413 return 0; 414 } 415 } 416 417 /** 418 * Enumeration class for the mode parameter in the 419 * {@link CmsDriverManager#readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)} 420 * method.<p> 421 */ 422 private static class CmsReadChangedProjectResourceMode { 423 424 /** 425 * Default constructor.<p> 426 */ 427 protected CmsReadChangedProjectResourceMode() { 428 429 // noop 430 } 431 } 432 433 /** Request context attribute used to override the time used for time-based exclusive access checks. */ 434 public static final String ATTR_EXCLUSIVE_ACCESS_CLOCK = "ATTR_EXCLUSIVE_ACCESS_CLOCK"; 435 436 /** Attribute for signaling to the user driver that a specific OU should be initialized by fillDefaults. */ 437 public static final String ATTR_INIT_OU = "INIT_OU"; 438 439 /** DB context attribute used to communicate information about resource cacheability between various methods. */ 440 public static final String ATTR_PERMISSION_NOCACHE = "ATTR_PERMISSION_NOCACHE"; 441 442 /** Attribute login. */ 443 public static final String ATTRIBUTE_LOGIN = "A_LOGIN"; 444 445 /** Cache key for all properties. */ 446 public static final String CACHE_ALL_PROPERTIES = "_CAP_"; 447 448 /** 449 * Values indicating changes of a resource, 450 * ordered according to the scope of the change. 451 */ 452 /** Value to indicate a change in access control entries of a resource. */ 453 public static final int CHANGED_ACCESSCONTROL = 1; 454 455 /** Value to indicate a content change. */ 456 public static final int CHANGED_CONTENT = 16; 457 458 /** Value to indicate a change in the lastmodified settings of a resource. */ 459 public static final int CHANGED_LASTMODIFIED = 4; 460 461 /** Value to indicate a project change. */ 462 public static final int CHANGED_PROJECT = 32; 463 464 /** Value to indicate a change in the resource data. */ 465 public static final int CHANGED_RESOURCE = 8; 466 467 /** Value to indicate a change in the availability timeframe. */ 468 public static final int CHANGED_TIMEFRAME = 2; 469 470 /** "cache" string in the configuration-file. */ 471 public static final String CONFIGURATION_CACHE = "cache"; 472 473 /** "db" string in the configuration-file. */ 474 public static final String CONFIGURATION_DB = "db"; 475 476 /** "driver.history" string in the configuration-file. */ 477 public static final String CONFIGURATION_HISTORY = "driver.history"; 478 479 /** "driver.project" string in the configuration-file. */ 480 public static final String CONFIGURATION_PROJECT = "driver.project"; 481 482 /** "subscription.vfs" string in the configuration file. */ 483 public static final String CONFIGURATION_SUBSCRIPTION = "driver.subscription"; 484 485 /** "driver.user" string in the configuration-file. */ 486 public static final String CONFIGURATION_USER = "driver.user"; 487 488 /** "driver.vfs" string in the configuration-file. */ 489 public static final String CONFIGURATION_VFS = "driver.vfs"; 490 491 /** DBC attribute key needed to fix publishing behavior involving siblings. */ 492 public static final String KEY_CHANGED_AND_DELETED = "changedAndDeleted"; 493 494 /** The vfs path of the loast and found folder. */ 495 public static final String LOST_AND_FOUND_FOLDER = "/system/lost-found"; 496 497 /** The maximum length of a VFS resource path. */ 498 public static final int MAX_VFS_RESOURCE_PATH_LENGTH = 512; 499 500 /** Key for indicating no changes. */ 501 public static final int NOTHING_CHANGED = 0; 502 503 /** Name of the configuration parameter to enable/disable logging to the CMS_LOG table. */ 504 public static final String PARAM_LOG_TABLE_ENABLED = "log.table.enabled"; 505 506 /** Indicates to ignore the resource path when matching resources. */ 507 public static final String READ_IGNORE_PARENT = null; 508 509 /** Indicates to ignore the time value. */ 510 public static final long READ_IGNORE_TIME = 0L; 511 512 /** Indicates to ignore the resource type when matching resources. */ 513 public static final int READ_IGNORE_TYPE = -1; 514 515 /** Indicates to match resources NOT having the given state. */ 516 public static final int READMODE_EXCLUDE_STATE = 8; 517 518 /** Indicates to match immediate children only. */ 519 public static final int READMODE_EXCLUDE_TREE = 1; 520 521 /** Indicates to match resources NOT having the given type. */ 522 public static final int READMODE_EXCLUDE_TYPE = 4; 523 524 /** Mode for reading project resources from the db. */ 525 public static final int READMODE_IGNORESTATE = 0; 526 527 /** Indicates to match resources in given project only. */ 528 public static final int READMODE_INCLUDE_PROJECT = 2; 529 530 /** Indicates to match all successors. */ 531 public static final int READMODE_INCLUDE_TREE = 0; 532 533 /** Mode for reading project resources from the db. */ 534 public static final int READMODE_MATCHSTATE = 1; 535 536 /** Indicates if only file resources should be read. */ 537 public static final int READMODE_ONLY_FILES = 128; 538 539 /** Indicates if only folder resources should be read. */ 540 public static final int READMODE_ONLY_FOLDERS = 64; 541 542 /** Mode for reading project resources from the db. */ 543 public static final int READMODE_UNMATCHSTATE = 2; 544 545 /** Flag that can be used to disable the resource OU caching if necessary. */ 546 public static boolean resourceOrgUnitCachingEnabled = true; 547 548 /** Prefix char for temporary files in the VFS. */ 549 public static final String TEMP_FILE_PREFIX = "~"; 550 551 /** Key to indicate complete update. */ 552 public static final int UPDATE_ALL = 3; 553 554 /** Key to indicate update of resource record. */ 555 public static final int UPDATE_RESOURCE = 4; 556 557 /** Key to indicate update of last modified project reference. */ 558 public static final int UPDATE_RESOURCE_PROJECT = 6; 559 560 /** Key to indicate update of resource state. */ 561 public static final int UPDATE_RESOURCE_STATE = 1; 562 563 /** Key to indicate update of resource state including the content date. */ 564 public static final int UPDATE_RESOURCE_STATE_CONTENT = 7; 565 566 /** Key to indicate update of structure record. */ 567 public static final int UPDATE_STRUCTURE = 5; 568 569 /** Key to indicate update of structure state. */ 570 public static final int UPDATE_STRUCTURE_STATE = 2; 571 572 /** Map of pools defined in opencms.properties. */ 573 protected static ConcurrentMap<String, CmsDbPoolV11> m_pools = Maps.newConcurrentMap(); 574 575 /** The log object for this class. */ 576 private static final Log LOG = CmsLog.getLog(CmsDriverManager.class); 577 578 /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */ 579 private static final CmsReadChangedProjectResourceMode RCPRM_FILES_AND_FOLDERS_MODE = new CmsReadChangedProjectResourceMode(); 580 581 /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */ 582 private static final CmsReadChangedProjectResourceMode RCPRM_FILES_ONLY_MODE = new CmsReadChangedProjectResourceMode(); 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_FOLDERS_ONLY_MODE = new CmsReadChangedProjectResourceMode(); 586 587 private final Object m_publishTagLock = new Object(); 588 589 /** The history driver. */ 590 private I_CmsHistoryDriver m_historyDriver; 591 592 /** The HTML link validator. */ 593 private CmsRelationSystemValidator m_htmlLinkValidator; 594 595 /** The class used for cache key generation. */ 596 private I_CmsCacheKey m_keyGenerator; 597 598 /** The lock manager. */ 599 private CmsLockManager m_lockManager; 600 601 /** The log entry cache. */ 602 private List<CmsLogEntry> m_log = new ArrayList<CmsLogEntry>(); 603 604 /** Local reference to the memory monitor to avoid multiple lookups through the OpenCms singleton. */ 605 private CmsMemoryMonitor m_monitor; 606 607 /** The project driver. */ 608 private I_CmsProjectDriver m_projectDriver; 609 610 /** The the configuration read from the <code>opencms.properties</code> file. */ 611 private CmsParameterConfiguration m_propertyConfiguration; 612 613 /** the publish engine. */ 614 private CmsPublishEngine m_publishEngine; 615 616 /** Object used for synchronizing updates to the user publish list. */ 617 private Object m_publishListUpdateLock = new Object(); 618 619 /** The security manager (for access checks). */ 620 private CmsSecurityManager m_securityManager; 621 622 /** The sql manager. */ 623 private CmsSqlManager m_sqlManager; 624 625 /** The subscription driver. */ 626 private I_CmsSubscriptionDriver m_subscriptionDriver; 627 628 /** The user driver. */ 629 private I_CmsUserDriver m_userDriver; 630 631 /** The VFS driver. */ 632 private I_CmsVfsDriver m_vfsDriver; 633 634 private volatile Integer m_lastPublishTag = null; 635 636 /** Cache for which OUs can be skipped for principal transfer when deleting a user. */ 637 private Cache<String, Boolean> m_skipTransferPrincipalResourceCache; 638 639 /** 640 * Private constructor, initializes some required member variables.<p> 641 */ 642 private CmsDriverManager() { 643 644 // intentionally left blank 645 } 646 647 /** 648 * Reads the required configurations from the opencms.properties file and creates 649 * the various drivers to access the cms resources.<p> 650 * 651 * The initialization process of the driver manager and its drivers is split into 652 * the following phases: 653 * <ul> 654 * <li>the database pool configuration is read</li> 655 * <li>a plain and empty driver manager instance is created</li> 656 * <li>an instance of each driver is created</li> 657 * <li>the driver manager is passed to each driver during initialization</li> 658 * <li>finally, the driver instances are passed to the driver manager during initialization</li> 659 * </ul> 660 * 661 * @param configurationManager the configuration manager 662 * @param securityManager the security manager 663 * @param runtimeInfoFactory the initialized OpenCms runtime info factory 664 * @param publishEngine the publish engine 665 * 666 * @return CmsDriverManager the instantiated driver manager 667 * @throws CmsInitException if the driver manager couldn't be instantiated 668 */ 669 public static CmsDriverManager newInstance( 670 CmsConfigurationManager configurationManager, 671 CmsSecurityManager securityManager, 672 I_CmsDbContextFactory runtimeInfoFactory, 673 CmsPublishEngine publishEngine) 674 throws CmsInitException { 675 676 // read the opencms.properties from the configuration 677 CmsParameterConfiguration config = configurationManager.getConfiguration(); 678 679 CmsDriverManager driverManager = null; 680 try { 681 // create a driver manager instance 682 driverManager = new CmsDriverManager(); 683 if (CmsLog.INIT.isInfoEnabled()) { 684 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE1_0)); 685 } 686 if (runtimeInfoFactory == null) { 687 throw new CmsInitException( 688 org.opencms.main.Messages.get().container(org.opencms.main.Messages.ERR_CRITICAL_NO_DB_CONTEXT_0)); 689 } 690 } catch (Exception exc) { 691 CmsMessageContainer message = Messages.get().container(Messages.LOG_ERR_DRIVER_MANAGER_START_0); 692 if (LOG.isFatalEnabled()) { 693 LOG.fatal(message.key(), exc); 694 } 695 throw new CmsInitException(message, exc); 696 } 697 698 // store the configuration 699 driverManager.m_propertyConfiguration = config; 700 701 // set the security manager 702 driverManager.m_securityManager = securityManager; 703 704 // set the lock manager 705 driverManager.m_lockManager = new CmsLockManager(driverManager); 706 707 // create and set the sql manager 708 driverManager.m_sqlManager = new CmsSqlManager(driverManager); 709 710 // set the publish engine 711 driverManager.m_publishEngine = publishEngine; 712 713 if (CmsLog.INIT.isInfoEnabled()) { 714 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE2_0)); 715 } 716 717 // read the pool names to initialize 718 List<String> driverPoolNames = config.getList(CmsDriverManager.CONFIGURATION_DB + ".pools"); 719 if (CmsLog.INIT.isInfoEnabled()) { 720 String names = ""; 721 for (String name : driverPoolNames) { 722 names += name + " "; 723 } 724 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_POOLS_1, names)); 725 } 726 727 // initialize each pool 728 for (String name : driverPoolNames) { 729 driverManager.newPoolInstance(config, name); 730 } 731 732 // initialize the runtime info factory with the generated driver manager 733 runtimeInfoFactory.initialize(driverManager); 734 735 if (CmsLog.INIT.isInfoEnabled()) { 736 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE3_0)); 737 } 738 739 // store the access objects 740 CmsDbContext dbc = runtimeInfoFactory.getDbContext(); 741 driverManager.m_vfsDriver = (I_CmsVfsDriver)driverManager.createDriver( 742 dbc, 743 configurationManager, 744 config, 745 CONFIGURATION_VFS, 746 ".vfs.driver"); 747 dbc.clear(); 748 749 dbc = runtimeInfoFactory.getDbContext(); 750 driverManager.m_userDriver = (I_CmsUserDriver)driverManager.createDriver( 751 dbc, 752 configurationManager, 753 config, 754 CONFIGURATION_USER, 755 ".user.driver"); 756 dbc.clear(); 757 758 dbc = runtimeInfoFactory.getDbContext(); 759 driverManager.m_projectDriver = (I_CmsProjectDriver)driverManager.createDriver( 760 dbc, 761 configurationManager, 762 config, 763 CONFIGURATION_PROJECT, 764 ".project.driver"); 765 dbc.clear(); 766 767 dbc = runtimeInfoFactory.getDbContext(); 768 driverManager.m_historyDriver = (I_CmsHistoryDriver)driverManager.createDriver( 769 dbc, 770 configurationManager, 771 config, 772 CONFIGURATION_HISTORY, 773 ".history.driver"); 774 dbc.clear(); 775 776 dbc = runtimeInfoFactory.getDbContext(); 777 try { 778 // we wrap this in a try-catch because otherwise it would fail during the update 779 // process, since the subscription driver configuration does not exist at that point. 780 driverManager.m_subscriptionDriver = (I_CmsSubscriptionDriver)driverManager.createDriver( 781 dbc, 782 configurationManager, 783 config, 784 CONFIGURATION_SUBSCRIPTION, 785 ".subscription.driver"); 786 } catch (IndexOutOfBoundsException npe) { 787 LOG.warn("Could not instantiate subscription driver!"); 788 LOG.warn(npe.getLocalizedMessage(), npe); 789 } 790 dbc.clear(); 791 792 // register the driver manager for required events 793 org.opencms.main.OpenCms.addCmsEventListener( 794 driverManager, 795 new int[] { 796 I_CmsEventListener.EVENT_UPDATE_EXPORTS, 797 I_CmsEventListener.EVENT_CLEAR_CACHES, 798 I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES, 799 I_CmsEventListener.EVENT_USER_MODIFIED, 800 I_CmsEventListener.EVENT_RESOURCE_MODIFIED, 801 I_CmsEventListener.EVENT_OU_MODIFIED, 802 I_CmsEventListener.EVENT_PUBLISH_PROJECT}); 803 804 // not sure the manual cache flushing based on events is sufficient in all cases for the 'is principal transfer skippable?' cache, 805 // so we limit it to a few minutes 806 Cache<String, Boolean> skipTransferPrincipalResourceCache = CacheBuilder.newBuilder().concurrencyLevel( 807 4).expireAfterWrite(5, TimeUnit.MINUTES).build(); 808 driverManager.m_skipTransferPrincipalResourceCache = skipTransferPrincipalResourceCache; 809 810 // return the configured driver manager 811 return driverManager; 812 } 813 814 /** 815 * Adds an alias entry.<p> 816 * 817 * @param dbc the database context 818 * @param project the current project 819 * @param alias the alias to add 820 * 821 * @throws CmsException if something goes wrong 822 */ 823 public void addAlias(CmsDbContext dbc, CmsProject project, CmsAlias alias) throws CmsException { 824 825 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 826 vfsDriver.insertAlias(dbc, project, alias); 827 } 828 829 /** 830 * Adds a new relation to the given resource.<p> 831 * 832 * @param dbc the database context 833 * @param resource the resource to add the relation to 834 * @param target the target of the relation 835 * @param type the type of the relation 836 * @param importCase if importing relations 837 * 838 * @throws CmsException if something goes wrong 839 */ 840 public void addRelationToResource( 841 CmsDbContext dbc, 842 CmsResource resource, 843 CmsResource target, 844 CmsRelationType type, 845 boolean importCase) 846 throws CmsException { 847 848 if (type.isDefinedInContent()) { 849 throw new CmsIllegalArgumentException( 850 Messages.get().container( 851 Messages.ERR_ADD_RELATION_IN_CONTENT_3, 852 dbc.removeSiteRoot(resource.getRootPath()), 853 dbc.removeSiteRoot(target.getRootPath()), 854 type.getLocalizedName(dbc.getRequestContext().getLocale()))); 855 } 856 CmsRelation relation = new CmsRelation(resource, target, type); 857 getVfsDriver(dbc).createRelation(dbc, dbc.currentProject().getUuid(), relation); 858 // IMPORTANT: In the import case, the importer has to ensure that the 859 // resource is reindexed offline after the relation has been added. 860 // We do not trigger reindexing here for each added relation due to performance issues. 861 // in the the non-import case reindexing is triggered by the update of the last modification date. 862 if (!importCase) { 863 // log it 864 log( 865 dbc, 866 new CmsLogEntry( 867 dbc, 868 resource.getStructureId(), 869 CmsLogEntryType.RESOURCE_ADD_RELATION, 870 new String[] {relation.getSourcePath(), relation.getTargetPath()}), 871 false); 872 // touch the resource 873 setDateLastModified(dbc, resource, System.currentTimeMillis()); 874 } 875 } 876 877 /** 878 * Adds a resource to the given organizational unit.<p> 879 * 880 * @param dbc the current db context 881 * @param orgUnit the organizational unit to add the resource to 882 * @param resource the resource that is to be added to the organizational unit 883 * 884 * @throws CmsException if something goes wrong 885 * 886 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 887 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 888 */ 889 public void addResourceToOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource) 890 throws CmsException { 891 892 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 893 getUserDriver(dbc).addResourceToOrganizationalUnit(dbc, orgUnit, resource); 894 } 895 896 /** 897 * Adds a user to a group.<p> 898 * 899 * @param dbc the current database context 900 * @param username the name of the user that is to be added to the group 901 * @param groupname the name of the group 902 * @param readRoles if reading roles or groups 903 * 904 * @throws CmsException if operation was not successful 905 * @throws CmsDbEntryNotFoundException if the given user or the given group was not found 906 * 907 * @see #removeUserFromGroup(CmsDbContext, String, String, boolean) 908 */ 909 public void addUserToGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 910 throws CmsException, CmsDbEntryNotFoundException { 911 912 //check if group exists 913 CmsGroup group = readGroup(dbc, groupname); 914 if (group == null) { 915 // the group does not exists 916 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 917 } 918 if (group.isVirtual() && !readRoles) { 919 String roleName = CmsRole.valueOf(group).getGroupName(); 920 if (!userInGroup(dbc, username, roleName, true)) { 921 addUserToGroup(dbc, username, roleName, true); 922 return; 923 } 924 } 925 if (group.isVirtual()) { 926 // this is an hack to prevent unlimited recursive calls 927 readRoles = false; 928 } 929 if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) { 930 // we want a role but we got a group, or the other way 931 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 932 } 933 if (userInGroup(dbc, username, groupname, readRoles)) { 934 // the user is already member of the group 935 return; 936 } 937 //check if the user exists 938 CmsUser user = readUser(dbc, username); 939 if (user == null) { 940 // the user does not exists 941 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, username)); 942 } 943 944 // if adding an user to a role 945 if (readRoles) { 946 CmsRole role = CmsRole.valueOf(group); 947 // a role can only be set if the user has the given role 948 m_securityManager.checkRole(dbc, role); 949 // now we check if we already have the role 950 if (m_securityManager.hasRole(dbc, user, role)) { 951 // do nothing 952 return; 953 } 954 // and now we need to remove all possible child-roles 955 List<CmsRole> children = role.getChildren(true); 956 Iterator<CmsGroup> itUserGroups = getGroupsOfUser( 957 dbc, 958 username, 959 group.getOuFqn(), 960 true, 961 true, 962 true, 963 dbc.getRequestContext().getRemoteAddress()).iterator(); 964 while (itUserGroups.hasNext()) { 965 CmsGroup roleGroup = itUserGroups.next(); 966 if (children.contains(CmsRole.valueOf(roleGroup))) { 967 // remove only child roles 968 removeUserFromGroup(dbc, username, roleGroup.getName(), true); 969 } 970 } 971 // update virtual groups 972 Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator(); 973 while (it.hasNext()) { 974 CmsGroup virtualGroup = it.next(); 975 // here we say readroles = true, to prevent an unlimited recursive calls 976 addUserToGroup(dbc, username, virtualGroup.getName(), true); 977 } 978 } 979 980 //add this user to the group 981 getUserDriver(dbc).createUserInGroup(dbc, user.getId(), group.getId()); 982 983 // flush the cache 984 if (readRoles) { 985 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 986 } 987 m_monitor.flushUserGroups(user.getId()); 988 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 989 990 if (!dbc.getProjectId().isNullUUID() && !CmsProject.ONLINE_PROJECT_ID.equals(dbc.getProjectId())) { 991 // user modified event is not needed 992 return; 993 } 994 // fire user modified event 995 Map<String, Object> eventData = new HashMap<String, Object>(); 996 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 997 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 998 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 999 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 1000 eventData.put( 1001 I_CmsEventListener.KEY_USER_ACTION, 1002 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP); 1003 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 1004 } 1005 1006 /** 1007 * Changes the lock of a resource to the current user, 1008 * that is "steals" the lock from another user.<p> 1009 * 1010 * @param dbc the current database context 1011 * @param resource the resource to change the lock for 1012 * @param lockType the new lock type to set 1013 * 1014 * @throws CmsException if something goes wrong 1015 * @throws CmsSecurityException if something goes wrong 1016 * 1017 * 1018 * @see CmsObject#changeLock(String) 1019 * @see I_CmsResourceType#changeLock(CmsObject, CmsSecurityManager, CmsResource) 1020 * 1021 * @see CmsSecurityManager#hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter) 1022 */ 1023 public void changeLock(CmsDbContext dbc, CmsResource resource, CmsLockType lockType) 1024 throws CmsException, CmsSecurityException { 1025 1026 // get the current lock 1027 CmsLock currentLock = getLock(dbc, resource); 1028 // check if the resource is locked at all 1029 if (currentLock.getEditionLock().isUnlocked() && currentLock.getSystemLock().isUnlocked()) { 1030 throw new CmsLockException( 1031 Messages.get().container( 1032 Messages.ERR_CHANGE_LOCK_UNLOCKED_RESOURCE_1, 1033 dbc.getRequestContext().getSitePath(resource))); 1034 } else if ((lockType == CmsLockType.EXCLUSIVE) 1035 && currentLock.isExclusiveOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) { 1036 // the current lock requires no change 1037 return; 1038 } 1039 1040 // duplicate logic from CmsSecurityManager#hasPermissions() because lock state can't be ignored 1041 // if another user has locked the file, the current user can never get WRITE permissions with the default check 1042 int denied = 0; 1043 1044 // check if the current user is vfs manager 1045 boolean canIgnorePermissions = m_securityManager.hasRoleForResource( 1046 dbc, 1047 dbc.currentUser(), 1048 CmsRole.VFS_MANAGER, 1049 resource); 1050 // if the resource type is jsp 1051 // write is only allowed for developers 1052 if (!canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) { 1053 if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource)) { 1054 denied |= CmsPermissionSet.PERMISSION_WRITE; 1055 } 1056 } 1057 CmsPermissionSetCustom permissions; 1058 if (canIgnorePermissions) { 1059 // if the current user is administrator, anything is allowed 1060 permissions = new CmsPermissionSetCustom(~0); 1061 } else { 1062 // otherwise, get the permissions from the access control list 1063 permissions = getPermissions(dbc, resource, dbc.currentUser()); 1064 } 1065 // revoke the denied permissions 1066 permissions.denyPermissions(denied); 1067 // now check if write permission is granted 1068 if ((CmsPermissionSet.ACCESS_WRITE.getPermissions() 1069 & permissions.getPermissions()) != CmsPermissionSet.ACCESS_WRITE.getPermissions()) { 1070 // check failed, throw exception 1071 m_securityManager.checkPermissions( 1072 dbc.getRequestContext(), 1073 resource, 1074 CmsPermissionSet.ACCESS_WRITE, 1075 I_CmsPermissionHandler.PERM_DENIED); 1076 } 1077 // if we got here write permission is granted on the target 1078 1079 // remove the old lock 1080 m_lockManager.removeResource(dbc, resource, true, lockType.isSystem()); 1081 // apply the new lock 1082 lockResource(dbc, resource, lockType); 1083 } 1084 1085 /** 1086 * Returns a list with all sub resources of a given folder that have set the given property, 1087 * matching the current property's value with the given old value and replacing it by a given new value.<p> 1088 * 1089 * @param dbc the current database context 1090 * @param resource the resource on which property definition values are changed 1091 * @param propertyDefinition the name of the propertydefinition to change the value 1092 * @param oldValue the old value of the propertydefinition 1093 * @param newValue the new value of the propertydefinition 1094 * @param recursive if true, change the property value on the resource and recursively all property values on 1095 * sub-resources (only for folders) 1096 * @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed 1097 * 1098 * @throws CmsVfsException for now only when the search for the oldvalue failed. 1099 * @throws CmsException if operation was not successful 1100 */ 1101 public List<CmsResource> changeResourcesInFolderWithProperty( 1102 CmsDbContext dbc, 1103 CmsResource resource, 1104 String propertyDefinition, 1105 String oldValue, 1106 String newValue, 1107 boolean recursive) 1108 throws CmsVfsException, CmsException { 1109 1110 CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION; 1111 // collect the resources to look up 1112 List<CmsResource> resources = new ArrayList<CmsResource>(); 1113 if (recursive) { 1114 // read the files in the folder 1115 resources = readResourcesWithProperty(dbc, resource, propertyDefinition, null, filter); 1116 // add the folder itself 1117 resources.add(resource); 1118 } else { 1119 resources.add(resource); 1120 } 1121 1122 Pattern oldPattern; 1123 try { 1124 // remove the place holder if available 1125 String tmpOldValue = oldValue; 1126 if (tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_START) 1127 && tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_END)) { 1128 tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_START, ""); 1129 tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_END, ""); 1130 } 1131 // compile regular expression pattern 1132 oldPattern = Pattern.compile(tmpOldValue); 1133 } catch (PatternSyntaxException e) { 1134 throw new CmsVfsException( 1135 Messages.get().container( 1136 Messages.ERR_CHANGE_RESOURCES_IN_FOLDER_WITH_PROP_4, 1137 new Object[] {propertyDefinition, oldValue, newValue, resource.getRootPath()}), 1138 e); 1139 } 1140 1141 List<CmsResource> changedResources = new ArrayList<CmsResource>(resources.size()); 1142 // create permission set and filter to check each resource 1143 CmsPermissionSet perm = CmsPermissionSet.ACCESS_WRITE; 1144 for (int i = 0; i < resources.size(); i++) { 1145 // loop through found resources and check property values 1146 CmsResource res = resources.get(i); 1147 // check resource state and permissions 1148 try { 1149 m_securityManager.checkPermissions(dbc, res, perm, true, filter); 1150 } catch (Exception e) { 1151 // resource is deleted or not writable for current user 1152 continue; 1153 } 1154 CmsProperty property = readPropertyObject(dbc, res, propertyDefinition, false); 1155 String propertyValue = property.getValue(); 1156 boolean changed = false; 1157 if ((propertyValue != null) && oldPattern.matcher(propertyValue).matches()) { 1158 // apply the place holder content 1159 String tmpNewValue = CmsStringUtil.transformValues(oldValue, newValue, propertyValue); 1160 // change structure value 1161 property.setStructureValue(tmpNewValue); 1162 changed = true; 1163 } 1164 if (changed) { 1165 // write property object if something has changed 1166 writePropertyObject(dbc, res, property); 1167 changedResources.add(res); 1168 } 1169 } 1170 return changedResources; 1171 } 1172 1173 /** 1174 * Changes the resource flags of a resource.<p> 1175 * 1176 * The resource flags are used to indicate various "special" conditions 1177 * for a resource. Most notably, the "internal only" setting which signals 1178 * that a resource can not be directly requested with it's URL.<p> 1179 * 1180 * @param dbc the current database context 1181 * @param resource the resource to change the flags for 1182 * @param flags the new resource flags for this resource 1183 * 1184 * @throws CmsException if something goes wrong 1185 * 1186 * @see CmsObject#chflags(String, int) 1187 * @see I_CmsResourceType#chflags(CmsObject, CmsSecurityManager, CmsResource, int) 1188 */ 1189 public void chflags(CmsDbContext dbc, CmsResource resource, int flags) throws CmsException { 1190 1191 // must operate on a clone to ensure resource is not modified in case permissions are not granted 1192 CmsResource clone = (CmsResource)resource.clone(); 1193 clone.setFlags(flags); 1194 // log it 1195 log( 1196 dbc, 1197 new CmsLogEntry( 1198 dbc, 1199 resource.getStructureId(), 1200 CmsLogEntryType.RESOURCE_FLAGS, 1201 new String[] {resource.getRootPath()}), 1202 false); 1203 // write it 1204 writeResource(dbc, clone); 1205 } 1206 1207 /** 1208 * Changes the resource type of a resource.<p> 1209 * 1210 * OpenCms handles resources according to the resource type, 1211 * not the file suffix. This is e.g. why a JSP in OpenCms can have the 1212 * suffix ".html" instead of ".jsp" only. Changing the resource type 1213 * makes sense e.g. if you want to make a plain text file a JSP resource, 1214 * or a binary file an image, etc.<p> 1215 * 1216 * @param dbc the current database context 1217 * @param resource the resource to change the type for 1218 * @param type the new resource type for this resource 1219 * 1220 * @throws CmsException if something goes wrong 1221 * 1222 * @see CmsObject#chtype(String, int) 1223 * @see I_CmsResourceType#chtype(CmsObject, CmsSecurityManager, CmsResource, int) 1224 */ 1225 @SuppressWarnings({"javadoc", "deprecation"}) 1226 public void chtype(CmsDbContext dbc, CmsResource resource, int type) throws CmsException { 1227 1228 // must operate on a clone to ensure resource is not modified in case permissions are not granted 1229 CmsResource clone = (CmsResource)resource.clone(); 1230 I_CmsResourceType newType = OpenCms.getResourceManager().getResourceType(type); 1231 clone.setType(newType.getTypeId()); 1232 // log it 1233 log( 1234 dbc, 1235 new CmsLogEntry( 1236 dbc, 1237 resource.getStructureId(), 1238 CmsLogEntryType.RESOURCE_TYPE, 1239 new String[] {resource.getRootPath()}), 1240 false); 1241 // write it 1242 writeResource(dbc, clone); 1243 } 1244 1245 /** 1246 * Cleans up the publish history entries according to the given filter. 1247 * 1248 * @param dbc the database context 1249 * @param filter the filter 1250 * @return the number of cleaned up rows 1251 * @throws CmsDataAccessException if something goes wrong 1252 */ 1253 public int cleanupPublishHistory(CmsDbContext dbc, CmsPublishHistoryCleanupFilter filter) 1254 throws CmsDataAccessException { 1255 1256 int result = m_projectDriver.cleanupPublishHistory(dbc, filter); 1257 if (filter.getMode() == CmsPublishHistoryCleanupFilter.Mode.single) { 1258 OpenCms.getMemoryMonitor().cachePublishedResources(filter.getHistoryId().toString(), null); 1259 } else { 1260 OpenCms.getMemoryMonitor().flushCache(CmsMemoryMonitor.CacheType.PUBLISHED_RESOURCES); 1261 } 1262 return result; 1263 } 1264 1265 /** 1266 * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) 1267 */ 1268 public void cmsEvent(CmsEvent event) { 1269 1270 if (LOG.isDebugEnabled()) { 1271 LOG.debug(Messages.get().getBundle().key(Messages.LOG_CMS_EVENT_1, Integer.valueOf(event.getType()))); 1272 } 1273 1274 I_CmsReport report; 1275 CmsDbContext dbc; 1276 1277 switch (event.getType()) { 1278 1279 case I_CmsEventListener.EVENT_UPDATE_EXPORTS: 1280 dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT); 1281 updateExportPoints(dbc); 1282 break; 1283 1284 case I_CmsEventListener.EVENT_PUBLISH_PROJECT: 1285 CmsUUID publishHistoryId = new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID)); 1286 report = (I_CmsReport)event.getData().get(I_CmsEventListener.KEY_REPORT); 1287 dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT); 1288 m_monitor.clearCacheForPublishing(); 1289 writeExportPoints(dbc, report, publishHistoryId); 1290 break; 1291 1292 case I_CmsEventListener.EVENT_CLEAR_CACHES: 1293 m_monitor.clearCache(); 1294 m_skipTransferPrincipalResourceCache.invalidateAll(); 1295 break; 1296 case I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES: 1297 m_monitor.clearPrincipalsCache(); 1298 break; 1299 case I_CmsEventListener.EVENT_USER_MODIFIED: 1300 String action = (String)event.getData().get(I_CmsEventListener.KEY_USER_ACTION); 1301 m_monitor.flushCache( 1302 CacheType.USER, 1303 CacheType.GROUP, 1304 CacheType.ORG_UNIT, 1305 CacheType.ACL, 1306 CacheType.PERMISSION, 1307 CacheType.USER_LIST); 1308 if (I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP.equals(action) 1309 || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP.equals(action) 1310 || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU.equals(action)) { 1311 1312 Object userIdObj = event.getData().get(I_CmsEventListener.KEY_USER_ID); 1313 if (userIdObj != null) { 1314 CmsUUID userId = null; 1315 if (userIdObj instanceof CmsUUID) { 1316 userId = (CmsUUID)userIdObj; 1317 } else if (userIdObj instanceof String) { 1318 try { 1319 userId = new CmsUUID(userIdObj.toString()); 1320 } catch (Exception e) { 1321 LOG.error(e.getLocalizedMessage(), e); 1322 } 1323 } 1324 if (userId != null) { 1325 m_monitor.flushUserGroups(userId); 1326 } 1327 } else { 1328 m_monitor.flushCache(CacheType.USERGROUPS); 1329 } 1330 m_monitor.flushCache(CacheType.HAS_ROLE, CacheType.ROLE_LIST); 1331 } 1332 break; 1333 case I_CmsEventListener.EVENT_RESOURCE_MODIFIED: 1334 1335 Object resObj = event.getData().get(I_CmsEventListener.KEY_RESOURCE); 1336 if ((resObj != null) && (resObj instanceof CmsResource)) { 1337 CmsResource resource = (CmsResource)resObj; 1338 if (resource.getRootPath().startsWith(CmsUserDriver.ORGUNIT_BASE_FOLDER)) { 1339 m_skipTransferPrincipalResourceCache.invalidateAll(); 1340 } 1341 } 1342 break; 1343 case I_CmsEventListener.EVENT_OU_MODIFIED: 1344 m_skipTransferPrincipalResourceCache.invalidateAll(); 1345 break; 1346 default: 1347 // noop 1348 } 1349 } 1350 1351 /** 1352 * Copies the access control entries of a given resource to a destination resource.<p> 1353 * 1354 * Already existing access control entries of the destination resource are removed.<p> 1355 * 1356 * @param dbc the current database context 1357 * @param source the resource to copy the access control entries from 1358 * @param destination the resource to which the access control entries are copied 1359 * @param updateLastModifiedInfo if true, user and date "last modified" information on the target resource will be updated 1360 * 1361 * @throws CmsException if something goes wrong 1362 */ 1363 public void copyAccessControlEntries( 1364 CmsDbContext dbc, 1365 CmsResource source, 1366 CmsResource destination, 1367 boolean updateLastModifiedInfo) 1368 throws CmsException { 1369 1370 // get the entries to copy 1371 ListIterator<CmsAccessControlEntry> aceList = getUserDriver( 1372 dbc).readAccessControlEntries(dbc, dbc.currentProject(), source.getResourceId(), false).listIterator(); 1373 1374 // remove the current entries from the destination 1375 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), destination.getResourceId()); 1376 1377 // now write the new entries 1378 while (aceList.hasNext()) { 1379 CmsAccessControlEntry ace = aceList.next(); 1380 getUserDriver(dbc).createAccessControlEntry( 1381 dbc, 1382 dbc.currentProject(), 1383 destination.getResourceId(), 1384 ace.getPrincipal(), 1385 ace.getPermissions().getAllowedPermissions(), 1386 ace.getPermissions().getDeniedPermissions(), 1387 ace.getFlags()); 1388 } 1389 1390 // log it 1391 log( 1392 dbc, 1393 new CmsLogEntry( 1394 dbc, 1395 destination.getStructureId(), 1396 CmsLogEntryType.RESOURCE_PERMISSIONS, 1397 new String[] {destination.getRootPath()}), 1398 false); 1399 1400 // update the "last modified" information 1401 if (updateLastModifiedInfo) { 1402 setDateLastModified(dbc, destination, destination.getDateLastModified()); 1403 } 1404 1405 // clear the cache 1406 m_monitor.clearAccessControlListCache(); 1407 1408 // fire a resource modification event 1409 Map<String, Object> data = new HashMap<String, Object>(2); 1410 data.put(I_CmsEventListener.KEY_RESOURCE, destination); 1411 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 1412 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 1413 } 1414 1415 /** 1416 * Copies a resource.<p> 1417 * 1418 * You must ensure that the destination path is an absolute, valid and 1419 * existing VFS path. Relative paths from the source are currently not supported.<p> 1420 * 1421 * In case the target resource already exists, it is overwritten with the 1422 * source resource.<p> 1423 * 1424 * The <code>siblingMode</code> parameter controls how to handle siblings 1425 * during the copy operation. 1426 * Possible values for this parameter are: 1427 * <ul> 1428 * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_NEW}</code></li> 1429 * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_SIBLING}</code></li> 1430 * <li><code>{@link org.opencms.file.CmsResource#COPY_PRESERVE_SIBLING}</code></li> 1431 * </ul><p> 1432 * 1433 * @param dbc the current database context 1434 * @param source the resource to copy 1435 * @param destination the name of the copy destination with complete path 1436 * @param siblingMode indicates how to handle siblings during copy 1437 * 1438 * @throws CmsException if something goes wrong 1439 * @throws CmsIllegalArgumentException if the <code>source</code> argument is <code>null</code> 1440 * 1441 * @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode) 1442 * @see I_CmsResourceType#copyResource(CmsObject, CmsSecurityManager, CmsResource, String, CmsResource.CmsResourceCopyMode) 1443 */ 1444 public CmsResource copyResource( 1445 CmsDbContext dbc, 1446 CmsResource source, 1447 String destination, 1448 CmsResource.CmsResourceCopyMode siblingMode) 1449 throws CmsException, CmsIllegalArgumentException { 1450 1451 // check the sibling mode to see if this resource has to be copied as a sibling 1452 boolean copyAsSibling = false; 1453 1454 // siblings of folders are not supported 1455 if (!source.isFolder()) { 1456 // if the "copy as sibling" mode is used, set the flag to true 1457 if (siblingMode == CmsResource.COPY_AS_SIBLING) { 1458 copyAsSibling = true; 1459 } 1460 // if the mode is "preserve siblings", we have to check the sibling counter 1461 if (siblingMode == CmsResource.COPY_PRESERVE_SIBLING) { 1462 if (source.getSiblingCount() > 1) { 1463 copyAsSibling = true; 1464 } 1465 } 1466 } 1467 1468 // read the source properties 1469 List<CmsProperty> properties = readPropertyObjects(dbc, source, false); 1470 1471 if (copyAsSibling) { 1472 // create a sibling of the source file at the destination 1473 return createSibling(dbc, source, destination, properties); 1474 } 1475 1476 // prepare the content if required 1477 byte[] content = null; 1478 if (source.isFile()) { 1479 if (source instanceof CmsFile) { 1480 // resource already is a file 1481 content = ((CmsFile)source).getContents(); 1482 } 1483 if ((content == null) || (content.length < 1)) { 1484 // no known content yet - read from database 1485 content = getVfsDriver(dbc).readContent(dbc, dbc.currentProject().getUuid(), source.getResourceId()); 1486 } 1487 } 1488 1489 // determine destination folder 1490 String destinationFoldername = CmsResource.getParentFolder(destination); 1491 1492 // read the destination folder (will also check read permissions) 1493 CmsFolder destinationFolder = m_securityManager.readFolder( 1494 dbc, 1495 destinationFoldername, 1496 CmsResourceFilter.IGNORE_EXPIRATION); 1497 1498 // no further permission check required here, will be done in createResource() 1499 1500 // set user and creation time stamps 1501 long currentTime = System.currentTimeMillis(); 1502 long dateLastModified; 1503 CmsUUID userLastModified; 1504 if (source.isFolder()) { 1505 // folders always get a new date and user when they are copied 1506 dateLastModified = currentTime; 1507 userLastModified = dbc.currentUser().getId(); 1508 } else { 1509 // files keep the date and user last modified from the source 1510 dateLastModified = source.getDateLastModified(); 1511 userLastModified = source.getUserLastModified(); 1512 } 1513 1514 // check the resource flags 1515 int flags = source.getFlags(); 1516 if (source.isLabeled()) { 1517 // reset "labeled" link flag for new resource 1518 flags &= ~CmsResource.FLAG_LABELED; 1519 } 1520 1521 // create the new resource 1522 CmsResource newResource = new CmsResource( 1523 new CmsUUID(), 1524 new CmsUUID(), 1525 destination, 1526 source.getTypeId(), 1527 source.isFolder(), 1528 flags, 1529 dbc.currentProject().getUuid(), 1530 CmsResource.STATE_NEW, 1531 currentTime, 1532 dbc.currentUser().getId(), 1533 dateLastModified, 1534 userLastModified, 1535 source.getDateReleased(), 1536 source.getDateExpired(), 1537 1, 1538 source.getLength(), 1539 source.getDateContent(), 1540 source.getVersion()); // version number does not matter since it will be computed later 1541 1542 // trigger "is touched" state on resource (will ensure modification date is kept unchanged) 1543 newResource.setDateLastModified(dateLastModified); 1544 1545 // log it 1546 log( 1547 dbc, 1548 new CmsLogEntry( 1549 dbc, 1550 newResource.getStructureId(), 1551 CmsLogEntryType.RESOURCE_COPIED, 1552 new String[] {newResource.getRootPath()}), 1553 false); 1554 1555 // create the resource 1556 newResource = createResource(dbc, destination, newResource, content, properties, false); 1557 // copy relations 1558 copyRelations(dbc, source, newResource); 1559 1560 // copy the access control entries to the created resource 1561 copyAccessControlEntries(dbc, source, newResource, false); 1562 1563 // clear the cache 1564 m_monitor.clearAccessControlListCache(); 1565 1566 List<CmsResource> modifiedResources = new ArrayList<CmsResource>(); 1567 modifiedResources.add(source); 1568 modifiedResources.add(newResource); 1569 modifiedResources.add(destinationFolder); 1570 OpenCms.fireCmsEvent( 1571 new CmsEvent( 1572 I_CmsEventListener.EVENT_RESOURCE_COPIED, 1573 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, modifiedResources))); 1574 return newResource; 1575 } 1576 1577 /** 1578 * Copies a resource to the current project of the user.<p> 1579 * 1580 * @param dbc the current database context 1581 * @param resource the resource to apply this operation to 1582 * 1583 * @throws CmsException if something goes wrong 1584 * 1585 * @see CmsObject#copyResourceToProject(String) 1586 * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource) 1587 */ 1588 public void copyResourceToProject(CmsDbContext dbc, CmsResource resource) throws CmsException { 1589 1590 // copy the resource to the project only if the resource is not already in the project 1591 if (!isInsideCurrentProject(dbc, resource.getRootPath())) { 1592 // check if there are already any subfolders of this resource 1593 I_CmsProjectDriver projectDriver = getProjectDriver(dbc); 1594 if (resource.isFolder()) { 1595 List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject()); 1596 for (int i = 0; i < projectResources.size(); i++) { 1597 String resname = projectResources.get(i); 1598 if (resname.startsWith(resource.getRootPath())) { 1599 // delete the existing project resource first 1600 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname); 1601 } 1602 } 1603 } 1604 try { 1605 projectDriver.createProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath()); 1606 } catch (CmsException exc) { 1607 // if the subfolder exists already - all is ok 1608 } finally { 1609 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 1610 1611 OpenCms.fireCmsEvent( 1612 new CmsEvent( 1613 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 1614 Collections.<String, Object> singletonMap("project", dbc.currentProject()))); 1615 } 1616 } 1617 } 1618 1619 /** 1620 * Counts the locked resources in this project.<p> 1621 * 1622 * @param project the project to count the locked resources in 1623 * 1624 * @return the amount of locked resources in this project 1625 */ 1626 public int countLockedResources(CmsProject project) { 1627 1628 // count locks 1629 return m_lockManager.countExclusiveLocksInProject(project); 1630 } 1631 1632 /** 1633 * Add a new group to the Cms.<p> 1634 * 1635 * Only the admin can do this. 1636 * Only users, which are in the group "administrators" are granted.<p> 1637 * 1638 * @param dbc the current database context 1639 * @param id the id of the new group 1640 * @param name the name of the new group 1641 * @param description the description for the new group 1642 * @param flags the flags for the new group 1643 * @param parent the name of the parent group (or <code>null</code>) 1644 * 1645 * @return new created group 1646 * 1647 * @throws CmsException if the creation of the group failed 1648 * @throws CmsIllegalArgumentException if the length of the given name was below 1 1649 */ 1650 public CmsGroup createGroup(CmsDbContext dbc, CmsUUID id, String name, String description, int flags, String parent) 1651 throws CmsIllegalArgumentException, CmsException { 1652 1653 // check the group name 1654 OpenCms.getValidationHandler().checkGroupName(CmsOrganizationalUnit.getSimpleName(name)); 1655 // trim the name 1656 name = name.trim(); 1657 1658 // check the OU 1659 readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 1660 1661 // get the id of the parent group if necessary 1662 if (CmsStringUtil.isNotEmpty(parent)) { 1663 CmsGroup parentGroup = readGroup(dbc, parent); 1664 if (!parentGroup.isRole() 1665 && !CmsOrganizationalUnit.getParentFqn(parent).equals(CmsOrganizationalUnit.getParentFqn(name))) { 1666 throw new CmsDataAccessException( 1667 Messages.get().container( 1668 Messages.ERR_PARENT_GROUP_MUST_BE_IN_SAME_OU_3, 1669 CmsOrganizationalUnit.getSimpleName(name), 1670 CmsOrganizationalUnit.getParentFqn(name), 1671 parent)); 1672 } 1673 } 1674 1675 // create the group 1676 CmsGroup group = getUserDriver(dbc).createGroup(dbc, id, name, description, flags, parent); 1677 1678 // if the group is in fact a role, initialize it 1679 if (group.isVirtual()) { 1680 // get all users that have the given role 1681 String groupname = CmsRole.valueOf(group).getGroupName(); 1682 Iterator<CmsUser> it = getUsersOfGroup(dbc, groupname, true, false, true).iterator(); 1683 while (it.hasNext()) { 1684 CmsUser user = it.next(); 1685 // put them in the new group 1686 addUserToGroup(dbc, user.getName(), group.getName(), true); 1687 } 1688 } 1689 1690 // put it into the cache 1691 m_monitor.cacheGroup(group); 1692 1693 if (!dbc.getProjectId().isNullUUID()) { 1694 // group modified event is not needed 1695 return group; 1696 } 1697 // fire group modified event 1698 Map<String, Object> eventData = new HashMap<String, Object>(); 1699 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 1700 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 1701 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_CREATE); 1702 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 1703 1704 // return it 1705 return group; 1706 } 1707 1708 /** 1709 * Creates a new organizational unit.<p> 1710 * 1711 * @param dbc the current db context 1712 * @param ouFqn the fully qualified name of the new organizational unit 1713 * @param description the description of the new organizational unit 1714 * @param flags the flags for the new organizational unit 1715 * @param resource the first associated resource 1716 * 1717 * @return a <code>{@link CmsOrganizationalUnit}</code> object representing 1718 * the newly created organizational unit 1719 * 1720 * @throws CmsException if operation was not successful 1721 * 1722 * @see org.opencms.security.CmsOrgUnitManager#createOrganizationalUnit(CmsObject, String, String, int, String) 1723 */ 1724 public CmsOrganizationalUnit createOrganizationalUnit( 1725 CmsDbContext dbc, 1726 String ouFqn, 1727 String description, 1728 int flags, 1729 CmsResource resource) 1730 throws CmsException { 1731 1732 // normal case 1733 CmsOrganizationalUnit parent = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(ouFqn)); 1734 String name = CmsOrganizationalUnit.getSimpleName(ouFqn); 1735 if (name.endsWith(CmsOrganizationalUnit.SEPARATOR)) { 1736 name = name.substring(0, name.length() - 1); 1737 } 1738 1739 // check the name 1740 CmsResource.checkResourceName(name); 1741 1742 // trim the name 1743 name = name.trim(); 1744 1745 // check the description 1746 if (CmsStringUtil.isEmptyOrWhitespaceOnly(description)) { 1747 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_OU_DESCRIPTION_EMPTY_0)); 1748 } 1749 1750 // create the organizational unit 1751 CmsOrganizationalUnit orgUnit = getUserDriver(dbc).createOrganizationalUnit( 1752 dbc, 1753 name, 1754 description, 1755 flags, 1756 parent, 1757 resource != null ? resource.getRootPath() : null); 1758 // put the new created org unit into the cache 1759 m_monitor.cacheOrgUnit(orgUnit); 1760 1761 // flush relevant caches 1762 m_monitor.clearPrincipalsCache(); 1763 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 1764 1765 // create a publish list for the 'virtual' publish event 1766 CmsResource ouRes = readResource( 1767 dbc, 1768 CmsUserDriver.ORGUNIT_BASE_FOLDER + orgUnit.getName(), 1769 CmsResourceFilter.DEFAULT); 1770 CmsPublishList pl = new CmsPublishList(ouRes, false); 1771 pl.add(ouRes, false); 1772 1773 getProjectDriver(dbc).writePublishHistory( 1774 dbc, 1775 pl.getPublishHistoryId(), 1776 new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW)); 1777 1778 // fire the 'virtual' publish event 1779 Map<String, Object> eventData = new HashMap<String, Object>(); 1780 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 1781 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 1782 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 1783 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 1784 OpenCms.fireCmsEvent(afterPublishEvent); 1785 1786 if (!dbc.getProjectId().isNullUUID()) { 1787 // OU modified event is not needed 1788 return orgUnit; 1789 } 1790 1791 // fire OU modified event 1792 Map<String, Object> event2Data = new HashMap<String, Object>(); 1793 event2Data.put(I_CmsEventListener.KEY_OU_NAME, orgUnit.getName()); 1794 event2Data.put(I_CmsEventListener.KEY_OU_ID, orgUnit.getId().toString()); 1795 event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_CREATE); 1796 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data)); 1797 1798 // return it 1799 return orgUnit; 1800 } 1801 1802 /** 1803 * Creates a project.<p> 1804 * 1805 * @param dbc the current database context 1806 * @param name the name of the project to create 1807 * @param description the description of the project 1808 * @param groupname the project user group to be set 1809 * @param managergroupname the project manager group to be set 1810 * @param projecttype the type of the project 1811 * 1812 * @return the created project 1813 * 1814 * @throws CmsIllegalArgumentException if the chosen <code>name</code> is already used 1815 * by the online project, or if the name is not valid 1816 * @throws CmsException if something goes wrong 1817 */ 1818 public CmsProject createProject( 1819 CmsDbContext dbc, 1820 String name, 1821 String description, 1822 String groupname, 1823 String managergroupname, 1824 CmsProject.CmsProjectType projecttype) 1825 throws CmsIllegalArgumentException, CmsException { 1826 1827 if (CmsProject.ONLINE_PROJECT_NAME.equals(name)) { 1828 throw new CmsIllegalArgumentException( 1829 Messages.get().container( 1830 Messages.ERR_CREATE_PROJECT_ONLINE_PROJECT_NAME_1, 1831 CmsProject.ONLINE_PROJECT_NAME)); 1832 } 1833 // check the name 1834 CmsProject.checkProjectName(CmsOrganizationalUnit.getSimpleName(name)); 1835 // check the ou 1836 readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 1837 // read the needed groups from the cms 1838 CmsGroup group = readGroup(dbc, groupname); 1839 CmsGroup managergroup = readGroup(dbc, managergroupname); 1840 1841 return getProjectDriver(dbc).createProject( 1842 dbc, 1843 new CmsUUID(), 1844 dbc.currentUser(), 1845 group, 1846 managergroup, 1847 name, 1848 description, 1849 projecttype.getDefaultFlags(), 1850 projecttype); 1851 } 1852 1853 /** 1854 * Creates a property definition.<p> 1855 * 1856 * Property definitions are valid for all resource types.<p> 1857 * 1858 * @param dbc the current database context 1859 * @param name the name of the property definition to create 1860 * 1861 * @return the created property definition 1862 * 1863 * @throws CmsException if something goes wrong 1864 */ 1865 public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 1866 1867 CmsPropertyDefinition propertyDefinition = null; 1868 1869 name = name.trim(); 1870 // validate the property name 1871 CmsPropertyDefinition.checkPropertyName(name); 1872 // TODO: make the type a parameter 1873 try { 1874 try { 1875 propertyDefinition = getVfsDriver(dbc).readPropertyDefinition( 1876 dbc, 1877 name, 1878 dbc.currentProject().getUuid()); 1879 } catch (CmsException e) { 1880 propertyDefinition = getVfsDriver(dbc).createPropertyDefinition( 1881 dbc, 1882 dbc.currentProject().getUuid(), 1883 name, 1884 CmsPropertyDefinition.TYPE_NORMAL); 1885 } 1886 1887 try { 1888 getVfsDriver(dbc).readPropertyDefinition(dbc, name, CmsProject.ONLINE_PROJECT_ID); 1889 } catch (CmsException e) { 1890 getVfsDriver(dbc).createPropertyDefinition( 1891 dbc, 1892 CmsProject.ONLINE_PROJECT_ID, 1893 name, 1894 CmsPropertyDefinition.TYPE_NORMAL); 1895 } 1896 1897 try { 1898 getHistoryDriver(dbc).readPropertyDefinition(dbc, name); 1899 } catch (CmsException e) { 1900 getHistoryDriver(dbc).createPropertyDefinition(dbc, name, CmsPropertyDefinition.TYPE_NORMAL); 1901 } 1902 } finally { 1903 1904 // fire an event that a property of a resource has been deleted 1905 OpenCms.fireCmsEvent( 1906 new CmsEvent( 1907 I_CmsEventListener.EVENT_PROPERTY_DEFINITION_CREATED, 1908 Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition))); 1909 1910 } 1911 1912 return propertyDefinition; 1913 } 1914 1915 /** 1916 * Creates a new publish job.<p> 1917 * 1918 * @param dbc the current database context 1919 * @param publishJob the publish job to create 1920 * 1921 * @throws CmsException if something goes wrong 1922 */ 1923 public void createPublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 1924 1925 getProjectDriver(dbc).createPublishJob(dbc, publishJob); 1926 } 1927 1928 /** 1929 * Creates a new resource with the provided content and properties.<p> 1930 * 1931 * The <code>content</code> parameter may be <code>null</code> if the resource id 1932 * already exists. If so, the created resource will be a sibling of the existing 1933 * resource, the existing content will remain unchanged.<p> 1934 * 1935 * This is used during file import for import of siblings as the 1936 * <code>manifest.xml</code> only contains one binary copy per file.<p> 1937 * 1938 * If the resource id exists but the <code>content</code> is not <code>null</code>, 1939 * the created resource will be made a sibling of the existing resource, 1940 * and both will share the new content.<p> 1941 * 1942 * @param dbc the current database context 1943 * @param resourcePath the name of the resource to create (full path) 1944 * @param resource the new resource to create 1945 * @param content the content for the new resource 1946 * @param properties the properties for the new resource 1947 * @param importCase if <code>true</code>, signals that this operation is done while 1948 * importing resource, causing different lock behavior and 1949 * potential "lost and found" usage 1950 * 1951 * @return the created resource 1952 * 1953 * @throws CmsException if something goes wrong 1954 */ 1955 public CmsResource createResource( 1956 CmsDbContext dbc, 1957 String resourcePath, 1958 CmsResource resource, 1959 byte[] content, 1960 List<CmsProperty> properties, 1961 boolean importCase) 1962 throws CmsException { 1963 1964 CmsResource newResource = null; 1965 if (resource.isFolder()) { 1966 resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath); 1967 } 1968 1969 try { 1970 synchronized (this) { 1971 // need to provide the parent folder id for resource creation 1972 String parentFolderName = CmsResource.getParentFolder(resourcePath); 1973 CmsResource parentFolder = readFolder(dbc, parentFolderName, CmsResourceFilter.IGNORE_EXPIRATION); 1974 1975 CmsLock parentLock = getLock(dbc, parentFolder); 1976 // it is not allowed to create a resource in a folder locked by other user 1977 if (!parentLock.isUnlocked() && !parentLock.isOwnedBy(dbc.currentUser())) { 1978 // one exception is if the admin user tries to create a temporary resource 1979 if (!CmsResource.getName(resourcePath).startsWith(TEMP_FILE_PREFIX) 1980 || !m_securityManager.hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) { 1981 throw new CmsLockException( 1982 Messages.get().container( 1983 Messages.ERR_CREATE_RESOURCE_PARENT_LOCK_1, 1984 dbc.removeSiteRoot(resourcePath))); 1985 } 1986 } 1987 if (CmsResourceTypeJsp.isJsp(resource)) { 1988 // security check when trying to create a new jsp file 1989 m_securityManager.checkRoleForResource(dbc, CmsRole.VFS_MANAGER, parentFolder); 1990 } 1991 1992 // check import configuration of "lost and found" folder 1993 boolean useLostAndFound = importCase && !OpenCms.getImportExportManager().overwriteCollidingResources(); 1994 1995 // check if the resource already exists by name 1996 CmsResource currentResourceByName = null; 1997 try { 1998 currentResourceByName = readResource(dbc, resourcePath, CmsResourceFilter.ALL); 1999 } catch (CmsVfsResourceNotFoundException e) { 2000 // if the resource does exist, we have to check the id later to decide what to do 2001 } 2002 2003 // check if the resource already exists by id 2004 try { 2005 CmsResource currentResourceById = readResource( 2006 dbc, 2007 resource.getStructureId(), 2008 CmsResourceFilter.ALL); 2009 // it is not allowed to import resources when there is already a resource with the same id but different path 2010 if (!currentResourceById.getRootPath().equals(resourcePath)) { 2011 throw new CmsVfsResourceAlreadyExistsException( 2012 Messages.get().container( 2013 Messages.ERR_RESOURCE_WITH_ID_ALREADY_EXISTS_3, 2014 dbc.removeSiteRoot(resourcePath), 2015 dbc.removeSiteRoot(currentResourceById.getRootPath()), 2016 currentResourceById.getStructureId())); 2017 } 2018 } catch (CmsVfsResourceNotFoundException e) { 2019 // if the resource does exist, we have to check the id later to decide what to do 2020 } 2021 2022 // check the permissions 2023 if (currentResourceByName == null) { 2024 // resource does not exist - check parent folder 2025 m_securityManager.checkPermissions( 2026 dbc, 2027 parentFolder, 2028 CmsPermissionSet.ACCESS_WRITE, 2029 false, 2030 CmsResourceFilter.IGNORE_EXPIRATION); 2031 } else { 2032 // resource already exists - check existing resource 2033 m_securityManager.checkPermissions( 2034 dbc, 2035 currentResourceByName, 2036 CmsPermissionSet.ACCESS_WRITE, 2037 !importCase, 2038 CmsResourceFilter.ALL); 2039 } 2040 2041 // now look for the resource by name 2042 if (currentResourceByName != null) { 2043 boolean overwrite = true; 2044 if (currentResourceByName.getState().isDeleted()) { 2045 if (!currentResourceByName.isFolder()) { 2046 // if a non-folder resource was deleted it's treated like a new resource 2047 overwrite = false; 2048 } 2049 } else { 2050 if (!importCase) { 2051 // direct "overwrite" of a resource is possible only during import, 2052 // or if the resource has been deleted 2053 throw new CmsVfsResourceAlreadyExistsException( 2054 org.opencms.db.generic.Messages.get().container( 2055 org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1, 2056 dbc.removeSiteRoot(resource.getRootPath()))); 2057 } 2058 // the resource already exists 2059 if (!resource.isFolder() 2060 && useLostAndFound 2061 && (!currentResourceByName.getResourceId().equals(resource.getResourceId()))) { 2062 // semantic change: the current resource is moved to L&F and the imported resource will overwrite the old one 2063 // will leave the resource with state deleted, 2064 // but it does not matter, since the state will be set later again 2065 moveToLostAndFound(dbc, currentResourceByName, false); 2066 } 2067 } 2068 if (!overwrite) { 2069 // lock the resource, will throw an exception if not lockable 2070 lockResource(dbc, currentResourceByName, CmsLockType.EXCLUSIVE); 2071 2072 // trigger createResource instead of writeResource 2073 currentResourceByName = null; 2074 } 2075 } 2076 // if null, create new resource, if not null write resource 2077 CmsResource overwrittenResource = currentResourceByName; 2078 2079 // extract the name (without path) 2080 String targetName = CmsResource.getName(resourcePath); 2081 2082 int contentLength; 2083 2084 // modify target name and content length in case of folder creation 2085 if (resource.isFolder()) { 2086 // folders never have any content 2087 contentLength = -1; 2088 // must cut of trailing '/' for folder creation (or name check fails) 2089 if (CmsResource.isFolder(targetName)) { 2090 targetName = targetName.substring(0, targetName.length() - 1); 2091 } 2092 } else { 2093 // otherwise ensure content and content length are set correctly 2094 if (content != null) { 2095 // if a content is provided, in each case the length is the length of this content 2096 contentLength = content.length; 2097 } else if (overwrittenResource != null) { 2098 // we have no content, but an already existing resource - length remains unchanged 2099 contentLength = overwrittenResource.getLength(); 2100 } else { 2101 // we have no content - length is used as set in the resource 2102 contentLength = resource.getLength(); 2103 } 2104 } 2105 2106 // check if the target name is valid (forbidden chars etc.), 2107 // if not throw an exception 2108 // must do this here since targetName is modified in folder case (see above) 2109 CmsResource.checkResourceName(targetName); 2110 2111 // set structure and resource ids as given 2112 CmsUUID structureId = resource.getStructureId(); 2113 CmsUUID resourceId = resource.getResourceId(); 2114 2115 // decide which structure id to use 2116 if (overwrittenResource != null) { 2117 // resource exists, re-use existing ids 2118 structureId = overwrittenResource.getStructureId(); 2119 } 2120 if (structureId.isNullUUID()) { 2121 // need a new structure id 2122 structureId = new CmsUUID(); 2123 } 2124 2125 // decide which resource id to use 2126 if (overwrittenResource != null) { 2127 // if we are overwriting we have to assure the resource id is the same 2128 resourceId = overwrittenResource.getResourceId(); 2129 } 2130 if (resourceId.isNullUUID()) { 2131 // need a new resource id 2132 resourceId = new CmsUUID(); 2133 } 2134 2135 try { 2136 // check online resource 2137 CmsResource onlineResource = getVfsDriver( 2138 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resourcePath, true); 2139 // only allow to overwrite with different id if importing (createResource will set the right id) 2140 try { 2141 CmsResource offlineResource = getVfsDriver(dbc).readResource( 2142 dbc, 2143 dbc.currentProject().getUuid(), 2144 onlineResource.getStructureId(), 2145 true); 2146 if (!offlineResource.getRootPath().equals(onlineResource.getRootPath())) { 2147 throw new CmsVfsOnlineResourceAlreadyExistsException( 2148 Messages.get().container( 2149 Messages.ERR_ONLINE_RESOURCE_EXISTS_2, 2150 dbc.removeSiteRoot(resourcePath), 2151 dbc.removeSiteRoot(offlineResource.getRootPath()))); 2152 } 2153 } catch (CmsVfsResourceNotFoundException e) { 2154 // there is no problem for now 2155 // but should never happen 2156 if (LOG.isErrorEnabled()) { 2157 LOG.error(e.getLocalizedMessage(), e); 2158 } 2159 } 2160 } catch (CmsVfsResourceNotFoundException e) { 2161 // ok, there is no online entry to worry about 2162 } 2163 2164 // now create a resource object with all informations 2165 newResource = new CmsResource( 2166 structureId, 2167 resourceId, 2168 resourcePath, 2169 resource.getTypeId(), 2170 resource.isFolder(), 2171 resource.getFlags(), 2172 dbc.currentProject().getUuid(), 2173 resource.getState(), 2174 resource.getDateCreated(), 2175 resource.getUserCreated(), 2176 resource.getDateLastModified(), 2177 resource.getUserLastModified(), 2178 resource.getDateReleased(), 2179 resource.getDateExpired(), 2180 1, 2181 contentLength, 2182 resource.getDateContent(), 2183 resource.getVersion()); // version number does not matter since it will be computed later 2184 2185 // ensure date is updated only if required 2186 if (resource.isTouched()) { 2187 // this will trigger the internal "is touched" state on the new resource 2188 newResource.setDateLastModified(resource.getDateLastModified()); 2189 } 2190 2191 if (resource.isFile()) { 2192 // check if a sibling to the imported resource lies in a marked site 2193 if (labelResource(dbc, resource, resourcePath, 2)) { 2194 int flags = resource.getFlags(); 2195 flags |= CmsResource.FLAG_LABELED; 2196 resource.setFlags(flags); 2197 } 2198 // ensure siblings don't overwrite existing resource records 2199 if (content == null) { 2200 newResource.setState(CmsResource.STATE_KEEP); 2201 } 2202 } 2203 2204 // delete all relations for the resource, before writing the content 2205 getVfsDriver( 2206 dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), newResource, CmsRelationFilter.TARGETS); 2207 if (overwrittenResource == null) { 2208 CmsLock lock = getLock(dbc, newResource); 2209 if (lock.getEditionLock().isExclusive()) { 2210 unlockResource(dbc, newResource, true, false); 2211 } 2212 // resource does not exist. 2213 newResource = getVfsDriver( 2214 dbc).createResource(dbc, dbc.currentProject().getUuid(), newResource, content); 2215 } else { 2216 // resource already exists. 2217 // probably the resource is a merged page file that gets overwritten during import, or it gets 2218 // overwritten by a copy operation. if so, the structure & resource state are not modified to changed. 2219 int updateStates = (overwrittenResource.getState().isNew() 2220 ? CmsDriverManager.NOTHING_CHANGED 2221 : CmsDriverManager.UPDATE_ALL); 2222 getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), newResource, updateStates); 2223 2224 if ((content != null) && resource.isFile()) { 2225 // also update file content if required 2226 getVfsDriver(dbc).writeContent(dbc, newResource.getResourceId(), content); 2227 } 2228 } 2229 2230 // write the properties (internal operation, no events or duplicate permission checks) 2231 writePropertyObjects(dbc, newResource, properties, false); 2232 2233 // lock the created resource 2234 try { 2235 // if it is locked by another user (copied or moved resource) this lock should be preserved and 2236 // the exception is OK: locks on created resources are a slave feature to original locks 2237 lockResource(dbc, newResource, CmsLockType.EXCLUSIVE); 2238 } catch (CmsLockException cle) { 2239 if (LOG.isDebugEnabled()) { 2240 LOG.debug( 2241 Messages.get().getBundle().key( 2242 Messages.ERR_CREATE_RESOURCE_LOCK_1, 2243 new Object[] {dbc.removeSiteRoot(newResource.getRootPath())})); 2244 } 2245 } 2246 2247 if (!importCase) { 2248 log( 2249 dbc, 2250 new CmsLogEntry( 2251 dbc, 2252 newResource.getStructureId(), 2253 CmsLogEntryType.RESOURCE_CREATED, 2254 new String[] {resource.getRootPath()}), 2255 false); 2256 } else { 2257 log( 2258 dbc, 2259 new CmsLogEntry( 2260 dbc, 2261 newResource.getStructureId(), 2262 CmsLogEntryType.RESOURCE_IMPORTED, 2263 new String[] {resource.getRootPath()}), 2264 false); 2265 } 2266 } 2267 } finally { 2268 // clear the internal caches 2269 m_monitor.clearAccessControlListCache(); 2270 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2271 2272 if (newResource != null) { 2273 // fire an event that a new resource has been created 2274 OpenCms.fireCmsEvent( 2275 new CmsEvent( 2276 I_CmsEventListener.EVENT_RESOURCE_CREATED, 2277 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, newResource))); 2278 } 2279 } 2280 return newResource; 2281 } 2282 2283 /** 2284 * Creates a new resource of the given resource type 2285 * with the provided content and properties.<p> 2286 * 2287 * If the provided content is null and the resource is not a folder, 2288 * the content will be set to an empty byte array.<p> 2289 * 2290 * @param dbc the current database context 2291 * @param resourcename the name of the resource to create (full path) 2292 * @param type the type of the resource to create 2293 * @param content the content for the new resource 2294 * @param properties the properties for the new resource 2295 * 2296 * @return the created resource 2297 * 2298 * @throws CmsException if something goes wrong 2299 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 2300 * 2301 * @see CmsObject#createResource(String, int, byte[], List) 2302 * @see CmsObject#createResource(String, int) 2303 * @see I_CmsResourceType#createResource(CmsObject, CmsSecurityManager, String, byte[], List) 2304 */ 2305 @SuppressWarnings("javadoc") 2306 public CmsResource createResource( 2307 CmsDbContext dbc, 2308 String resourcename, 2309 int type, 2310 byte[] content, 2311 List<CmsProperty> properties) 2312 throws CmsException, CmsIllegalArgumentException { 2313 2314 String targetName = resourcename; 2315 2316 if (content == null) { 2317 // name based resource creation MUST have a content 2318 content = new byte[0]; 2319 } 2320 int size; 2321 2322 if (CmsFolder.isFolderType(type)) { 2323 // must cut of trailing '/' for folder creation 2324 if (CmsResource.isFolder(targetName)) { 2325 targetName = targetName.substring(0, targetName.length() - 1); 2326 } 2327 size = -1; 2328 } else { 2329 size = content.length; 2330 } 2331 2332 // create a new resource 2333 CmsResource newResource = new CmsResource( 2334 CmsUUID.getNullUUID(), // uuids will be "corrected" later 2335 CmsUUID.getNullUUID(), 2336 targetName, 2337 type, 2338 CmsFolder.isFolderType(type), 2339 0, 2340 dbc.currentProject().getUuid(), 2341 CmsResource.STATE_NEW, 2342 0, 2343 dbc.currentUser().getId(), 2344 0, 2345 dbc.currentUser().getId(), 2346 CmsResource.DATE_RELEASED_DEFAULT, 2347 CmsResource.DATE_EXPIRED_DEFAULT, 2348 1, 2349 size, 2350 0, // version number does not matter since it will be computed later 2351 0); // content time will be corrected later 2352 2353 return createResource(dbc, targetName, newResource, content, properties, false); 2354 } 2355 2356 /** 2357 * Creates a new sibling of the source resource.<p> 2358 * 2359 * @param dbc the current database context 2360 * @param source the resource to create a sibling for 2361 * @param destination the name of the sibling to create with complete path 2362 * @param properties the individual properties for the new sibling 2363 * 2364 * @return the new created sibling 2365 * 2366 * @throws CmsException if something goes wrong 2367 * 2368 * @see CmsObject#createSibling(String, String, List) 2369 * @see I_CmsResourceType#createSibling(CmsObject, CmsSecurityManager, CmsResource, String, List) 2370 */ 2371 public CmsResource createSibling( 2372 CmsDbContext dbc, 2373 CmsResource source, 2374 String destination, 2375 List<CmsProperty> properties) 2376 throws CmsException { 2377 2378 if (source.isFolder()) { 2379 throw new CmsVfsException(Messages.get().container(Messages.ERR_VFS_FOLDERS_DONT_SUPPORT_SIBLINGS_0)); 2380 } 2381 2382 // determine destination folder and resource name 2383 String destinationFoldername = CmsResource.getParentFolder(destination); 2384 2385 // read the destination folder (will also check read permissions) 2386 CmsFolder destinationFolder = readFolder(dbc, destinationFoldername, CmsResourceFilter.IGNORE_EXPIRATION); 2387 2388 // no further permission check required here, will be done in createResource() 2389 2390 // check the resource flags 2391 int flags = source.getFlags(); 2392 if (labelResource(dbc, source, destination, 1)) { 2393 // set "labeled" link flag for new resource 2394 flags |= CmsResource.FLAG_LABELED; 2395 } 2396 2397 // create the new resource 2398 CmsResource newResource = new CmsResource( 2399 new CmsUUID(), 2400 source.getResourceId(), 2401 destination, 2402 source.getTypeId(), 2403 source.isFolder(), 2404 flags, 2405 dbc.currentProject().getUuid(), 2406 CmsResource.STATE_KEEP, 2407 source.getDateCreated(), // ensures current resource record remains untouched 2408 source.getUserCreated(), 2409 source.getDateLastModified(), 2410 source.getUserLastModified(), 2411 source.getDateReleased(), 2412 source.getDateExpired(), 2413 source.getSiblingCount() + 1, 2414 source.getLength(), 2415 source.getDateContent(), 2416 source.getVersion()); // version number does not matter since it will be computed later 2417 2418 // trigger "is touched" state on resource (will ensure modification date is kept unchanged) 2419 newResource.setDateLastModified(newResource.getDateLastModified()); 2420 2421 log( 2422 dbc, 2423 new CmsLogEntry( 2424 dbc, 2425 newResource.getStructureId(), 2426 CmsLogEntryType.RESOURCE_CLONED, 2427 new String[] {newResource.getRootPath()}), 2428 false); 2429 // create the resource (null content signals creation of sibling) 2430 newResource = createResource(dbc, destination, newResource, null, properties, false); 2431 2432 // copy relations 2433 copyRelations(dbc, source, newResource); 2434 2435 // clear the caches 2436 m_monitor.clearAccessControlListCache(); 2437 2438 List<CmsResource> modifiedResources = new ArrayList<CmsResource>(); 2439 modifiedResources.add(source); 2440 modifiedResources.add(newResource); 2441 modifiedResources.add(destinationFolder); 2442 Map<String, Object> eventData = new HashMap<>(); 2443 eventData.put(I_CmsEventListener.KEY_RESOURCES, modifiedResources); 2444 eventData.put(I_CmsEventListener.KEY_CHANGE, I_CmsEventListener.VALUE_CREATE_SIBLING); 2445 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, eventData)); 2446 2447 return newResource; 2448 } 2449 2450 /** 2451 * Creates the project for the temporary workplace files.<p> 2452 * 2453 * @param dbc the current database context 2454 * 2455 * @return the created project for the temporary workplace files 2456 * 2457 * @throws CmsException if something goes wrong 2458 */ 2459 public CmsProject createTempfileProject(CmsDbContext dbc) throws CmsException { 2460 2461 // read the needed groups from the cms 2462 CmsGroup projectUserGroup = readGroup(dbc, dbc.currentProject().getGroupId()); 2463 CmsGroup projectManagerGroup = readGroup(dbc, dbc.currentProject().getManagerGroupId()); 2464 2465 CmsProject tempProject = getProjectDriver(dbc).createProject( 2466 dbc, 2467 new CmsUUID(), 2468 dbc.currentUser(), 2469 projectUserGroup, 2470 projectManagerGroup, 2471 I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME, 2472 Messages.get().getBundle(dbc.getRequestContext().getLocale()).key( 2473 Messages.GUI_WORKPLACE_TEMPFILE_PROJECT_DESC_0), 2474 CmsProject.PROJECT_FLAG_HIDDEN, 2475 CmsProject.PROJECT_TYPE_NORMAL); 2476 getProjectDriver(dbc).createProjectResource(dbc, tempProject.getUuid(), "/"); 2477 2478 OpenCms.fireCmsEvent( 2479 new CmsEvent( 2480 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 2481 Collections.<String, Object> singletonMap("project", tempProject))); 2482 2483 return tempProject; 2484 } 2485 2486 /** 2487 * Creates a new user.<p> 2488 * 2489 * @param dbc the current database context 2490 * @param name the name for the new user 2491 * @param password the password for the new user 2492 * @param description the description for the new user 2493 * @param additionalInfos the additional infos for the user 2494 * 2495 * @return the created user 2496 * 2497 * @see CmsObject#createUser(String, String, String, Map) 2498 * 2499 * @throws CmsException if something goes wrong 2500 * @throws CmsIllegalArgumentException if the name for the user is not valid 2501 */ 2502 public CmsUser createUser( 2503 CmsDbContext dbc, 2504 String name, 2505 String password, 2506 String description, 2507 Map<String, Object> additionalInfos) 2508 throws CmsException, CmsIllegalArgumentException { 2509 2510 // no space before or after the name 2511 name = name.trim(); 2512 // check the user name 2513 String userName = CmsOrganizationalUnit.getSimpleName(name); 2514 OpenCms.getValidationHandler().checkUserName(userName); 2515 if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) { 2516 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName)); 2517 } 2518 // check the ou 2519 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 2520 // check the password 2521 validatePassword(password); 2522 2523 Map<String, Object> info = new HashMap<String, Object>(); 2524 if (additionalInfos != null) { 2525 info.putAll(additionalInfos); 2526 } 2527 if (description != null) { 2528 info.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description); 2529 } 2530 int flags = 0; 2531 if (ou.hasFlagWebuser()) { 2532 flags += I_CmsPrincipal.FLAG_USER_WEBUSER; 2533 } 2534 CmsUser user = getUserDriver(dbc).createUser( 2535 dbc, 2536 new CmsUUID(), 2537 name, 2538 OpenCms.getPasswordHandler().digest(password), 2539 " ", 2540 " ", 2541 " ", 2542 0, 2543 I_CmsPrincipal.FLAG_ENABLED + flags, 2544 0, 2545 info); 2546 2547 if (!dbc.getProjectId().isNullUUID()) { 2548 // user modified event is not needed 2549 return user; 2550 } 2551 // fire user modified event 2552 Map<String, Object> eventData = new HashMap<String, Object>(); 2553 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 2554 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_CREATE_USER); 2555 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 2556 return user; 2557 } 2558 2559 /** 2560 * Deletes aliases indicated by a filter.<p> 2561 * 2562 * @param dbc the current database context 2563 * @param project the current project 2564 * @param filter the filter which describes which aliases to delete 2565 * 2566 * @throws CmsException if something goes wrong 2567 */ 2568 public void deleteAliases(CmsDbContext dbc, CmsProject project, CmsAliasFilter filter) throws CmsException { 2569 2570 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 2571 vfsDriver.deleteAliases(dbc, project, filter); 2572 } 2573 2574 /** 2575 * Deletes all property values of a file or folder.<p> 2576 * 2577 * If there are no other siblings than the specified resource, 2578 * both the structure and resource property values get deleted. 2579 * If the specified resource has siblings, only the structure 2580 * property values get deleted.<p> 2581 * 2582 * @param dbc the current database context 2583 * @param resourcename the name of the resource for which all properties should be deleted 2584 * 2585 * @throws CmsException if operation was not successful 2586 */ 2587 public void deleteAllProperties(CmsDbContext dbc, String resourcename) throws CmsException { 2588 2589 CmsResource resource = null; 2590 List<CmsResource> resources = new ArrayList<CmsResource>(); 2591 2592 try { 2593 // read the resource 2594 resource = readResource(dbc, resourcename, CmsResourceFilter.IGNORE_EXPIRATION); 2595 2596 // check the security 2597 m_securityManager.checkPermissions( 2598 dbc, 2599 resource, 2600 CmsPermissionSet.ACCESS_WRITE, 2601 false, 2602 CmsResourceFilter.ALL); 2603 2604 // delete the property values 2605 if (resource.getSiblingCount() > 1) { 2606 // the resource has siblings- delete only the (structure) properties of this sibling 2607 getVfsDriver(dbc).deletePropertyObjects( 2608 dbc, 2609 dbc.currentProject().getUuid(), 2610 resource, 2611 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES); 2612 resources.addAll(readSiblings(dbc, resource, CmsResourceFilter.ALL)); 2613 2614 } else { 2615 // the resource has no other siblings- delete all (structure+resource) properties 2616 getVfsDriver(dbc).deletePropertyObjects( 2617 dbc, 2618 dbc.currentProject().getUuid(), 2619 resource, 2620 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 2621 resources.add(resource); 2622 } 2623 } finally { 2624 // clear the driver manager cache 2625 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2626 2627 // fire an event that all properties of a resource have been deleted 2628 OpenCms.fireCmsEvent( 2629 new CmsEvent( 2630 I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, 2631 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources))); 2632 } 2633 } 2634 2635 /** 2636 * Deletes all entries in the published resource table.<p> 2637 * 2638 * @param dbc the current database context 2639 * @param linkType the type of resource deleted (0= non-paramter, 1=parameter) 2640 * 2641 * @throws CmsException if something goes wrong 2642 */ 2643 public void deleteAllStaticExportPublishedResources(CmsDbContext dbc, int linkType) throws CmsException { 2644 2645 getProjectDriver(dbc).deleteAllStaticExportPublishedResources(dbc, linkType); 2646 } 2647 2648 /** 2649 * Deletes a group, where all permissions, users and children of the group 2650 * are transfered to a replacement group.<p> 2651 * 2652 * @param dbc the current request context 2653 * @param group the id of the group to be deleted 2654 * @param replacementId the id of the group to be transfered, can be <code>null</code> 2655 * 2656 * @throws CmsException if operation was not successful 2657 * @throws CmsDataAccessException if group to be deleted contains user 2658 */ 2659 public void deleteGroup(CmsDbContext dbc, CmsGroup group, CmsUUID replacementId) 2660 throws CmsDataAccessException, CmsException { 2661 2662 CmsGroup replacementGroup = null; 2663 if (replacementId != null) { 2664 replacementGroup = readGroup(dbc, replacementId); 2665 } 2666 // get all child groups of the group 2667 List<CmsGroup> children = getChildren(dbc, group, false); 2668 // get all users in this group 2669 List<CmsUser> users = getUsersOfGroup(dbc, group.getName(), true, true, group.isRole()); 2670 // get online project 2671 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 2672 if (replacementGroup == null) { 2673 // remove users 2674 Iterator<CmsUser> itUsers = users.iterator(); 2675 while (itUsers.hasNext()) { 2676 CmsUser user = itUsers.next(); 2677 if (userInGroup(dbc, user.getName(), group.getName(), group.isRole())) { 2678 removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole()); 2679 } 2680 } 2681 // transfer children to grandfather if possible 2682 CmsUUID parentId = group.getParentId(); 2683 if (parentId == null) { 2684 parentId = CmsUUID.getNullUUID(); 2685 } 2686 Iterator<CmsGroup> itChildren = children.iterator(); 2687 while (itChildren.hasNext()) { 2688 CmsGroup child = itChildren.next(); 2689 child.setParentId(parentId); 2690 writeGroup(dbc, child); 2691 } 2692 } else { 2693 // move children 2694 Iterator<CmsGroup> itChildren = children.iterator(); 2695 while (itChildren.hasNext()) { 2696 CmsGroup child = itChildren.next(); 2697 child.setParentId(replacementId); 2698 writeGroup(dbc, child); 2699 } 2700 // move users 2701 Iterator<CmsUser> itUsers = users.iterator(); 2702 while (itUsers.hasNext()) { 2703 CmsUser user = itUsers.next(); 2704 addUserToGroup(dbc, user.getName(), replacementGroup.getName(), group.isRole()); 2705 removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole()); 2706 } 2707 // transfer for offline 2708 transferPrincipalResources(dbc, dbc.currentProject(), group.getId(), replacementId, true); 2709 // transfer for online 2710 transferPrincipalResources(dbc, onlineProject, group.getId(), replacementId, true); 2711 } 2712 // remove the group 2713 getUserDriver( 2714 dbc).removeAccessControlEntriesForPrincipal(dbc, dbc.currentProject(), onlineProject, group.getId()); 2715 getUserDriver(dbc).deleteGroup(dbc, group.getName()); 2716 // backup the group 2717 getHistoryDriver(dbc).writePrincipal(dbc, group); 2718 if (OpenCms.getSubscriptionManager().isEnabled()) { 2719 // delete all subscribed resources for group 2720 unsubscribeAllResourcesFor(dbc, OpenCms.getSubscriptionManager().getPoolName(), group); 2721 } 2722 2723 // clear the relevant caches 2724 m_monitor.uncacheGroup(group); 2725 m_monitor.flushCache( 2726 CmsMemoryMonitor.CacheType.USERGROUPS, 2727 CmsMemoryMonitor.CacheType.USER_LIST, 2728 CmsMemoryMonitor.CacheType.ACL); 2729 2730 if (!dbc.getProjectId().isNullUUID()) { 2731 // group modified event is not needed 2732 return; 2733 } 2734 // fire group modified event 2735 Map<String, Object> eventData = new HashMap<String, Object>(); 2736 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 2737 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 2738 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_DELETE); 2739 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 2740 } 2741 2742 /** 2743 * Deletes the versions from the history tables, keeping the given number of versions per resource.<p> 2744 * 2745 * if the <code>cleanUp</code> option is set, additionally versions of deleted resources will be removed.<p> 2746 * 2747 * @param dbc the current database context 2748 * @param versionsToKeep number of versions to keep, is ignored if negative 2749 * @param versionsDeleted number of versions to keep for deleted resources, is ignored if negative 2750 * @param timeDeleted deleted resources older than this will also be deleted, is ignored if negative 2751 * @param clearDeletedFilter filter to evaluate whether the deleted resources should be cleared 2752 * @param report the report for output logging 2753 * 2754 * @throws CmsException if operation was not successful 2755 */ 2756 public void deleteHistoricalVersions( 2757 CmsDbContext dbc, 2758 int versionsToKeep, 2759 int versionsDeleted, 2760 long timeDeleted, 2761 Predicate<I_CmsHistoryResource> clearDeletedFilter, 2762 I_CmsReport report) 2763 throws CmsException { 2764 2765 if (clearDeletedFilter == null) { 2766 clearDeletedFilter = res -> true; 2767 } 2768 2769 report.println(Messages.get().container(Messages.RPT_START_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE); 2770 if (versionsToKeep >= 0) { 2771 report.println( 2772 Messages.get().container(Messages.RPT_START_DELETE_ACT_VERSIONS_1, Integer.valueOf(versionsToKeep)), 2773 I_CmsReport.FORMAT_HEADLINE); 2774 2775 List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllNotDeletedEntries(dbc); 2776 if (resources.isEmpty()) { 2777 report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK); 2778 } 2779 int n = resources.size(); 2780 int m = 1; 2781 Iterator<I_CmsHistoryResource> itResources = resources.iterator(); 2782 while (itResources.hasNext()) { 2783 I_CmsHistoryResource histResource = itResources.next(); 2784 2785 report.print( 2786 org.opencms.report.Messages.get().container( 2787 org.opencms.report.Messages.RPT_SUCCESSION_2, 2788 String.valueOf(m), 2789 String.valueOf(n)), 2790 I_CmsReport.FORMAT_NOTE); 2791 report.print( 2792 org.opencms.report.Messages.get().container( 2793 org.opencms.report.Messages.RPT_ARGUMENT_1, 2794 dbc.removeSiteRoot(histResource.getRootPath()))); 2795 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2796 2797 try { 2798 int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsToKeep, -1); 2799 2800 report.print( 2801 Messages.get().container(Messages.RPT_VERSION_DELETING_1, Integer.valueOf(deleted)), 2802 I_CmsReport.FORMAT_NOTE); 2803 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2804 report.println( 2805 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 2806 I_CmsReport.FORMAT_OK); 2807 } catch (CmsDataAccessException e) { 2808 report.println( 2809 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0), 2810 I_CmsReport.FORMAT_ERROR); 2811 2812 if (LOG.isDebugEnabled()) { 2813 LOG.debug(e.getLocalizedMessage(), e); 2814 } 2815 } 2816 2817 m++; 2818 } 2819 2820 report.println( 2821 Messages.get().container(Messages.RPT_END_DELETE_ACT_VERSIONS_0), 2822 I_CmsReport.FORMAT_HEADLINE); 2823 } 2824 if ((versionsDeleted >= 0) || (timeDeleted >= 0)) { 2825 if (timeDeleted >= 0) { 2826 report.println( 2827 Messages.get().container( 2828 Messages.RPT_START_DELETE_DEL_VERSIONS_2, 2829 Integer.valueOf(versionsDeleted), 2830 new Date(timeDeleted)), 2831 I_CmsReport.FORMAT_HEADLINE); 2832 } else { 2833 report.println( 2834 Messages.get().container( 2835 Messages.RPT_START_DELETE_DEL_VERSIONS_1, 2836 Integer.valueOf(versionsDeleted)), 2837 I_CmsReport.FORMAT_HEADLINE); 2838 } 2839 List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllDeletedEntries(dbc); 2840 resources = resources.stream().filter(clearDeletedFilter).collect(Collectors.toList()); 2841 if (resources.isEmpty()) { 2842 report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK); 2843 } 2844 int n = resources.size(); 2845 int m = 1; 2846 Iterator<I_CmsHistoryResource> itResources = resources.iterator(); 2847 while (itResources.hasNext()) { 2848 I_CmsHistoryResource histResource = itResources.next(); 2849 2850 report.print( 2851 org.opencms.report.Messages.get().container( 2852 org.opencms.report.Messages.RPT_SUCCESSION_2, 2853 String.valueOf(m), 2854 String.valueOf(n)), 2855 I_CmsReport.FORMAT_NOTE); 2856 report.print( 2857 org.opencms.report.Messages.get().container( 2858 org.opencms.report.Messages.RPT_ARGUMENT_1, 2859 dbc.removeSiteRoot(histResource.getRootPath()))); 2860 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2861 2862 try { 2863 int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsDeleted, timeDeleted); 2864 2865 report.print( 2866 Messages.get().container(Messages.RPT_VERSION_DELETING_1, Integer.valueOf(deleted)), 2867 I_CmsReport.FORMAT_NOTE); 2868 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2869 report.println( 2870 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 2871 I_CmsReport.FORMAT_OK); 2872 } catch (CmsDataAccessException e) { 2873 report.println( 2874 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0), 2875 I_CmsReport.FORMAT_ERROR); 2876 2877 if (LOG.isDebugEnabled()) { 2878 LOG.debug(e.getLocalizedMessage(), e); 2879 } 2880 } 2881 2882 m++; 2883 } 2884 report.println( 2885 Messages.get().container(Messages.RPT_END_DELETE_DEL_VERSIONS_0), 2886 I_CmsReport.FORMAT_HEADLINE); 2887 } 2888 report.println(Messages.get().container(Messages.RPT_END_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE); 2889 } 2890 2891 /** 2892 * Deletes all log entries matching the given filter.<p> 2893 * 2894 * @param dbc the current db context 2895 * @param filter the filter to use for deletion 2896 * 2897 * @throws CmsException if something goes wrong 2898 * 2899 * @see CmsSecurityManager#deleteLogEntries(CmsRequestContext, CmsLogFilter) 2900 */ 2901 public void deleteLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException { 2902 2903 updateLog(dbc); 2904 m_projectDriver.deleteLog(dbc, filter); 2905 } 2906 2907 /** 2908 * Deletes an organizational unit.<p> 2909 * 2910 * Only organizational units that contain no suborganizational unit can be deleted.<p> 2911 * 2912 * The organizational unit can not be delete if it is used in the request context, 2913 * or if the current user belongs to it.<p> 2914 * 2915 * All users and groups in the given organizational unit will be deleted.<p> 2916 * 2917 * @param dbc the current db context 2918 * @param organizationalUnit the organizational unit to delete 2919 * 2920 * @throws CmsException if operation was not successful 2921 * 2922 * @see org.opencms.security.CmsOrgUnitManager#deleteOrganizationalUnit(CmsObject, String) 2923 */ 2924 public void deleteOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) 2925 throws CmsException { 2926 2927 // check organizational unit in context 2928 if (dbc.getRequestContext().getOuFqn().equals(organizationalUnit.getName())) { 2929 throw new CmsDbConsistencyException( 2930 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_IN_CONTEXT_1, organizationalUnit.getName())); 2931 } 2932 // check organizational unit for user 2933 if (dbc.currentUser().getOuFqn().equals(organizationalUnit.getName())) { 2934 throw new CmsDbConsistencyException( 2935 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_CURRENT_USER_1, organizationalUnit.getName())); 2936 } 2937 // check sub organizational units 2938 if (!getOrganizationalUnits(dbc, organizationalUnit, true).isEmpty()) { 2939 throw new CmsDbConsistencyException( 2940 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_SUB_ORGUNITS_1, organizationalUnit.getName())); 2941 } 2942 // check groups 2943 List<CmsGroup> groups = getGroups(dbc, organizationalUnit, true, false); 2944 Iterator<CmsGroup> itGroups = groups.iterator(); 2945 while (itGroups.hasNext()) { 2946 CmsGroup group = itGroups.next(); 2947 if (!OpenCms.getDefaultUsers().isDefaultGroup(group.getName())) { 2948 throw new CmsDbConsistencyException( 2949 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_GROUPS_1, organizationalUnit.getName())); 2950 } 2951 } 2952 // check users 2953 if (!getUsers(dbc, organizationalUnit, true).isEmpty()) { 2954 throw new CmsDbConsistencyException( 2955 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_USERS_1, organizationalUnit.getName())); 2956 } 2957 2958 // delete default groups if needed 2959 itGroups = groups.iterator(); 2960 while (itGroups.hasNext()) { 2961 CmsGroup group = itGroups.next(); 2962 deleteGroup(dbc, group, null); 2963 } 2964 2965 // delete projects 2966 Iterator<CmsProject> itProjects = getProjectDriver(dbc).readProjects( 2967 dbc, 2968 organizationalUnit.getName()).iterator(); 2969 while (itProjects.hasNext()) { 2970 CmsProject project = itProjects.next(); 2971 deleteProject(dbc, project, false); 2972 } 2973 2974 // delete roles 2975 Iterator<CmsGroup> itRoles = getGroups(dbc, organizationalUnit, true, true).iterator(); 2976 while (itRoles.hasNext()) { 2977 CmsGroup role = itRoles.next(); 2978 deleteGroup(dbc, role, null); 2979 } 2980 2981 // create a publish list for the 'virtual' publish event 2982 CmsResource resource = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT); 2983 CmsPublishList pl = new CmsPublishList(resource, false); 2984 pl.add(resource, false); 2985 2986 // remove the organizational unit itself 2987 getUserDriver(dbc).deleteOrganizationalUnit(dbc, organizationalUnit); 2988 2989 // write the publish history entry 2990 getProjectDriver(dbc).writePublishHistory( 2991 dbc, 2992 pl.getPublishHistoryId(), 2993 new CmsPublishedResource(resource, -1, CmsResourceState.STATE_DELETED)); 2994 2995 // flush relevant caches 2996 m_monitor.clearPrincipalsCache(); 2997 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2998 2999 // fire the 'virtual' publish event 3000 Map<String, Object> eventData = new HashMap<String, Object>(); 3001 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 3002 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 3003 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 3004 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 3005 OpenCms.fireCmsEvent(afterPublishEvent); 3006 3007 m_lockManager.removeDeletedResource(dbc, resource.getRootPath()); 3008 3009 if (!dbc.getProjectId().isNullUUID()) { 3010 // OU modified event is not needed 3011 return; 3012 } 3013 // fire OU modified event 3014 Map<String, Object> event2Data = new HashMap<String, Object>(); 3015 event2Data.put(I_CmsEventListener.KEY_OU_NAME, organizationalUnit.getName()); 3016 event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_DELETE); 3017 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data)); 3018 3019 } 3020 3021 /** 3022 * Deletes a project.<p> 3023 * 3024 * Only the admin or the owner of the project can do this. 3025 * 3026 * @param dbc the current database context 3027 * @param deleteProject the project to be deleted 3028 * 3029 * @throws CmsException if something goes wrong 3030 */ 3031 public void deleteProject(CmsDbContext dbc, CmsProject deleteProject) throws CmsException { 3032 3033 deleteProject(dbc, deleteProject, true); 3034 } 3035 3036 /** 3037 * Deletes a project.<p> 3038 * 3039 * Only the admin or the owner of the project can do this. 3040 * 3041 * @param dbc the current database context 3042 * @param deleteProject the project to be deleted 3043 * @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 3044 * 3045 * @throws CmsException if something goes wrong 3046 */ 3047 public void deleteProject(CmsDbContext dbc, CmsProject deleteProject, boolean resetResources) throws CmsException { 3048 3049 CmsUUID projectId = deleteProject.getUuid(); 3050 3051 if (resetResources) { 3052 // changed/new/deleted files in the specified project 3053 List<CmsResource> modifiedFiles = readChangedResourcesInsideProject(dbc, projectId, RCPRM_FILES_ONLY_MODE); 3054 // changed/new/deleted folders in the specified project 3055 List<CmsResource> modifiedFolders = readChangedResourcesInsideProject( 3056 dbc, 3057 projectId, 3058 RCPRM_FOLDERS_ONLY_MODE); 3059 resetResourcesInProject(dbc, projectId, modifiedFiles, modifiedFolders); 3060 } 3061 3062 // unlock all resources in the project 3063 m_lockManager.removeResourcesInProject(deleteProject.getUuid(), true); 3064 m_monitor.clearAccessControlListCache(); 3065 m_monitor.clearResourceCache(); 3066 3067 // set project to online project if current project is the one which will be deleted 3068 if (projectId.equals(dbc.currentProject().getUuid())) { 3069 dbc.getRequestContext().setCurrentProject(readProject(dbc, CmsProject.ONLINE_PROJECT_ID)); 3070 } 3071 3072 // delete the project itself 3073 getProjectDriver(dbc).deleteProject(dbc, deleteProject); 3074 m_monitor.uncacheProject(deleteProject); 3075 3076 // fire the corresponding event 3077 OpenCms.fireCmsEvent( 3078 new CmsEvent( 3079 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 3080 Collections.<String, Object> singletonMap("project", deleteProject))); 3081 3082 } 3083 3084 /** 3085 * Deletes a property definition.<p> 3086 * 3087 * @param dbc the current database context 3088 * @param name the name of the property definition to delete 3089 * 3090 * @throws CmsException if something goes wrong 3091 */ 3092 public void deletePropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 3093 3094 CmsPropertyDefinition propertyDefinition = null; 3095 3096 try { 3097 // first read and then delete the metadefinition. 3098 propertyDefinition = readPropertyDefinition(dbc, name); 3099 getVfsDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition); 3100 getHistoryDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition); 3101 } finally { 3102 3103 // fire an event that a property of a resource has been deleted 3104 OpenCms.fireCmsEvent( 3105 new CmsEvent( 3106 I_CmsEventListener.EVENT_PROPERTY_DEFINITION_MODIFIED, 3107 Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition))); 3108 } 3109 } 3110 3111 /** 3112 * Deletes a publish job identified by its history id.<p> 3113 * 3114 * @param dbc the current database context 3115 * @param publishHistoryId the history id identifying the publish job 3116 * 3117 * @throws CmsException if something goes wrong 3118 */ 3119 public void deletePublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 3120 3121 getProjectDriver(dbc).deletePublishJob(dbc, publishHistoryId); 3122 } 3123 3124 /** 3125 * Deletes the publish list assigned to a publish job.<p> 3126 * 3127 * @param dbc the current database context 3128 * @param publishHistoryId the history id identifying the publish job 3129 * @throws CmsException if something goes wrong 3130 */ 3131 public void deletePublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 3132 3133 getProjectDriver(dbc).deletePublishList(dbc, publishHistoryId); 3134 } 3135 3136 /** 3137 * Deletes all relations for the given resource matching the given filter.<p> 3138 * 3139 * @param dbc the current db context 3140 * @param resource the resource to delete the relations for 3141 * @param filter the filter to use for deletion 3142 * 3143 * @throws CmsException if something goes wrong 3144 * 3145 * @see CmsSecurityManager#deleteRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter) 3146 */ 3147 public void deleteRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter) 3148 throws CmsException { 3149 3150 if (filter.includesDefinedInContent()) { 3151 throw new CmsIllegalArgumentException( 3152 Messages.get().container( 3153 Messages.ERR_DELETE_RELATION_IN_CONTENT_2, 3154 dbc.removeSiteRoot(resource.getRootPath()), 3155 filter.getTypes())); 3156 } 3157 getVfsDriver(dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), resource, filter); 3158 setDateLastModified(dbc, resource, System.currentTimeMillis()); 3159 log( 3160 dbc, 3161 new CmsLogEntry( 3162 dbc, 3163 resource.getStructureId(), 3164 CmsLogEntryType.RESOURCE_REMOVE_RELATION, 3165 new String[] {resource.getRootPath(), filter.toString()}), 3166 false); 3167 } 3168 3169 /** 3170 * Deletes a resource.<p> 3171 * 3172 * The <code>siblingMode</code> parameter controls how to handle siblings 3173 * during the delete operation. 3174 * Possible values for this parameter are: 3175 * <ul> 3176 * <li><code>{@link CmsResource#DELETE_REMOVE_SIBLINGS}</code></li> 3177 * <li><code>{@link CmsResource#DELETE_PRESERVE_SIBLINGS}</code></li> 3178 * </ul><p> 3179 * 3180 * @param dbc the current database context 3181 * @param resource the name of the resource to delete (full path) 3182 * @param siblingMode indicates how to handle siblings of the deleted resource 3183 * 3184 * @throws CmsException if something goes wrong 3185 * 3186 * @see CmsObject#deleteResource(String, CmsResource.CmsResourceDeleteMode) 3187 * @see I_CmsResourceType#deleteResource(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode) 3188 */ 3189 public void deleteResource(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceDeleteMode siblingMode) 3190 throws CmsException { 3191 3192 // upgrade a potential inherited, non-shared lock into a common lock 3193 CmsLock currentLock = getLock(dbc, resource); 3194 if (currentLock.getEditionLock().isDirectlyInherited()) { 3195 // upgrade the lock status if required 3196 lockResource(dbc, resource, CmsLockType.EXCLUSIVE); 3197 } 3198 3199 // check if siblings of the resource exist and must be deleted as well 3200 if (resource.isFolder()) { 3201 // folder can have no siblings 3202 siblingMode = CmsResource.DELETE_PRESERVE_SIBLINGS; 3203 } 3204 3205 // if selected, add all siblings of this resource to the list of resources to be deleted 3206 boolean allSiblingsRemoved; 3207 List<CmsResource> resources; 3208 if (siblingMode == CmsResource.DELETE_REMOVE_SIBLINGS) { 3209 resources = new ArrayList<CmsResource>(readSiblings(dbc, resource, CmsResourceFilter.ALL)); 3210 allSiblingsRemoved = true; 3211 3212 // ensure that the resource requested to be deleted is the last resource that gets actually deleted 3213 // to keep the shared locks of the siblings while those get deleted. 3214 resources.remove(resource); 3215 resources.add(resource); 3216 } else { 3217 // only delete the resource, no siblings 3218 resources = Collections.singletonList(resource); 3219 allSiblingsRemoved = false; 3220 } 3221 3222 int size = resources.size(); 3223 // if we have only one resource no further check is required 3224 if (size > 1) { 3225 CmsMultiException me = new CmsMultiException(); 3226 // ensure that each sibling is unlocked or locked by the current user 3227 for (int i = 0; i < size; i++) { 3228 CmsResource currentResource = resources.get(i); 3229 currentLock = getLock(dbc, currentResource); 3230 if (!currentLock.getEditionLock().isUnlocked() && !currentLock.isOwnedBy(dbc.currentUser())) { 3231 // the resource is locked by a user different from the current user 3232 CmsRequestContext context = dbc.getRequestContext(); 3233 me.addException( 3234 new CmsLockException( 3235 org.opencms.lock.Messages.get().container( 3236 org.opencms.lock.Messages.ERR_SIBLING_LOCKED_2, 3237 context.getSitePath(currentResource), 3238 context.getSitePath(resource)))); 3239 } 3240 } 3241 if (!me.getExceptions().isEmpty()) { 3242 throw me; 3243 } 3244 } 3245 3246 boolean removeAce = true; 3247 3248 if (resource.isFolder()) { 3249 // check if the folder has any resources in it 3250 Iterator<CmsResource> childResources = getVfsDriver( 3251 dbc).readChildResources(dbc, dbc.currentProject(), resource, true, true).iterator(); 3252 3253 CmsUUID projectId = CmsProject.ONLINE_PROJECT_ID; 3254 if (dbc.currentProject().isOnlineProject()) { 3255 projectId = CmsUUID.getOpenCmsUUID(); // HACK: to get an offline project id 3256 } 3257 3258 // collect the names of the resources inside the folder, excluding the moved resources 3259 StringBuffer errorResNames = new StringBuffer(128); 3260 while (childResources.hasNext()) { 3261 CmsResource errorRes = childResources.next(); 3262 if (errorRes.getState().isDeleted()) { 3263 continue; 3264 } 3265 // if deleting offline, or not moved, or just renamed inside the deleted folder 3266 // so, it may remain some orphan online entries for moved resources 3267 // which will be fixed during the publishing of the moved resources 3268 boolean error = !dbc.currentProject().isOnlineProject(); 3269 if (!error) { 3270 try { 3271 String originalPath = getVfsDriver( 3272 dbc).readResource(dbc, projectId, errorRes.getRootPath(), true).getRootPath(); 3273 error = originalPath.equals(errorRes.getRootPath()) 3274 || originalPath.startsWith(resource.getRootPath()); 3275 } catch (CmsVfsResourceNotFoundException e) { 3276 // ignore 3277 } 3278 } 3279 if (error) { 3280 if (errorResNames.length() != 0) { 3281 errorResNames.append(", "); 3282 } 3283 errorResNames.append("[" + dbc.removeSiteRoot(errorRes.getRootPath()) + "]"); 3284 } 3285 } 3286 3287 // the current implementation only deletes empty folders 3288 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(errorResNames.toString())) { 3289 throw new CmsVfsException( 3290 org.opencms.db.generic.Messages.get().container( 3291 org.opencms.db.generic.Messages.ERR_DELETE_NONEMTY_FOLDER_2, 3292 dbc.removeSiteRoot(resource.getRootPath()), 3293 errorResNames.toString())); 3294 } 3295 } 3296 3297 // delete all collected resources 3298 for (int i = 0; i < size; i++) { 3299 CmsResource currentResource = resources.get(i); 3300 3301 // try to delete/remove the resource only if the user has write access to the resource 3302 // check permissions only for the sibling, the resource it self was already checked or 3303 // is to be removed without write permissions, ie. while deleting a folder 3304 if (!currentResource.equals(resource) 3305 && (I_CmsPermissionHandler.PERM_ALLOWED != m_securityManager.hasPermissions( 3306 dbc, 3307 currentResource, 3308 CmsPermissionSet.ACCESS_WRITE, 3309 LockCheck.yes, 3310 CmsResourceFilter.ALL))) { 3311 3312 // no write access to sibling - must keep ACE (see below) 3313 allSiblingsRemoved = false; 3314 } else { 3315 // write access to sibling granted 3316 boolean existsOnline = (getVfsDriver(dbc).validateStructureIdExists( 3317 dbc, 3318 CmsProject.ONLINE_PROJECT_ID, 3319 currentResource.getStructureId()) || !(currentResource.getState().equals(CmsResource.STATE_NEW))); 3320 if (!existsOnline) { 3321 // the resource does not exist online => remove the resource 3322 // this means the resource is "new" (blue) in the offline project 3323 3324 // delete all properties of this resource 3325 deleteAllProperties(dbc, currentResource.getRootPath()); 3326 3327 if (currentResource.isFolder()) { 3328 getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentResource); 3329 } else { 3330 // check labels 3331 if (currentResource.isLabeled() && !labelResource(dbc, currentResource, null, 2)) { 3332 // update the resource flags to "un label" the other siblings 3333 int flags = currentResource.getFlags(); 3334 flags &= ~CmsResource.FLAG_LABELED; 3335 currentResource.setFlags(flags); 3336 } 3337 getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentResource); 3338 } 3339 3340 // ensure an exclusive lock is removed in the lock manager for a deleted new resource, 3341 // otherwise it would "stick" in the lock manager, preventing other users from creating 3342 // a file with the same name (issue with temp files in editor) 3343 m_lockManager.removeDeletedResource(dbc, currentResource.getRootPath()); 3344 // delete relations 3345 getVfsDriver(dbc).deleteRelations( 3346 dbc, 3347 dbc.currentProject().getUuid(), 3348 currentResource, 3349 CmsRelationFilter.TARGETS); 3350 getVfsDriver(dbc).deleteUrlNameMappingEntries( 3351 dbc, 3352 false, 3353 CmsUrlNameMappingFilter.ALL.filterStructureId(currentResource.getStructureId())); 3354 getVfsDriver(dbc).deleteAliases( 3355 dbc, 3356 dbc.currentProject(), 3357 new CmsAliasFilter(null, null, currentResource.getStructureId())); 3358 log( 3359 dbc, 3360 new CmsLogEntry( 3361 dbc, 3362 currentResource.getStructureId(), 3363 CmsLogEntryType.RESOURCE_NEW_DELETED, 3364 new String[] {currentResource.getRootPath()}), 3365 true); 3366 } else { 3367 // the resource exists online => mark the resource as deleted 3368 // structure record is removed during next publish 3369 // if one (or more) siblings are not removed, the ACE can not be removed 3370 removeAce = false; 3371 // set resource state to deleted 3372 currentResource.setState(CmsResource.STATE_DELETED); 3373 getVfsDriver( 3374 dbc).writeResourceState(dbc, dbc.currentProject(), currentResource, UPDATE_STRUCTURE, false); 3375 3376 // update the project ID 3377 getVfsDriver(dbc).writeLastModifiedProjectId( 3378 dbc, 3379 dbc.currentProject(), 3380 dbc.currentProject().getUuid(), 3381 currentResource); 3382 // log it 3383 3384 log( 3385 dbc, 3386 new CmsLogEntry( 3387 dbc, 3388 currentResource.getStructureId(), 3389 CmsLogEntryType.RESOURCE_DELETED, 3390 new String[] {currentResource.getRootPath()}), 3391 true); 3392 } 3393 } 3394 } 3395 3396 if ((resource.getSiblingCount() <= 1) || allSiblingsRemoved) { 3397 if (removeAce) { 3398 // remove the access control entries 3399 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId()); 3400 } 3401 } 3402 3403 // flush all caches 3404 m_monitor.clearAccessControlListCache(); 3405 m_monitor.flushCache( 3406 CmsMemoryMonitor.CacheType.PROPERTY, 3407 CmsMemoryMonitor.CacheType.PROPERTY_LIST, 3408 CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 3409 3410 Map<String, Object> eventData = new HashMap<String, Object>(); 3411 eventData.put(I_CmsEventListener.KEY_RESOURCES, resources); 3412 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 3413 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_DELETED, eventData)); 3414 } 3415 3416 /** 3417 * Deletes an entry in the published resource table.<p> 3418 * 3419 * @param dbc the current database context 3420 * @param resourceName The name of the resource to be deleted in the static export 3421 * @param linkType the type of resource deleted (0= non-parameter, 1=parameter) 3422 * @param linkParameter the parameters of the resource 3423 * 3424 * @throws CmsException if something goes wrong 3425 */ 3426 public void deleteStaticExportPublishedResource( 3427 CmsDbContext dbc, 3428 String resourceName, 3429 int linkType, 3430 String linkParameter) 3431 throws CmsException { 3432 3433 getProjectDriver(dbc).deleteStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter); 3434 } 3435 3436 /** 3437 * Deletes a user, where all permissions and resources attributes of the user 3438 * were transfered to a replacement user, if given.<p> 3439 * 3440 * Only users, which are in the group "administrators" are granted.<p> 3441 * 3442 * @param dbc the current database context 3443 * @param project the current project 3444 * @param username the name of the user to be deleted 3445 * @param replacementUsername the name of the user to be transfered, can be <code>null</code> 3446 * 3447 * @throws CmsException if operation was not successful 3448 */ 3449 public void deleteUser(CmsDbContext dbc, CmsProject project, String username, String replacementUsername) 3450 throws CmsException { 3451 3452 // Test if the users exists 3453 CmsUser user = readUser(dbc, username); 3454 CmsUser replacementUser = null; 3455 if (replacementUsername != null) { 3456 replacementUser = readUser(dbc, replacementUsername); 3457 } 3458 3459 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 3460 boolean withACEs = true; 3461 if (replacementUser == null) { 3462 withACEs = false; 3463 replacementUser = readUser(dbc, OpenCms.getDefaultUsers().getUserDeletedResource()); 3464 } 3465 3466 boolean isVfsManager = m_securityManager.hasRole(dbc, replacementUser, CmsRole.VFS_MANAGER); 3467 3468 // iterate groups and roles 3469 for (int i = 0; i < 2; i++) { 3470 boolean readRoles = i != 0; 3471 Iterator<CmsGroup> itGroups = getGroupsOfUser( 3472 dbc, 3473 username, 3474 "", 3475 true, 3476 readRoles, 3477 true, 3478 dbc.getRequestContext().getRemoteAddress()).iterator(); 3479 while (itGroups.hasNext()) { 3480 CmsGroup group = itGroups.next(); 3481 if (!isVfsManager) { 3482 // add replacement user to user groups 3483 if (!userInGroup(dbc, replacementUser.getName(), group.getName(), readRoles)) { 3484 addUserToGroup(dbc, replacementUser.getName(), group.getName(), readRoles); 3485 } 3486 } 3487 // remove user from groups 3488 if (userInGroup(dbc, username, group.getName(), readRoles)) { 3489 // we need this additional check because removing a user from a group 3490 // may also automatically remove him from other groups if the group was 3491 // associated with a role. 3492 removeUserFromGroup(dbc, username, group.getName(), readRoles); 3493 } 3494 } 3495 } 3496 // remove all locks set for the deleted user 3497 m_lockManager.removeLocks(user.getId()); 3498 3499 boolean skipTransfer = false; 3500 try { 3501 3502 // Looking up the resources for the principal transfer is pretty expensive, 3503 // but for webuser OUs without associated OU resources, it should be unnecessary. 3504 // So we can save ourselves the work. 3505 // We cache this "skippable" status on a per-OU basis, so we don't need to a do it for each 3506 // user if we delete a lot of users in a row. 3507 // 3508 // Note: Even if there *were* resources which would be affected by transferPrincipalResources, 3509 // skipping the transfer doesn't do any harm - the UI just displays the ID instead. 3510 3511 skipTransfer = m_skipTransferPrincipalResourceCache.get(user.getOuFqn(), () -> { 3512 3513 String ouName = CmsOrganizationalUnit.removeLeadingSeparator(user.getOuFqn()); 3514 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouName); 3515 if (!ou.hasFlagWebuser()) { 3516 return false; 3517 } 3518 List<CmsResource> ouResources = getResourcesForOrganizationalUnit(dbc, ou); 3519 return ouResources.size() == 0; 3520 }); 3521 } catch (ExecutionException e) { 3522 LOG.error(e.getLocalizedMessage(), e); 3523 } 3524 if (skipTransfer) { 3525 LOG.info( 3526 "Skipping principal transfer while deleting user " 3527 + user.getName() 3528 + " (" 3529 + user.getId() 3530 + ") because it is part of a webuser OU with no OU resources."); 3531 } else { 3532 // offline 3533 if (dbc.getProjectId().isNullUUID()) { 3534 // offline project available 3535 transferPrincipalResources(dbc, project, user.getId(), replacementUser.getId(), withACEs); 3536 } 3537 // online 3538 transferPrincipalResources(dbc, onlineProject, user.getId(), replacementUser.getId(), withACEs); 3539 } 3540 getUserDriver(dbc).removeAccessControlEntriesForPrincipal(dbc, project, onlineProject, user.getId()); 3541 getHistoryDriver(dbc).writePrincipal(dbc, user); 3542 getUserDriver(dbc).deleteUser(dbc, username); 3543 // delete user from cache 3544 m_monitor.clearUserCache(user); 3545 3546 if (!dbc.getProjectId().isNullUUID()) { 3547 // user modified event is not needed 3548 return; 3549 } 3550 // fire user modified event 3551 Map<String, Object> eventData = new HashMap<String, Object>(); 3552 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 3553 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 3554 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_DELETE_USER); 3555 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 3556 } 3557 3558 /** 3559 * Destroys this driver manager and releases all allocated resources.<p> 3560 */ 3561 public void destroy() { 3562 3563 try { 3564 if (m_projectDriver != null) { 3565 try { 3566 m_projectDriver.destroy(); 3567 } catch (Throwable t) { 3568 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_PROJECT_DRIVER_0), t); 3569 } 3570 m_projectDriver = null; 3571 } 3572 if (m_userDriver != null) { 3573 try { 3574 m_userDriver.destroy(); 3575 } catch (Throwable t) { 3576 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_USER_DRIVER_0), t); 3577 } 3578 m_userDriver = null; 3579 } 3580 if (m_vfsDriver != null) { 3581 try { 3582 m_vfsDriver.destroy(); 3583 } catch (Throwable t) { 3584 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_VFS_DRIVER_0), t); 3585 } 3586 m_vfsDriver = null; 3587 } 3588 if (m_historyDriver != null) { 3589 try { 3590 m_historyDriver.destroy(); 3591 } catch (Throwable t) { 3592 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_HISTORY_DRIVER_0), t); 3593 } 3594 m_historyDriver = null; 3595 } 3596 3597 if (m_pools != null) { 3598 for (CmsDbPoolV11 pool : m_pools.values()) { 3599 try { 3600 pool.close(); 3601 if (CmsLog.INIT.isDebugEnabled()) { 3602 CmsLog.INIT.debug(Messages.get().getBundle().key(Messages.INIT_CLOSE_CONN_POOL_1, pool)); 3603 } 3604 3605 } catch (Throwable t) { 3606 LOG.error(Messages.get().getBundle().key(Messages.LOG_CLOSE_CONN_POOL_ERROR_1, pool), t); 3607 } 3608 } 3609 m_pools.clear(); 3610 } 3611 3612 m_monitor.clearCache(); 3613 3614 m_lockManager = null; 3615 m_htmlLinkValidator = null; 3616 } catch (Throwable t) { 3617 // ignore 3618 } 3619 if (CmsLog.INIT.isInfoEnabled()) { 3620 CmsLog.INIT.info( 3621 Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_DESTROY_1, getClass().getName())); 3622 } 3623 } 3624 3625 /** 3626 * Tests if a resource with the given resourceId does already exist in the Database.<p> 3627 * 3628 * @param dbc the current database context 3629 * @param resourceId the resource id to test for 3630 * @return true if a resource with the given id was found, false otherweise 3631 * @throws CmsException if something goes wrong 3632 */ 3633 public boolean existsResourceId(CmsDbContext dbc, CmsUUID resourceId) throws CmsException { 3634 3635 return getVfsDriver(dbc).validateResourceIdExists(dbc, dbc.currentProject().getUuid(), resourceId); 3636 } 3637 3638 /** 3639 * Fills the given publish list with the the VFS resources that actually get published.<p> 3640 * 3641 * Please refer to the source code of this method for the rules on how to decide whether a 3642 * new/changed/deleted <code>{@link CmsResource}</code> object can be published or not.<p> 3643 * 3644 * @param dbc the current database context 3645 * @param publishList must be initialized with basic publish information (Project or direct publish operation), 3646 * the given publish list will be filled with all new/changed/deleted files from the current 3647 * (offline) project that will be actually published 3648 * 3649 * @throws CmsException if something goes wrong 3650 * 3651 * @see org.opencms.db.CmsPublishList 3652 */ 3653 public void fillPublishList(CmsDbContext dbc, CmsPublishList publishList) throws CmsException { 3654 3655 if (!publishList.isDirectPublish()) { 3656 // when publishing a project 3657 // all modified resources with the last change done in the current project are candidates if unlocked 3658 List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree( 3659 dbc, 3660 dbc.currentProject().getUuid(), 3661 CmsDriverManager.READ_IGNORE_PARENT, 3662 CmsDriverManager.READ_IGNORE_TYPE, 3663 CmsResource.STATE_UNCHANGED, 3664 CmsDriverManager.READ_IGNORE_TIME, 3665 CmsDriverManager.READ_IGNORE_TIME, 3666 CmsDriverManager.READ_IGNORE_TIME, 3667 CmsDriverManager.READ_IGNORE_TIME, 3668 CmsDriverManager.READ_IGNORE_TIME, 3669 CmsDriverManager.READ_IGNORE_TIME, 3670 CmsDriverManager.READMODE_INCLUDE_TREE 3671 | CmsDriverManager.READMODE_INCLUDE_PROJECT 3672 | CmsDriverManager.READMODE_EXCLUDE_STATE 3673 | CmsDriverManager.READMODE_ONLY_FOLDERS); 3674 3675 List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree( 3676 dbc, 3677 dbc.currentProject().getUuid(), 3678 CmsDriverManager.READ_IGNORE_PARENT, 3679 CmsDriverManager.READ_IGNORE_TYPE, 3680 CmsResource.STATE_UNCHANGED, 3681 CmsDriverManager.READ_IGNORE_TIME, 3682 CmsDriverManager.READ_IGNORE_TIME, 3683 CmsDriverManager.READ_IGNORE_TIME, 3684 CmsDriverManager.READ_IGNORE_TIME, 3685 CmsDriverManager.READ_IGNORE_TIME, 3686 CmsDriverManager.READ_IGNORE_TIME, 3687 CmsDriverManager.READMODE_INCLUDE_TREE 3688 | CmsDriverManager.READMODE_INCLUDE_PROJECT 3689 | CmsDriverManager.READMODE_EXCLUDE_STATE 3690 | CmsDriverManager.READMODE_ONLY_FILES); 3691 CmsRequestContext context = dbc.getRequestContext(); 3692 if ((context != null) 3693 && (context.getAttribute(CmsDefaultWorkflowManager.ATTR_CHECK_PUBLISH_RESOURCE_LIMIT) != null)) { 3694 3695 // check if total size and if it exceeds the resource limit and the request 3696 // context attribute is set, throw an exception. 3697 // we do it here since filterResources() can be very expensive on large resource lists 3698 3699 int limit = OpenCms.getWorkflowManager().getResourceLimit(); 3700 int total = fileList.size() + folderList.size(); 3701 if (total > limit) { 3702 throw new CmsTooManyPublishResourcesException(total); 3703 } 3704 } 3705 publishList.addAll(filterResources(dbc, null, folderList), true); 3706 publishList.addAll(filterResources(dbc, publishList, fileList), true); 3707 } else { 3708 // this is a direct publish 3709 Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator(); 3710 while (it.hasNext()) { 3711 // iterate all resources in the direct publish list 3712 CmsResource directPublishResource = it.next(); 3713 if (directPublishResource.isFolder()) { 3714 // when publishing a folder directly, 3715 // the folder and all modified resources within the tree below this folder 3716 // and with the last change done in the current project are candidates if lockable 3717 CmsLock lock = getLock(dbc, directPublishResource); 3718 if (!directPublishResource.getState().isUnchanged() && lock.isLockableBy(dbc.currentUser())) { 3719 3720 try { 3721 m_securityManager.checkPermissions( 3722 dbc, 3723 directPublishResource, 3724 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 3725 false, 3726 CmsResourceFilter.ALL); 3727 publishList.add(directPublishResource, true); 3728 } catch (CmsException e) { 3729 // skip if not enough permissions 3730 } 3731 } 3732 boolean shouldPublishDeletedSubResources = publishList.isUserPublishList() 3733 && directPublishResource.getState().isDeleted(); 3734 if (publishList.isPublishSubResources() || shouldPublishDeletedSubResources) { 3735 addSubResources(dbc, publishList, directPublishResource, resource -> true); 3736 } 3737 } else if (directPublishResource.isFile() && !directPublishResource.getState().isUnchanged()) { 3738 3739 // when publishing a file directly this file is the only candidate 3740 // if it is modified and lockable 3741 CmsLock lock = getLock(dbc, directPublishResource); 3742 if (lock.isLockableBy(dbc.currentUser())) { 3743 // check permissions 3744 try { 3745 m_securityManager.checkPermissions( 3746 dbc, 3747 directPublishResource, 3748 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 3749 false, 3750 CmsResourceFilter.ALL); 3751 publishList.add(directPublishResource, true); 3752 } catch (CmsException e) { 3753 // skip if not enough permissions 3754 } 3755 } 3756 } 3757 } 3758 } 3759 3760 // Step 2: if desired, extend the list of files to publish with related siblings 3761 if (publishList.isPublishSiblings()) { 3762 List<CmsResource> publishFiles = publishList.getFileList(); 3763 int size = publishFiles.size(); 3764 3765 // Improved: first calculate closure of all siblings, then filter and add them 3766 Set<CmsResource> siblingsClosure = new HashSet<CmsResource>(publishFiles); 3767 for (int i = 0; i < size; i++) { 3768 CmsResource currentFile = publishFiles.get(i); 3769 if (currentFile.getSiblingCount() > 1) { 3770 siblingsClosure.addAll(readSiblings(dbc, currentFile, CmsResourceFilter.ALL_MODIFIED)); 3771 } 3772 } 3773 publishList.addAll(filterSiblings(dbc, publishList, siblingsClosure), true); 3774 } 3775 publishList.initialize(); 3776 } 3777 3778 /** 3779 * Returns the list of access control entries of a resource given its name.<p> 3780 * 3781 * @param dbc the current database context 3782 * @param resource the resource to read the access control entries for 3783 * @param getInherited true if the result should include all access control entries inherited by parent folders 3784 * 3785 * @return a list of <code>{@link CmsAccessControlEntry}</code> objects defining all permissions for the given resource 3786 * 3787 * @throws CmsException if something goes wrong 3788 */ 3789 public List<CmsAccessControlEntry> getAccessControlEntries( 3790 CmsDbContext dbc, 3791 CmsResource resource, 3792 boolean getInherited) 3793 throws CmsException { 3794 3795 // get the ACE of the resource itself 3796 I_CmsUserDriver userDriver = getUserDriver(dbc); 3797 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 3798 List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries( 3799 dbc, 3800 dbc.currentProject(), 3801 resource.getResourceId(), 3802 false); 3803 3804 // sort and check if we got the 'overwrite all' ace to stop looking up 3805 boolean overwriteAll = sortAceList(ace); 3806 3807 // get the ACE of each parent folder 3808 // Note: for the immediate parent, get non-inherited access control entries too, 3809 // if the resource is not a folder 3810 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 3811 int d = (resource.isFolder()) ? 1 : 0; 3812 3813 while (!overwriteAll && getInherited && (parentPath != null)) { 3814 resource = vfsDriver.readFolder(dbc, dbc.currentProject().getUuid(), parentPath); 3815 List<CmsAccessControlEntry> entries = userDriver.readAccessControlEntries( 3816 dbc, 3817 dbc.currentProject(), 3818 resource.getResourceId(), 3819 d > 0); 3820 3821 // sort and check if we got the 'overwrite all' ace to stop looking up 3822 overwriteAll = sortAceList(entries); 3823 3824 for (CmsAccessControlEntry e : entries) { 3825 e.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED); 3826 } 3827 3828 ace.addAll(entries); 3829 parentPath = CmsResource.getParentFolder(resource.getRootPath()); 3830 d++; 3831 } 3832 3833 return ace; 3834 } 3835 3836 /** 3837 * Returns the full access control list of a given resource.<p> 3838 * 3839 * @param dbc the current database context 3840 * @param resource the resource 3841 * 3842 * @return the access control list of the resource 3843 * 3844 * @throws CmsException if something goes wrong 3845 */ 3846 public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource) throws CmsException { 3847 3848 return getAccessControlList(dbc, resource, false); 3849 } 3850 3851 /** 3852 * Returns the access control list of a given resource.<p> 3853 * 3854 * If <code>inheritedOnly</code> is set, only inherited access control entries 3855 * are returned.<p> 3856 * 3857 * Note: For file resources, *all* permissions set at the immediate parent folder are inherited, 3858 * not only these marked to inherit. 3859 * 3860 * @param dbc the current database context 3861 * @param resource the resource 3862 * @param inheritedOnly skip non-inherited entries if set 3863 * 3864 * @return the access control list of the resource 3865 * 3866 * @throws CmsException if something goes wrong 3867 */ 3868 public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource, boolean inheritedOnly) 3869 throws CmsException { 3870 3871 return getAccessControlList(dbc, resource, inheritedOnly, resource.isFolder(), 0); 3872 } 3873 3874 /** 3875 * Returns the number of active connections managed by a pool.<p> 3876 * 3877 * @param dbPoolUrl the url of a pool 3878 * @return the number of active connections 3879 * @throws CmsDbException if something goes wrong 3880 */ 3881 public int getActiveConnections(String dbPoolUrl) throws CmsDbException { 3882 3883 CmsDbPoolV11 pool = m_pools.get(dbPoolUrl); 3884 if (pool == null) { 3885 CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl); 3886 throw new CmsDbException(message); 3887 } 3888 try { 3889 return pool.getActiveConnections(); 3890 } catch (Exception exc) { 3891 CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl); 3892 throw new CmsDbException(message, exc); 3893 } 3894 3895 } 3896 3897 /** 3898 * Reads all access control entries.<p> 3899 * 3900 * @param dbc the current database context 3901 * @return all access control entries for the current project (offline/online) 3902 * 3903 * @throws CmsException if something goes wrong 3904 */ 3905 public List<CmsAccessControlEntry> getAllAccessControlEntries(CmsDbContext dbc) throws CmsException { 3906 3907 I_CmsUserDriver userDriver = getUserDriver(dbc); 3908 List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries( 3909 dbc, 3910 dbc.currentProject(), 3911 CmsAccessControlEntry.PRINCIPAL_READALL_ID, 3912 false); 3913 return ace; 3914 } 3915 3916 /** 3917 * Returns all projects which are owned by the current user or which are 3918 * accessible by the current user.<p> 3919 * 3920 * @param dbc the current database context 3921 * @param orgUnit the organizational unit to search project in 3922 * @param includeSubOus if to include sub organizational units 3923 * 3924 * @return a list of objects of type <code>{@link CmsProject}</code> 3925 * 3926 * @throws CmsException if something goes wrong 3927 */ 3928 public List<CmsProject> getAllAccessibleProjects( 3929 CmsDbContext dbc, 3930 CmsOrganizationalUnit orgUnit, 3931 boolean includeSubOus) 3932 throws CmsException { 3933 3934 Set<CmsProject> projects = new HashSet<CmsProject>(); 3935 3936 // get the ous where the user has the project manager role 3937 List<CmsOrganizationalUnit> ous = getOrgUnitsForRole( 3938 dbc, 3939 CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()), 3940 includeSubOus); 3941 3942 // get the groups of the user if needed 3943 Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>(); 3944 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 3945 while (itGroups.hasNext()) { 3946 CmsGroup group = itGroups.next(); 3947 userGroupIds.add(group.getId()); 3948 } 3949 3950 // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus' 3951 // get all projects that might come in question 3952 projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName())); 3953 3954 // filter hidden and not accessible projects 3955 Iterator<CmsProject> itProjects = projects.iterator(); 3956 while (itProjects.hasNext()) { 3957 CmsProject project = itProjects.next(); 3958 boolean accessible = true; 3959 // if hidden 3960 accessible = accessible && !project.isHidden(); 3961 3962 if (!includeSubOus) { 3963 // if not exact in the given ou 3964 accessible = accessible && project.getOuFqn().equals(orgUnit.getName()); 3965 } else { 3966 // if not in the given ou 3967 accessible = accessible && project.getOuFqn().startsWith(orgUnit.getName()); 3968 } 3969 3970 if (!accessible) { 3971 itProjects.remove(); 3972 continue; 3973 } 3974 3975 accessible = false; 3976 // online project 3977 accessible = accessible || project.isOnlineProject(); 3978 // if owner 3979 accessible = accessible || project.getOwnerId().equals(dbc.currentUser().getId()); 3980 3981 // project managers 3982 Iterator<CmsOrganizationalUnit> itOus = ous.iterator(); 3983 while (!accessible && itOus.hasNext()) { 3984 CmsOrganizationalUnit ou = itOus.next(); 3985 // for project managers check visibility 3986 accessible = accessible || project.getOuFqn().startsWith(ou.getName()); 3987 } 3988 3989 if (!accessible) { 3990 // if direct user or manager of project 3991 CmsUUID groupId = null; 3992 if (userGroupIds.contains(project.getGroupId())) { 3993 groupId = project.getGroupId(); 3994 } else if (userGroupIds.contains(project.getManagerGroupId())) { 3995 groupId = project.getManagerGroupId(); 3996 } 3997 if (groupId != null) { 3998 String oufqn = readGroup(dbc, groupId).getOuFqn(); 3999 accessible = accessible || (oufqn.startsWith(dbc.getRequestContext().getOuFqn())); 4000 } 4001 } 4002 if (!accessible) { 4003 // remove not accessible project 4004 itProjects.remove(); 4005 } 4006 } 4007 4008 List<CmsProject> accessibleProjects = new ArrayList<CmsProject>(projects); 4009 // sort the list of projects based on the project name 4010 Collections.sort(accessibleProjects); 4011 // ensure the online project is in first place 4012 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 4013 if (accessibleProjects.contains(onlineProject)) { 4014 accessibleProjects.remove(onlineProject); 4015 } 4016 accessibleProjects.add(0, onlineProject); 4017 4018 return accessibleProjects; 4019 } 4020 4021 /** 4022 * Returns a list with all projects from history.<p> 4023 * 4024 * @param dbc the current database context 4025 * 4026 * @return list of <code>{@link CmsHistoryProject}</code> objects 4027 * with all projects from history. 4028 * 4029 * @throws CmsException if operation was not successful 4030 */ 4031 public List<CmsHistoryProject> getAllHistoricalProjects(CmsDbContext dbc) throws CmsException { 4032 4033 // user is allowed to access all existing projects for the ous he has the project_manager role 4034 Set<CmsOrganizationalUnit> manOus = new HashSet<CmsOrganizationalUnit>( 4035 getOrgUnitsForRole(dbc, CmsRole.PROJECT_MANAGER, true)); 4036 4037 List<CmsHistoryProject> projects = getHistoryDriver(dbc).readProjects(dbc); 4038 Iterator<CmsHistoryProject> itProjects = projects.iterator(); 4039 while (itProjects.hasNext()) { 4040 CmsHistoryProject project = itProjects.next(); 4041 if (project.isHidden()) { 4042 // project is hidden 4043 itProjects.remove(); 4044 continue; 4045 } 4046 if (!project.getOuFqn().startsWith(dbc.currentUser().getOuFqn())) { 4047 // project is not visible from the users ou 4048 itProjects.remove(); 4049 continue; 4050 } 4051 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, project.getOuFqn()); 4052 if (manOus.contains(ou)) { 4053 // user is project manager for this project 4054 continue; 4055 } else if (project.getOwnerId().equals(dbc.currentUser().getId())) { 4056 // user is owner of the project 4057 continue; 4058 } else { 4059 boolean found = false; 4060 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 4061 while (itGroups.hasNext()) { 4062 CmsGroup group = itGroups.next(); 4063 if (project.getManagerGroupId().equals(group.getId())) { 4064 found = true; 4065 break; 4066 } 4067 } 4068 if (found) { 4069 // user is member of the manager group of the project 4070 continue; 4071 } 4072 } 4073 itProjects.remove(); 4074 } 4075 return projects; 4076 } 4077 4078 /** 4079 * Returns all projects which are owned by the current user or which are manageable 4080 * for the group of the user.<p> 4081 * 4082 * @param dbc the current database context 4083 * @param orgUnit the organizational unit to search project in 4084 * @param includeSubOus if to include sub organizational units 4085 * 4086 * @return a list of objects of type <code>{@link CmsProject}</code> 4087 * 4088 * @throws CmsException if operation was not successful 4089 */ 4090 public List<CmsProject> getAllManageableProjects( 4091 CmsDbContext dbc, 4092 CmsOrganizationalUnit orgUnit, 4093 boolean includeSubOus) 4094 throws CmsException { 4095 4096 Set<CmsProject> projects = new HashSet<CmsProject>(); 4097 4098 // get the ous where the user has the project manager role 4099 List<CmsOrganizationalUnit> ous = getOrgUnitsForRole( 4100 dbc, 4101 CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()), 4102 includeSubOus); 4103 4104 // get the groups of the user if needed 4105 Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>(); 4106 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 4107 while (itGroups.hasNext()) { 4108 CmsGroup group = itGroups.next(); 4109 userGroupIds.add(group.getId()); 4110 } 4111 4112 // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus' 4113 // get all projects that might come in question 4114 projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName())); 4115 4116 // filter hidden and not manageable projects 4117 Iterator<CmsProject> itProjects = projects.iterator(); 4118 while (itProjects.hasNext()) { 4119 CmsProject project = itProjects.next(); 4120 boolean manageable = true; 4121 // if online 4122 manageable = manageable && !project.isOnlineProject(); 4123 // if hidden 4124 manageable = manageable && !project.isHidden(); 4125 4126 if (!includeSubOus) { 4127 // if not exact in the given ou 4128 manageable = manageable && project.getOuFqn().equals(orgUnit.getName()); 4129 } else { 4130 // if not in the given ou 4131 manageable = manageable && project.getOuFqn().startsWith(orgUnit.getName()); 4132 } 4133 4134 if (!manageable) { 4135 itProjects.remove(); 4136 continue; 4137 } 4138 4139 manageable = false; 4140 // if owner 4141 manageable = manageable || project.getOwnerId().equals(dbc.currentUser().getId()); 4142 4143 // project managers 4144 Iterator<CmsOrganizationalUnit> itOus = ous.iterator(); 4145 while (!manageable && itOus.hasNext()) { 4146 CmsOrganizationalUnit ou = itOus.next(); 4147 // for project managers check visibility 4148 manageable = manageable || project.getOuFqn().startsWith(ou.getName()); 4149 } 4150 4151 if (!manageable) { 4152 // if manager of project 4153 if (userGroupIds.contains(project.getManagerGroupId())) { 4154 String oufqn = readGroup(dbc, project.getManagerGroupId()).getOuFqn(); 4155 manageable = manageable || (oufqn.startsWith(dbc.getRequestContext().getOuFqn())); 4156 } 4157 } 4158 if (!manageable) { 4159 // remove not accessible project 4160 itProjects.remove(); 4161 } 4162 } 4163 4164 List<CmsProject> manageableProjects = new ArrayList<CmsProject>(projects); 4165 // sort the list of projects based on the project name 4166 Collections.sort(manageableProjects); 4167 // ensure the online project is not in the list 4168 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 4169 if (manageableProjects.contains(onlineProject)) { 4170 manageableProjects.remove(onlineProject); 4171 } 4172 4173 return manageableProjects; 4174 } 4175 4176 /** 4177 * Returns all child groups of a group.<p> 4178 * 4179 * @param dbc the current database context 4180 * @param group the group to get the child for 4181 * @param includeSubChildren if set also returns all sub-child groups of the given group 4182 * 4183 * @return a list of all child <code>{@link CmsGroup}</code> objects 4184 * 4185 * @throws CmsException if operation was not successful 4186 */ 4187 public List<CmsGroup> getChildren(CmsDbContext dbc, CmsGroup group, boolean includeSubChildren) 4188 throws CmsException { 4189 4190 if (!includeSubChildren) { 4191 return getUserDriver(dbc).readChildGroups(dbc, group.getName()); 4192 } 4193 Set<CmsGroup> allChildren = new TreeSet<CmsGroup>(); 4194 // iterate all child groups 4195 Iterator<CmsGroup> it = getUserDriver(dbc).readChildGroups(dbc, group.getName()).iterator(); 4196 while (it.hasNext()) { 4197 CmsGroup child = it.next(); 4198 // add the group itself 4199 allChildren.add(child); 4200 // now get all sub-children for each group 4201 allChildren.addAll(getChildren(dbc, child, true)); 4202 } 4203 return new ArrayList<CmsGroup>(allChildren); 4204 } 4205 4206 /** 4207 * Returns the date when the resource was last visited by the user.<p> 4208 * 4209 * @param dbc the database context 4210 * @param poolName the name of the database pool to use 4211 * @param user the user to check the date 4212 * @param resource the resource to check the date 4213 * 4214 * @return the date when the resource was last visited by the user 4215 * 4216 * @throws CmsException if something goes wrong 4217 */ 4218 public long getDateLastVisitedBy(CmsDbContext dbc, String poolName, CmsUser user, CmsResource resource) 4219 throws CmsException { 4220 4221 return m_subscriptionDriver.getDateLastVisitedBy(dbc, poolName, user, resource); 4222 } 4223 4224 /** 4225 * Returns all groups of the given organizational unit.<p> 4226 * 4227 * @param dbc the current db context 4228 * @param orgUnit the organizational unit to get the groups for 4229 * @param includeSubOus if all groups of sub-organizational units should be retrieved too 4230 * @param readRoles if to read roles or groups 4231 * 4232 * @return all <code>{@link CmsGroup}</code> objects in the organizational unit 4233 * 4234 * @throws CmsException if operation was not successful 4235 * 4236 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 4237 * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean) 4238 */ 4239 public List<CmsGroup> getGroups( 4240 CmsDbContext dbc, 4241 CmsOrganizationalUnit orgUnit, 4242 boolean includeSubOus, 4243 boolean readRoles) 4244 throws CmsException { 4245 4246 return getUserDriver(dbc).getGroups(dbc, orgUnit, includeSubOus, readRoles); 4247 } 4248 4249 /** 4250 * Returns the groups of an user filtered by the specified IP address.<p> 4251 * 4252 * @param dbc the current database context 4253 * @param username the name of the user 4254 * @param readRoles if to read roles or groups 4255 * 4256 * @return the groups of the given user, as a list of {@link CmsGroup} objects 4257 * 4258 * @throws CmsException if something goes wrong 4259 */ 4260 public List<CmsGroup> getGroupsOfUser(CmsDbContext dbc, String username, boolean readRoles) throws CmsException { 4261 4262 return getGroupsOfUser(dbc, username, "", true, readRoles, false, dbc.getRequestContext().getRemoteAddress()); 4263 } 4264 4265 /** 4266 * Returns the groups of an user filtered by the specified IP address.<p> 4267 * 4268 * @param dbc the current database context 4269 * @param username the name of the user 4270 * @param ouFqn the fully qualified name of the organizational unit to restrict the result set for 4271 * @param includeChildOus include groups of child organizational units 4272 * @param readRoles if to read roles or groups 4273 * @param directGroupsOnly if set only the direct assigned groups will be returned, if not also indirect groups 4274 * @param remoteAddress the IP address to filter the groups in the result list 4275 * 4276 * @return a list of <code>{@link CmsGroup}</code> objects 4277 * 4278 * @throws CmsException if operation was not successful 4279 */ 4280 public List<CmsGroup> getGroupsOfUser( 4281 CmsDbContext dbc, 4282 String username, 4283 String ouFqn, 4284 boolean includeChildOus, 4285 boolean readRoles, 4286 boolean directGroupsOnly, 4287 String remoteAddress) 4288 throws CmsException { 4289 4290 CmsUser user = readUser(dbc, username); 4291 String prefix = ouFqn + "_" + includeChildOus + "_" + directGroupsOnly + "_" + readRoles + "_" + remoteAddress; 4292 String cacheKey = m_keyGenerator.getCacheKeyForUserGroups(prefix, dbc, user); 4293 List<CmsGroup> groups = m_monitor.getCachedUserGroups(user.getId(), cacheKey); 4294 if (groups == null) { 4295 // get all groups of the user 4296 List<CmsGroup> directGroups = getUserDriver(dbc).readGroupsOfUser( 4297 dbc, 4298 user.getId(), 4299 readRoles ? "" : ouFqn, 4300 readRoles ? true : includeChildOus, 4301 remoteAddress, 4302 readRoles); 4303 Set<CmsGroup> allGroups = new HashSet<CmsGroup>(); 4304 if (!readRoles) { 4305 allGroups.addAll(directGroups); 4306 } 4307 if (!directGroupsOnly) { 4308 if (!readRoles) { 4309 // now get all parents of the groups 4310 for (int i = 0; i < directGroups.size(); i++) { 4311 CmsGroup parent = getParent(dbc, directGroups.get(i).getName()); 4312 while ((parent != null) && (!allGroups.contains(parent))) { 4313 if (parent.getOuFqn().startsWith(ouFqn)) { 4314 allGroups.add(parent); 4315 } 4316 // read next parent group 4317 parent = getParent(dbc, parent.getName()); 4318 } 4319 } 4320 } 4321 } 4322 if (readRoles) { 4323 // for each for role 4324 for (int i = 0; i < directGroups.size(); i++) { 4325 CmsGroup group = directGroups.get(i); 4326 CmsRole role = CmsRole.valueOf(group); 4327 if (!includeChildOus && role.getOuFqn().equals(ouFqn)) { 4328 allGroups.add(group); 4329 } 4330 if (includeChildOus && role.getOuFqn().startsWith(ouFqn)) { 4331 allGroups.add(group); 4332 } 4333 if (directGroupsOnly || (!includeChildOus && !role.getOuFqn().equals(ouFqn))) { 4334 // if roles of child OUs are not requested and the role does not belong to the requested OU don't include the role children 4335 continue; 4336 } 4337 CmsOrganizationalUnit currentOu = readOrganizationalUnit(dbc, group.getOuFqn()); 4338 boolean readChildRoleGroups = true; 4339 if (currentOu.hasFlagWebuser() && role.forOrgUnit(null).equals(CmsRole.ACCOUNT_MANAGER)) { 4340 readChildRoleGroups = false; 4341 } 4342 if (readChildRoleGroups) { 4343 // get the child roles 4344 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 4345 while (itChildRoles.hasNext()) { 4346 CmsRole childRole = itChildRoles.next(); 4347 if (childRole.isSystemRole()) { 4348 if (canReadRoleInOu(currentOu, childRole)) { 4349 // include system roles only 4350 try { 4351 allGroups.add(readGroup(dbc, childRole.getGroupName())); 4352 } catch (CmsDataAccessException e) { 4353 // should not happen, log error if it does 4354 LOG.error(e.getLocalizedMessage(), e); 4355 } 4356 } 4357 } 4358 } 4359 } else { 4360 LOG.info("Skipping child role group check for web user OU " + currentOu.getName()); 4361 } 4362 if (includeChildOus) { 4363 // if needed include the roles of child ous 4364 Iterator<CmsOrganizationalUnit> itSubOus = getOrganizationalUnits( 4365 dbc, 4366 readOrganizationalUnit(dbc, group.getOuFqn()), 4367 true).iterator(); 4368 while (itSubOus.hasNext()) { 4369 CmsOrganizationalUnit subOu = itSubOus.next(); 4370 // add role in child ou 4371 try { 4372 if (canReadRoleInOu(subOu, role)) { 4373 allGroups.add(readGroup(dbc, role.forOrgUnit(subOu.getName()).getGroupName())); 4374 } 4375 } catch (CmsDbEntryNotFoundException e) { 4376 // ignore, this may happen while deleting an orgunit 4377 if (LOG.isDebugEnabled()) { 4378 LOG.debug(e.getLocalizedMessage(), e); 4379 } 4380 } 4381 // add child roles in child ous 4382 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 4383 while (itChildRoles.hasNext()) { 4384 CmsRole childRole = itChildRoles.next(); 4385 try { 4386 if (canReadRoleInOu(subOu, childRole)) { 4387 allGroups.add( 4388 readGroup(dbc, childRole.forOrgUnit(subOu.getName()).getGroupName())); 4389 } 4390 } catch (CmsDbEntryNotFoundException e) { 4391 // ignore, this may happen while deleting an orgunit 4392 if (LOG.isDebugEnabled()) { 4393 LOG.debug(e.getLocalizedMessage(), e); 4394 } 4395 } 4396 } 4397 } 4398 } 4399 } 4400 } 4401 // make group list unmodifiable for caching 4402 groups = Collections.unmodifiableList(new ArrayList<CmsGroup>(allGroups)); 4403 if (dbc.getProjectId().isNullUUID()) { 4404 m_monitor.getGroupListCache().setGroups(user, cacheKey, groups); 4405 } 4406 } 4407 4408 return groups; 4409 } 4410 4411 /** 4412 * Returns the history driver.<p> 4413 * 4414 * @return the history driver 4415 */ 4416 public I_CmsHistoryDriver getHistoryDriver() { 4417 4418 return m_historyDriver; 4419 } 4420 4421 /** 4422 * Returns the history driver for a given database context.<p> 4423 * 4424 * @param dbc the database context 4425 * @return the history driver for the database context 4426 */ 4427 public I_CmsHistoryDriver getHistoryDriver(CmsDbContext dbc) { 4428 4429 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4430 return m_historyDriver; 4431 } 4432 I_CmsHistoryDriver driver = dbc.getHistoryDriver(dbc.getProjectId()); 4433 return driver != null ? driver : m_historyDriver; 4434 4435 } 4436 4437 /** 4438 * Returns the number of idle connections managed by a pool.<p> 4439 * 4440 * @param dbPoolUrl the url of a pool 4441 * @return the number of idle connections 4442 * @throws CmsDbException if something goes wrong 4443 */ 4444 public int getIdleConnections(String dbPoolUrl) throws CmsDbException { 4445 4446 CmsDbPoolV11 pool = m_pools.get(dbPoolUrl); 4447 if (pool == null) { 4448 CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl); 4449 throw new CmsDbException(message); 4450 } 4451 try { 4452 return pool.getIdleConnections(); 4453 } catch (Exception exc) { 4454 CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl); 4455 throw new CmsDbException(message, exc); 4456 } 4457 4458 } 4459 4460 /** 4461 * Returns the lock state of a resource.<p> 4462 * 4463 * @param dbc the current database context 4464 * @param resource the resource to return the lock state for 4465 * 4466 * @return the lock state of the resource 4467 * 4468 * @throws CmsException if something goes wrong 4469 */ 4470 public CmsLock getLock(CmsDbContext dbc, CmsResource resource) throws CmsException { 4471 4472 return m_lockManager.getLock(dbc, resource); 4473 } 4474 4475 /** 4476 * Returns all locked resources in a given folder.<p> 4477 * 4478 * @param dbc the current database context 4479 * @param resource the folder to search in 4480 * @param filter the lock filter 4481 * 4482 * @return a list of locked resource paths (relative to current site) 4483 * 4484 * @throws CmsException if the current project is locked 4485 */ 4486 public List<String> getLockedResources(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter) 4487 throws CmsException { 4488 4489 List<String> lockedResources = new ArrayList<String>(); 4490 // get locked resources 4491 Iterator<CmsLock> it = m_lockManager.getLocks(dbc, resource.getRootPath(), filter).iterator(); 4492 while (it.hasNext()) { 4493 CmsLock lock = it.next(); 4494 lockedResources.add(dbc.removeSiteRoot(lock.getResourceName())); 4495 } 4496 Collections.sort(lockedResources); 4497 return lockedResources; 4498 } 4499 4500 /** 4501 * Returns all locked resources in a given folder.<p> 4502 * 4503 * @param dbc the current database context 4504 * @param resource the folder to search in 4505 * @param filter the lock filter 4506 * 4507 * @return a list of locked resources 4508 * 4509 * @throws CmsException if the current project is locked 4510 */ 4511 public List<CmsResource> getLockedResourcesObjects(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter) 4512 throws CmsException { 4513 4514 return m_lockManager.getLockedResources(dbc, resource, filter); 4515 } 4516 4517 /** 4518 * Returns all locked resources in a given folder, but uses a cache for resource lookups.<p> 4519 * 4520 * @param dbc the current database context 4521 * @param resource the folder to search in 4522 * @param filter the lock filter 4523 * @param cache the cache to use for resource lookups 4524 * 4525 * @return a list of locked resources 4526 * 4527 * @throws CmsException if the current project is locked 4528 */ 4529 public List<CmsResource> getLockedResourcesObjectsWithCache( 4530 CmsDbContext dbc, 4531 CmsResource resource, 4532 CmsLockFilter filter, 4533 Map<String, CmsResource> cache) 4534 throws CmsException { 4535 4536 return m_lockManager.getLockedResourcesWithCache(dbc, resource, filter, cache); 4537 } 4538 4539 /** 4540 * Returns all log entries matching the given filter.<p> 4541 * 4542 * @param dbc the current db context 4543 * @param filter the filter to match the log entries 4544 * 4545 * @return all log entries matching the given filter 4546 * 4547 * @throws CmsException if something goes wrong 4548 * 4549 * @see CmsSecurityManager#getLogEntries(CmsRequestContext, CmsLogFilter) 4550 */ 4551 public List<CmsLogEntry> getLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException { 4552 4553 updateLog(dbc); 4554 return m_projectDriver.readLog(dbc, filter); 4555 } 4556 4557 /** 4558 * Returns the next publish tag for the published historical resources.<p> 4559 * 4560 * @param dbc the current database context 4561 * 4562 * @return the next available publish tag 4563 */ 4564 public int getNextPublishTag(CmsDbContext dbc) { 4565 4566 if (dbc.getProjectId().isNullUUID()) { 4567 synchronized (m_publishTagLock) { 4568 int dbNextPublishTag = getHistoryDriver(dbc).readNextPublishTag(dbc); 4569 if (m_lastPublishTag == null) { 4570 m_lastPublishTag = Integer.valueOf(dbNextPublishTag); 4571 return dbNextPublishTag; 4572 } else { 4573 int newValue = Math.max(dbNextPublishTag, m_lastPublishTag.intValue() + 1); 4574 m_lastPublishTag = Integer.valueOf(newValue); 4575 return newValue; 4576 4577 } 4578 } 4579 } else { 4580 return getHistoryDriver(dbc).readNextPublishTag(dbc); 4581 } 4582 4583 } 4584 4585 /** 4586 * Returns all child organizational units of the given parent organizational unit including 4587 * hierarchical deeper organization units if needed.<p> 4588 * 4589 * @param dbc the current db context 4590 * @param parent the parent organizational unit, or <code>null</code> for the root 4591 * @param includeChildren if hierarchical deeper organization units should also be returned 4592 * 4593 * @return a list of <code>{@link CmsOrganizationalUnit}</code> objects 4594 * 4595 * @throws CmsException if operation was not successful 4596 * 4597 * @see org.opencms.security.CmsOrgUnitManager#getOrganizationalUnits(CmsObject, String, boolean) 4598 */ 4599 public List<CmsOrganizationalUnit> getOrganizationalUnits( 4600 CmsDbContext dbc, 4601 CmsOrganizationalUnit parent, 4602 boolean includeChildren) 4603 throws CmsException { 4604 4605 if (parent == null) { 4606 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PARENT_ORGUNIT_NULL_0)); 4607 } 4608 return getUserDriver(dbc).getOrganizationalUnits(dbc, parent, includeChildren); 4609 } 4610 4611 /** 4612 * Returns all the organizational units for which the current user has the given role.<p> 4613 * 4614 * @param dbc the current database context 4615 * @param role the role to check 4616 * @param includeSubOus if sub organizational units should be included in the search 4617 * 4618 * @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects 4619 * 4620 * @throws CmsException if something goes wrong 4621 */ 4622 public List<CmsOrganizationalUnit> getOrgUnitsForRole(CmsDbContext dbc, CmsRole role, boolean includeSubOus) 4623 throws CmsException { 4624 4625 String ouFqn = role.getOuFqn(); 4626 if (ouFqn == null) { 4627 ouFqn = ""; 4628 role = role.forOrgUnit(""); 4629 } 4630 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouFqn); 4631 List<CmsOrganizationalUnit> orgUnits = new ArrayList<CmsOrganizationalUnit>(); 4632 if (m_securityManager.hasRole(dbc, dbc.currentUser(), role)) { 4633 orgUnits.add(ou); 4634 } 4635 if (includeSubOus) { 4636 Iterator<CmsOrganizationalUnit> it = getOrganizationalUnits(dbc, ou, true).iterator(); 4637 while (it.hasNext()) { 4638 CmsOrganizationalUnit orgUnit = it.next(); 4639 if (m_securityManager.hasRole(dbc, dbc.currentUser(), role.forOrgUnit(orgUnit.getName()))) { 4640 orgUnits.add(orgUnit); 4641 } 4642 } 4643 } 4644 return orgUnits; 4645 } 4646 4647 /** 4648 * Returns the parent group of a group.<p> 4649 * 4650 * @param dbc the current database context 4651 * @param groupname the name of the group 4652 * 4653 * @return group the parent group or <code>null</code> 4654 * 4655 * @throws CmsException if operation was not successful 4656 */ 4657 public CmsGroup getParent(CmsDbContext dbc, String groupname) throws CmsException { 4658 4659 CmsGroup group = readGroup(dbc, groupname); 4660 if (group.getParentId().isNullUUID()) { 4661 return null; 4662 } 4663 4664 // try to read from cache 4665 CmsGroup parent = m_monitor.getCachedGroup(group.getParentId().toString()); 4666 if (parent == null) { 4667 parent = getUserDriver(dbc).readGroup(dbc, group.getParentId()); 4668 m_monitor.cacheGroup(parent); 4669 } 4670 return parent; 4671 } 4672 4673 /** 4674 * Returns the set of permissions of the current user for a given resource.<p> 4675 * 4676 * @param dbc the current database context 4677 * @param resource the resource 4678 * @param user the user 4679 * 4680 * @return bit set with allowed permissions 4681 * 4682 * @throws CmsException if something goes wrong 4683 */ 4684 public CmsPermissionSetCustom getPermissions(CmsDbContext dbc, CmsResource resource, CmsUser user) 4685 throws CmsException { 4686 4687 CmsAccessControlList acList = getAccessControlList(dbc, resource, false); 4688 List<CmsGroup> groups = getGroupsOfUser(dbc, user.getName(), false); 4689 List<CmsRole> roles = getRolesForUser(dbc, user); 4690 CmsPermissionSetCustom permissions = acList.getPermissions(user, groups, roles); 4691 4692 if (acList.getExclusiveAccessPrincipals().size() > 0) { 4693 long now; 4694 @SuppressWarnings("unchecked") 4695 Supplier<Long> alternativeClock = (Supplier<Long>)(dbc.getRequestContext().getAttribute( 4696 ATTR_EXCLUSIVE_ACCESS_CLOCK)); 4697 if (alternativeClock != null) { 4698 // used for testing 4699 now = alternativeClock.get().longValue(); 4700 } else { 4701 // *NOT* using dbc.getRequestContext().getRequestTime(), even though that value is used for normal resource availability checks, because 4702 // that value may be manipulated by some workplace classes, and we want the real time for permission checks 4703 now = System.currentTimeMillis(); 4704 } 4705 permissions.setCacheable(false); // resources going in/out of availability can change permissions - don't cache 4706 if (!resource.isReleasedAndNotExpired(now)) { 4707 boolean hasExclusiveAccess = false; 4708 for (CmsGroup group : groups) { 4709 if (acList.getExclusiveAccessPrincipals().contains(group.getId())) { 4710 hasExclusiveAccess = true; 4711 break; 4712 } 4713 } 4714 hasExclusiveAccess |= acList.getExclusiveAccessPrincipals().contains(user.getId()); 4715 if (!hasExclusiveAccess) { 4716 permissions.denyPermissions(CmsPermissionSet.PERMISSION_FULL); 4717 } 4718 } 4719 } 4720 return permissions; 4721 } 4722 4723 /** 4724 * Returns the project driver.<p> 4725 * 4726 * @return the project driver 4727 */ 4728 public I_CmsProjectDriver getProjectDriver() { 4729 4730 return m_projectDriver; 4731 } 4732 4733 /** 4734 * Returns the project driver for a given DB context.<p> 4735 * 4736 * @param dbc the database context 4737 * 4738 * @return the project driver for the database context 4739 */ 4740 public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc) { 4741 4742 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4743 return m_projectDriver; 4744 } 4745 I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId()); 4746 return driver != null ? driver : m_projectDriver; 4747 } 4748 4749 /** 4750 * Returns either the project driver for the DB context (if it has one) or a default project driver.<p> 4751 * 4752 * @param dbc the DB context 4753 * @param defaultDriver the driver which should be returned if there is no project driver for the DB context 4754 * 4755 * @return either the project driver for the DB context, or the default driver 4756 */ 4757 public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc, I_CmsProjectDriver defaultDriver) { 4758 4759 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4760 return defaultDriver; 4761 } 4762 I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId()); 4763 return driver != null ? driver : defaultDriver; 4764 } 4765 4766 /** 4767 * Returns the uuid id for the given id.<p> 4768 * 4769 * TODO: remove this method as soon as possible 4770 * 4771 * @param dbc the current database context 4772 * @param id the old project id 4773 * 4774 * @return the new uuid for the given id 4775 * 4776 * @throws CmsException if something goes wrong 4777 */ 4778 public CmsUUID getProjectId(CmsDbContext dbc, int id) throws CmsException { 4779 4780 Iterator<CmsProject> itProjects = getAllAccessibleProjects( 4781 dbc, 4782 readOrganizationalUnit(dbc, ""), 4783 true).iterator(); 4784 while (itProjects.hasNext()) { 4785 CmsProject project = itProjects.next(); 4786 if (project.getUuid().hashCode() == id) { 4787 return project.getUuid(); 4788 } 4789 } 4790 return null; 4791 } 4792 4793 /** 4794 * Returns the configuration read from the <code>opencms.properties</code> file.<p> 4795 * 4796 * @return the configuration read from the <code>opencms.properties</code> file 4797 */ 4798 public CmsParameterConfiguration getPropertyConfiguration() { 4799 4800 return m_propertyConfiguration; 4801 } 4802 4803 /** 4804 * Returns a new publish list that contains the unpublished resources related 4805 * to all resources in the given publish list, the related resources exclude 4806 * all resources in the given publish list and also locked (by other users) resources.<p> 4807 * 4808 * @param dbc the current database context 4809 * @param publishList the publish list to exclude from result 4810 * @param filter the relation filter to use to get the related resources 4811 * 4812 * @return a new publish list that contains the related resources 4813 * 4814 * @throws CmsException if something goes wrong 4815 * 4816 * @see org.opencms.publish.CmsPublishManager#getRelatedResourcesToPublish(CmsObject, CmsPublishList) 4817 */ 4818 public CmsPublishList getRelatedResourcesToPublish( 4819 CmsDbContext dbc, 4820 CmsPublishList publishList, 4821 CmsRelationFilter filter) 4822 throws CmsException { 4823 4824 Map<String, CmsResource> relations = new HashMap<String, CmsResource>(); 4825 4826 // check if progress should be set in the thread 4827 A_CmsProgressThread thread = null; 4828 if (Thread.currentThread() instanceof A_CmsProgressThread) { 4829 thread = (A_CmsProgressThread)Thread.currentThread(); 4830 } 4831 4832 // get all resources to publish 4833 List<CmsResource> publishResources = publishList.getAllResources(); 4834 Iterator<CmsResource> itCheckList = publishResources.iterator(); 4835 // iterate over them 4836 int count = 0; 4837 while (itCheckList.hasNext()) { 4838 4839 // set progress in thread 4840 count++; 4841 if (thread != null) { 4842 4843 if (thread.isInterrupted()) { 4844 throw new CmsIllegalStateException( 4845 org.opencms.workplace.commons.Messages.get().container( 4846 org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0)); 4847 } 4848 thread.setProgress((count * 20) / publishResources.size()); 4849 thread.setDescription( 4850 org.opencms.workplace.commons.Messages.get().getBundle().key( 4851 org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP1_2, 4852 Integer.valueOf(count), 4853 Integer.valueOf(publishResources.size()))); 4854 } 4855 4856 CmsResource checkResource = itCheckList.next(); 4857 // get and iterate over all related resources 4858 Iterator<CmsRelation> itRelations = getRelationsForResource(dbc, checkResource, filter).iterator(); 4859 while (itRelations.hasNext()) { 4860 CmsRelation relation = itRelations.next(); 4861 try { 4862 // get the target of the relation, see CmsRelation#getTarget(CmsObject, CmsResourceFilter) 4863 CmsResource target; 4864 try { 4865 // first look up by id 4866 target = readResource(dbc, relation.getTargetId(), CmsResourceFilter.ALL); 4867 } catch (CmsVfsResourceNotFoundException e) { 4868 // then look up by name, but from the root site 4869 String storedSiteRoot = dbc.getRequestContext().getSiteRoot(); 4870 try { 4871 dbc.getRequestContext().setSiteRoot(""); 4872 target = readResource(dbc, relation.getTargetPath(), CmsResourceFilter.ALL); 4873 } finally { 4874 dbc.getRequestContext().setSiteRoot(storedSiteRoot); 4875 } 4876 } 4877 CmsLock lock = getLock(dbc, target); 4878 // just add resources that may come in question 4879 if (!publishResources.contains(target) // is not in the original list 4880 && !relations.containsKey(target.getRootPath()) // has not been already added by another relation 4881 && !target.getState().isUnchanged() // has been changed 4882 && lock.isLockableBy(dbc.currentUser())) { // is lockable by current user 4883 4884 relations.put(target.getRootPath(), target); 4885 // now check the folder structure 4886 CmsResource parent = getVfsDriver(dbc).readParentFolder( 4887 dbc, 4888 dbc.currentProject().getUuid(), 4889 target.getStructureId()); 4890 while ((parent != null) && parent.getState().isNew()) { 4891 // just add resources that may come in question 4892 if (!publishResources.contains(parent) // is not in the original list 4893 && !relations.containsKey(parent.getRootPath())) { // has not been already added by another relation 4894 4895 relations.put(parent.getRootPath(), parent); 4896 } 4897 parent = getVfsDriver(dbc).readParentFolder( 4898 dbc, 4899 dbc.currentProject().getUuid(), 4900 parent.getStructureId()); 4901 } 4902 } 4903 } catch (CmsVfsResourceNotFoundException e) { 4904 // ignore broken links 4905 if (LOG.isDebugEnabled()) { 4906 LOG.debug(e.getLocalizedMessage(), e); 4907 } 4908 } 4909 } 4910 } 4911 4912 CmsPublishList ret = new CmsPublishList(publishList.getDirectPublishResources(), false, false); 4913 ret.addAll(relations.values(), false); 4914 ret.initialize(); 4915 return ret; 4916 } 4917 4918 /** 4919 * Returns all relations for the given resource matching the given filter.<p> 4920 * 4921 * @param dbc the current db context 4922 * @param resource the resource to retrieve the relations for 4923 * @param filter the filter to match the relation 4924 * 4925 * @return all relations for the given resource matching the given filter 4926 * 4927 * @throws CmsException if something goes wrong 4928 * 4929 * @see CmsSecurityManager#getRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter) 4930 */ 4931 public List<CmsRelation> getRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter) 4932 throws CmsException { 4933 4934 CmsUUID projectId = getProjectIdForContext(dbc); 4935 return getVfsDriver(dbc).readRelations(dbc, projectId, resource, filter); 4936 } 4937 4938 /** 4939 * Returns the list of organizational units the given resource belongs to.<p> 4940 * 4941 * @param dbc the current database context 4942 * @param resource the resource 4943 * 4944 * @return list of {@link CmsOrganizationalUnit} objects 4945 * 4946 * @throws CmsException if something goes wrong 4947 */ 4948 public List<CmsOrganizationalUnit> getResourceOrgUnits(CmsDbContext dbc, CmsResource resource) throws CmsException { 4949 4950 boolean nullDbcProjectId = (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID(); 4951 if (nullDbcProjectId && resourceOrgUnitCachingEnabled) { 4952 try { 4953 return m_monitor.getResourceOuCache().get(new ResourceOUCacheKey(this, dbc)).getResourceOrgUnits( 4954 resource.getRootPath()); 4955 } catch (ExecutionException e) { 4956 LOG.error(e.getLocalizedMessage(), e); 4957 } 4958 } 4959 List<CmsOrganizationalUnit> result = getVfsDriver(dbc).getResourceOus( 4960 dbc, 4961 dbc.currentProject().getUuid(), 4962 resource); 4963 4964 return result; 4965 } 4966 4967 /** 4968 * Returns all resources of the given organizational unit.<p> 4969 * 4970 * @param dbc the current db context 4971 * @param orgUnit the organizational unit to get all resources for 4972 * 4973 * @return all <code>{@link CmsResource}</code> objects in the organizational unit 4974 * 4975 * @throws CmsException if operation was not successful 4976 * 4977 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 4978 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 4979 * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean) 4980 */ 4981 public List<CmsResource> getResourcesForOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit) 4982 throws CmsException { 4983 4984 return getUserDriver(dbc).getResourcesForOrganizationalUnit(dbc, orgUnit); 4985 } 4986 4987 /** 4988 * Returns all resources associated to a given principal via an ACE with the given permissions.<p> 4989 * 4990 * If the <code>includeAttr</code> flag is set it returns also all resources associated to 4991 * a given principal through some of following attributes.<p> 4992 * 4993 * <ul> 4994 * <li>User Created</li> 4995 * <li>User Last Modified</li> 4996 * </ul><p> 4997 * 4998 * @param dbc the current database context 4999 * @param project the to read the entries from 5000 * @param principalId the id of the principal 5001 * @param permissions a set of permissions to match, can be <code>null</code> for all ACEs 5002 * @param includeAttr a flag to include resources associated by attributes 5003 * 5004 * @return a set of <code>{@link CmsResource}</code> objects 5005 * 5006 * @throws CmsException if something goes wrong 5007 */ 5008 public Set<CmsResource> getResourcesForPrincipal( 5009 CmsDbContext dbc, 5010 CmsProject project, 5011 CmsUUID principalId, 5012 CmsPermissionSet permissions, 5013 boolean includeAttr) 5014 throws CmsException { 5015 5016 Set<CmsResource> resources = new HashSet<CmsResource>( 5017 getVfsDriver(dbc).readResourcesForPrincipalACE(dbc, project, principalId)); 5018 if (permissions != null) { 5019 Iterator<CmsResource> itRes = resources.iterator(); 5020 while (itRes.hasNext()) { 5021 CmsAccessControlEntry ace = readAccessControlEntry(dbc, itRes.next(), principalId); 5022 if ((ace.getPermissions().getPermissions() 5023 & permissions.getPermissions()) != permissions.getPermissions()) { 5024 // remove if permissions does not match 5025 itRes.remove(); 5026 } 5027 } 5028 } 5029 if (includeAttr) { 5030 resources.addAll(getVfsDriver(dbc).readResourcesForPrincipalAttr(dbc, project, principalId)); 5031 } 5032 return resources; 5033 } 5034 5035 /** 5036 * Gets the rewrite aliases matching a given filter.<p> 5037 * 5038 * @param dbc the current database context 5039 * @param filter the filter used for filtering rewrite aliases 5040 * 5041 * @return the rewrite aliases matching the given filter 5042 * 5043 * @throws CmsException if something goes wrong 5044 */ 5045 public List<CmsRewriteAlias> getRewriteAliases(CmsDbContext dbc, CmsRewriteAliasFilter filter) throws CmsException { 5046 5047 return getVfsDriver(dbc).readRewriteAliases(dbc, filter); 5048 } 5049 5050 /** 5051 * Collects the groups which constitute a given role.<p> 5052 * 5053 * @param dbc the database context 5054 * @param roleGroupName the group related to the role 5055 * @param directUsersOnly if true, only the group belonging to the entry itself wil 5056 * 5057 * @return the set of groups which constitute the role 5058 * 5059 * @throws CmsException if something goes wrong 5060 */ 5061 public Set<CmsGroup> getRoleGroups(CmsDbContext dbc, String roleGroupName, boolean directUsersOnly) 5062 throws CmsException { 5063 5064 return getRoleGroupsImpl(dbc, roleGroupName, directUsersOnly, new HashMap<String, Set<CmsGroup>>()); 5065 } 5066 5067 /** 5068 * Collects the groups which constitute a given role.<p> 5069 * 5070 * @param dbc the database context 5071 * @param roleGroupName the group related to the role 5072 * @param directUsersOnly if true, only the group belonging to the entry itself wil 5073 * @param accumulator a map for memoizing return values of recursive calls 5074 * 5075 * @return the set of groups which constitute the role 5076 * 5077 * @throws CmsException if something goes wrong 5078 */ 5079 public Set<CmsGroup> getRoleGroupsImpl( 5080 CmsDbContext dbc, 5081 String roleGroupName, 5082 boolean directUsersOnly, 5083 Map<String, Set<CmsGroup>> accumulator) 5084 throws CmsException { 5085 5086 Set<CmsGroup> result = new HashSet<CmsGroup>(); 5087 if (accumulator.get(roleGroupName) != null) { 5088 return accumulator.get(roleGroupName); 5089 } 5090 CmsGroup group = readGroup(dbc, roleGroupName); // check that the group really exists 5091 if ((group == null) || (!group.isRole())) { 5092 throw new CmsDbEntryNotFoundException( 5093 Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, roleGroupName)); 5094 } 5095 result.add(group); 5096 if (!directUsersOnly) { 5097 CmsRole role = CmsRole.valueOf(group); 5098 if (role.getParentRole() != null) { 5099 try { 5100 String parentGroup = role.getParentRole().getGroupName(); 5101 // iterate the parent roles 5102 result.addAll(getRoleGroupsImpl(dbc, parentGroup, directUsersOnly, accumulator)); 5103 } catch (CmsDbEntryNotFoundException e) { 5104 // ignore, this may happen while deleting an orgunit 5105 if (LOG.isDebugEnabled()) { 5106 LOG.debug(e.getLocalizedMessage(), e); 5107 } 5108 } 5109 } 5110 String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn()); 5111 if (parentOu != null) { 5112 // iterate the parent ou's 5113 result.addAll(getRoleGroupsImpl(dbc, parentOu + group.getSimpleName(), directUsersOnly, accumulator)); 5114 } 5115 } 5116 accumulator.put(roleGroupName, result); 5117 return result; 5118 } 5119 5120 /** 5121 * Returns all roles the given user has for the given resource.<p> 5122 * 5123 * @param dbc the current database context 5124 * @param user the user to check 5125 * @param resource the resource to check the roles for 5126 * 5127 * @return a list of {@link CmsRole} objects 5128 * 5129 * @throws CmsException if something goes wrong 5130 */ 5131 public List<CmsRole> getRolesForResource(CmsDbContext dbc, CmsUser user, CmsResource resource) throws CmsException { 5132 5133 // guest user has no role 5134 if (user.isGuestUser()) { 5135 return Collections.emptyList(); 5136 } 5137 5138 // try to read from cache 5139 String key = user.getId().toString() + resource.getRootPath(); 5140 List<CmsRole> result = m_monitor.getCachedRoleList(key); 5141 if (result != null) { 5142 return result; 5143 } 5144 result = new ArrayList<CmsRole>(); 5145 5146 Iterator<CmsOrganizationalUnit> itOus = getResourceOrgUnits(dbc, resource).iterator(); 5147 while (itOus.hasNext()) { 5148 CmsOrganizationalUnit ou = itOus.next(); 5149 5150 // read all roles of the current user 5151 List<CmsGroup> groups = new ArrayList<CmsGroup>( 5152 getGroupsOfUser( 5153 dbc, 5154 user.getName(), 5155 ou.getName(), 5156 false, 5157 true, 5158 false, 5159 dbc.getRequestContext().getRemoteAddress())); 5160 // check the roles applying to the given resource 5161 Iterator<CmsGroup> it = groups.iterator(); 5162 while (it.hasNext()) { 5163 CmsGroup group = it.next(); 5164 CmsRole givenRole = CmsRole.valueOf(group).forOrgUnit(null); 5165 if (givenRole.isOrganizationalUnitIndependent() || result.contains(givenRole)) { 5166 // skip already added roles 5167 continue; 5168 } 5169 result.add(givenRole); 5170 } 5171 } 5172 5173 result = Collections.unmodifiableList(result); 5174 m_monitor.cacheRoleList(key, result); 5175 return result; 5176 } 5177 5178 /** 5179 * Returns all roles the given user has independent of the resource.<p> 5180 * 5181 * @param dbc the current database context 5182 * @param user the user to check 5183 * 5184 * @return a list of {@link CmsRole} objects 5185 * 5186 * @throws CmsException if something goes wrong 5187 */ 5188 public List<CmsRole> getRolesForUser(CmsDbContext dbc, CmsUser user) throws CmsException { 5189 5190 // guest user has no role 5191 if (user.isGuestUser()) { 5192 return Collections.emptyList(); 5193 } 5194 5195 // try to read from cache 5196 List<CmsRole> result = m_monitor.getGroupListCache().getBareRoles(user.getId()); 5197 if (result != null) { 5198 return result; 5199 } 5200 result = new ArrayList<CmsRole>(); 5201 5202 // read all roles of the current user 5203 List<CmsGroup> groups = new ArrayList<CmsGroup>( 5204 getGroupsOfUser(dbc, user.getName(), "", true, true, false, dbc.getRequestContext().getRemoteAddress())); 5205 5206 // check the roles applying to the given resource 5207 Iterator<CmsGroup> it = groups.iterator(); 5208 while (it.hasNext()) { 5209 CmsGroup group = it.next(); 5210 CmsRole givenRole = CmsRole.valueOf(group); 5211 givenRole = givenRole.forOrgUnit(null); 5212 if (!result.contains(givenRole)) { 5213 result.add(givenRole); 5214 } 5215 } 5216 result = Collections.unmodifiableList(result); 5217 m_monitor.getGroupListCache().setBareRoles(user, result); 5218 return result; 5219 } 5220 5221 /** 5222 * Returns the security manager this driver manager belongs to.<p> 5223 * 5224 * @return the security manager this driver manager belongs to 5225 */ 5226 public CmsSecurityManager getSecurityManager() { 5227 5228 return m_securityManager; 5229 } 5230 5231 /** 5232 * Returns an instance of the common sql manager.<p> 5233 * 5234 * @return an instance of the common sql manager 5235 */ 5236 public CmsSqlManager getSqlManager() { 5237 5238 return m_sqlManager; 5239 } 5240 5241 /** 5242 * Returns the subscription driver of this driver manager.<p> 5243 * 5244 * @return a subscription driver 5245 */ 5246 public I_CmsSubscriptionDriver getSubscriptionDriver() { 5247 5248 return m_subscriptionDriver; 5249 } 5250 5251 /** 5252 * Returns the user driver.<p> 5253 * 5254 * @return the user driver 5255 */ 5256 public I_CmsUserDriver getUserDriver() { 5257 5258 return m_userDriver; 5259 } 5260 5261 /** 5262 * Returns the user driver for a given database context.<p> 5263 * 5264 * @param dbc the database context 5265 * 5266 * @return the user driver for the database context 5267 */ 5268 public I_CmsUserDriver getUserDriver(CmsDbContext dbc) { 5269 5270 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5271 return m_userDriver; 5272 } 5273 I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId()); 5274 return driver != null ? driver : m_userDriver; 5275 5276 } 5277 5278 /** 5279 * Returns either the user driver for the given DB context (if it has one) or a default value instead.<p> 5280 * 5281 * @param dbc the DB context 5282 * @param defaultDriver the driver that should be returned if no driver for the DB context was found 5283 * 5284 * @return either the user driver for the DB context, or <code>defaultDriver</code> if none were found 5285 */ 5286 public I_CmsUserDriver getUserDriver(CmsDbContext dbc, I_CmsUserDriver defaultDriver) { 5287 5288 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5289 return defaultDriver; 5290 } 5291 I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId()); 5292 return driver != null ? driver : defaultDriver; 5293 } 5294 5295 /** 5296 * Returns all direct users of the given organizational unit.<p> 5297 * 5298 * @param dbc the current db context 5299 * @param orgUnit the organizational unit to get all users for 5300 * @param recursive if all groups of sub-organizational units should be retrieved too 5301 * 5302 * @return all <code>{@link CmsUser}</code> objects in the organizational unit 5303 * 5304 * @throws CmsException if operation was not successful 5305 * 5306 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5307 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5308 */ 5309 public List<CmsUser> getUsers(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, boolean recursive) 5310 throws CmsException { 5311 5312 return getUserDriver(dbc).getUsers(dbc, orgUnit, recursive); 5313 } 5314 5315 /** 5316 * Returns a list of users in a group.<p> 5317 * 5318 * @param dbc the current database context 5319 * @param groupname the name of the group to list users from 5320 * @param includeOtherOuUsers include users of other organizational units 5321 * @param directUsersOnly if set only the direct assigned users will be returned, 5322 * if not also indirect users, ie. members of parent roles, 5323 * this parameter only works with roles 5324 * @param readRoles if to read roles or groups 5325 * 5326 * @return all <code>{@link CmsUser}</code> objects in the group 5327 * 5328 * @throws CmsException if operation was not successful 5329 */ 5330 public List<CmsUser> getUsersOfGroup( 5331 CmsDbContext dbc, 5332 String groupname, 5333 boolean includeOtherOuUsers, 5334 boolean directUsersOnly, 5335 boolean readRoles) 5336 throws CmsException { 5337 5338 return internalUsersOfGroup( 5339 dbc, 5340 CmsOrganizationalUnit.getParentFqn(groupname), 5341 groupname, 5342 includeOtherOuUsers, 5343 directUsersOnly, 5344 readRoles); 5345 } 5346 5347 /** 5348 * Returns the given user's publish list.<p> 5349 * 5350 * @param dbc the database context 5351 * @param userId the user's id 5352 * 5353 * @return the given user's publish list 5354 * 5355 * @throws CmsDataAccessException if something goes wrong 5356 */ 5357 public List<CmsResource> getUsersPubList(CmsDbContext dbc, CmsUUID userId) throws CmsDataAccessException { 5358 5359 synchronized (m_publishListUpdateLock) { 5360 updateLog(dbc); 5361 return m_projectDriver.getUsersPubList(dbc, userId); 5362 } 5363 } 5364 5365 /** 5366 * Returns all direct users of the given organizational unit, without their additional info.<p> 5367 * 5368 * @param dbc the current db context 5369 * @param orgUnit the organizational unit to get all users for 5370 * @param recursive if all groups of sub-organizational units should be retrieved too 5371 * 5372 * @return all <code>{@link CmsUser}</code> objects in the organizational unit 5373 * 5374 * @throws CmsException if operation was not successful 5375 * 5376 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5377 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5378 */ 5379 public List<CmsUser> getUsersWithoutAdditionalInfo( 5380 CmsDbContext dbc, 5381 CmsOrganizationalUnit orgUnit, 5382 boolean recursive) 5383 throws CmsException { 5384 5385 return getUserDriver(dbc).getUsersWithoutAdditionalInfo(dbc, orgUnit, recursive); 5386 } 5387 5388 /** 5389 * Returns the VFS driver.<p> 5390 * 5391 * @return the VFS driver 5392 */ 5393 public I_CmsVfsDriver getVfsDriver() { 5394 5395 return m_vfsDriver; 5396 } 5397 5398 /** 5399 * Returns the VFS driver for the given database context.<p> 5400 * 5401 * @param dbc the database context 5402 * 5403 * @return a VFS driver 5404 */ 5405 public I_CmsVfsDriver getVfsDriver(CmsDbContext dbc) { 5406 5407 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5408 return m_vfsDriver; 5409 } 5410 I_CmsVfsDriver driver = dbc.getVfsDriver(dbc.getProjectId()); 5411 return driver != null ? driver : m_vfsDriver; 5412 5413 } 5414 5415 /** 5416 * Writes a vector of access control entries as new access control entries of a given resource.<p> 5417 * 5418 * Already existing access control entries of this resource are removed before. 5419 * Access is granted, if:<p> 5420 * <ul> 5421 * <li>the current user has control permission on the resource</li> 5422 * </ul> 5423 * 5424 * @param dbc the current database context 5425 * @param resource the resource 5426 * @param acEntries a list of <code>{@link CmsAccessControlEntry}</code> objects 5427 * 5428 * @throws CmsException if something goes wrong 5429 */ 5430 public void importAccessControlEntries( 5431 CmsDbContext dbc, 5432 CmsResource resource, 5433 List<CmsAccessControlEntry> acEntries) 5434 throws CmsException { 5435 5436 I_CmsUserDriver userDriver = getUserDriver(dbc); 5437 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId()); 5438 List<CmsAccessControlEntry> fixedAces = new ArrayList<>(); 5439 for (CmsAccessControlEntry entry : acEntries) { 5440 if (entry.getResource() == null) { 5441 entry = new CmsAccessControlEntry( 5442 resource.getResourceId(), 5443 entry.getPrincipal(), 5444 entry.getPermissions(), 5445 entry.getFlags()); 5446 } 5447 fixedAces.add(entry); 5448 } 5449 5450 Iterator<CmsAccessControlEntry> i = fixedAces.iterator(); 5451 while (i.hasNext()) { 5452 userDriver.writeAccessControlEntry(dbc, dbc.currentProject(), i.next()); 5453 } 5454 m_monitor.clearAccessControlListCache(); 5455 } 5456 5457 /** 5458 * Imports a rewrite alias.<p> 5459 * 5460 * @param dbc the database context 5461 * @param siteRoot the site root of the alias 5462 * @param source the source of the alias 5463 * @param target the target of the alias 5464 * @param mode the alias mode 5465 * 5466 * @return the import result 5467 * 5468 * @throws CmsException if something goes wrong 5469 */ 5470 public CmsAliasImportResult importRewriteAlias( 5471 CmsDbContext dbc, 5472 String siteRoot, 5473 String source, 5474 String target, 5475 CmsAliasMode mode) 5476 throws CmsException { 5477 5478 I_CmsVfsDriver vfs = getVfsDriver(dbc); 5479 List<CmsRewriteAlias> existingAliases = vfs.readRewriteAliases( 5480 dbc, 5481 new CmsRewriteAliasFilter().setSiteRoot(siteRoot)); 5482 CmsUUID idToDelete = null; 5483 for (CmsRewriteAlias alias : existingAliases) { 5484 if (alias.getPatternString().equals(source)) { 5485 idToDelete = alias.getId(); 5486 } 5487 } 5488 if (idToDelete != null) { 5489 vfs.deleteRewriteAliases(dbc, new CmsRewriteAliasFilter().setId(idToDelete)); 5490 } 5491 CmsRewriteAlias alias = new CmsRewriteAlias(new CmsUUID(), siteRoot, source, target, mode); 5492 List<CmsRewriteAlias> aliases = new ArrayList<CmsRewriteAlias>(); 5493 aliases.add(alias); 5494 getVfsDriver(dbc).insertRewriteAliases(dbc, aliases); 5495 CmsAliasImportResult result = new CmsAliasImportResult( 5496 CmsAliasImportStatus.aliasNew, 5497 "OK", 5498 source, 5499 target, 5500 mode); 5501 return result; 5502 } 5503 5504 /** 5505 * Creates a new user by import.<p> 5506 * 5507 * @param dbc the current database context 5508 * @param id the id of the user 5509 * @param name the new name for the user 5510 * @param password the new password for the user (already encrypted) 5511 * @param firstname the firstname of the user 5512 * @param lastname the lastname of the user 5513 * @param email the email of the user 5514 * @param flags the flags for a user (for example <code>{@link I_CmsPrincipal#FLAG_ENABLED}</code>) 5515 * @param dateCreated the creation date 5516 * @param additionalInfos the additional user infos 5517 * 5518 * @return the imported user 5519 * 5520 * @throws CmsException if something goes wrong 5521 */ 5522 public CmsUser importUser( 5523 CmsDbContext dbc, 5524 String id, 5525 String name, 5526 String password, 5527 String firstname, 5528 String lastname, 5529 String email, 5530 int flags, 5531 long dateCreated, 5532 Map<String, Object> additionalInfos) 5533 throws CmsException { 5534 5535 // no space before or after the name 5536 name = name.trim(); 5537 // check the user name 5538 String userName = CmsOrganizationalUnit.getSimpleName(name); 5539 OpenCms.getValidationHandler().checkUserName(userName); 5540 if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) { 5541 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName)); 5542 } 5543 // check the ou 5544 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 5545 5546 // check webuser ou 5547 if (ou.hasFlagWebuser() && ((flags & I_CmsPrincipal.FLAG_USER_WEBUSER) == 0)) { 5548 flags += I_CmsPrincipal.FLAG_USER_WEBUSER; 5549 } 5550 CmsUser newUser = getUserDriver(dbc).createUser( 5551 dbc, 5552 new CmsUUID(id), 5553 name, 5554 password, 5555 firstname, 5556 lastname, 5557 email, 5558 0, 5559 flags, 5560 dateCreated, 5561 additionalInfos); 5562 return newUser; 5563 } 5564 5565 /** 5566 * Increments a counter and returns its value before incrementing.<p> 5567 * 5568 * @param dbc the current database context 5569 * @param name the name of the counter which should be incremented 5570 * 5571 * @return the value of the counter 5572 * 5573 * @throws CmsException if something goes wrong 5574 */ 5575 public int incrementCounter(CmsDbContext dbc, String name) throws CmsException { 5576 5577 return getVfsDriver(dbc).incrementCounter(dbc, name); 5578 } 5579 5580 /** 5581 * Initializes the driver and sets up all required modules and connections.<p> 5582 * 5583 * @param configurationManager the configuration manager 5584 * @param dbContextFactory the db context factory 5585 * 5586 * @throws CmsException if something goes wrong 5587 * @throws Exception if something goes wrong 5588 */ 5589 public void init(CmsConfigurationManager configurationManager, I_CmsDbContextFactory dbContextFactory) 5590 throws CmsException, Exception { 5591 5592 // initialize the access-module. 5593 if (CmsLog.INIT.isInfoEnabled()) { 5594 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE4_0)); 5595 } 5596 // store local reference to the memory monitor to avoid multiple lookups through the OpenCms singelton 5597 m_monitor = OpenCms.getMemoryMonitor(); 5598 5599 CmsSystemConfiguration systemConfiguation = (CmsSystemConfiguration)configurationManager.getConfiguration( 5600 CmsSystemConfiguration.class); 5601 CmsCacheSettings settings = systemConfiguation.getCacheSettings(); 5602 5603 // initialize the key generator 5604 m_keyGenerator = (I_CmsCacheKey)Class.forName(settings.getCacheKeyGenerator()).newInstance(); 5605 5606 // initialize the HTML link validator 5607 m_htmlLinkValidator = new CmsRelationSystemValidator(this); 5608 5609 // fills the defaults if needed 5610 CmsDbContext dbc1 = dbContextFactory.getDbContext(); 5611 getUserDriver().fillDefaults(dbc1); 5612 getProjectDriver().fillDefaults(dbc1); 5613 5614 // set the driver manager in the publish engine 5615 m_publishEngine.setDriverManager(this); 5616 // create the root organizational unit if needed 5617 CmsDbContext dbc2 = dbContextFactory.getDbContext( 5618 new CmsRequestContext( 5619 readUser(dbc1, OpenCms.getDefaultUsers().getUserAdmin()), 5620 readProject(dbc1, CmsProject.ONLINE_PROJECT_ID), 5621 null, 5622 CmsSiteMatcher.DEFAULT_MATCHER, 5623 "", 5624 false, 5625 null, 5626 null, 5627 null, 5628 0, 5629 null, 5630 null, 5631 "", 5632 false)); 5633 dbc1.clear(); 5634 getUserDriver().createRootOrganizationalUnit(dbc2); 5635 dbc2.clear(); 5636 } 5637 5638 /** 5639 * Initializes the organizational unit.<p> 5640 * 5641 * @param dbc the DB context 5642 * @param ou the organizational unit 5643 */ 5644 public void initOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit ou) { 5645 5646 try { 5647 dbc.setAttribute(ATTR_INIT_OU, ou); 5648 m_userDriver.fillDefaults(dbc); 5649 } finally { 5650 dbc.removeAttribute(ATTR_INIT_OU); 5651 } 5652 } 5653 5654 /** 5655 * Checks if the specified resource is inside the current project.<p> 5656 * 5657 * The project "view" is determined by a set of path prefixes. 5658 * If the resource starts with any one of this prefixes, it is considered to 5659 * be "inside" the project.<p> 5660 * 5661 * @param dbc the current database context 5662 * @param resourcename the specified resource name (full path) 5663 * 5664 * @return <code>true</code>, if the specified resource is inside the current project 5665 */ 5666 public boolean isInsideCurrentProject(CmsDbContext dbc, String resourcename) { 5667 5668 List<String> projectResources = null; 5669 try { 5670 projectResources = readProjectResources(dbc, dbc.currentProject()); 5671 } catch (CmsException e) { 5672 if (LOG.isErrorEnabled()) { 5673 LOG.error( 5674 Messages.get().getBundle().key( 5675 Messages.LOG_CHECK_RESOURCE_INSIDE_CURRENT_PROJECT_2, 5676 resourcename, 5677 dbc.currentProject().getName()), 5678 e); 5679 } 5680 return false; 5681 } 5682 return CmsProject.isInsideProject(projectResources, resourcename); 5683 } 5684 5685 /** 5686 * Checks whether the subscription driver is available.<p> 5687 * 5688 * @return true if the subscription driver is available 5689 */ 5690 public boolean isSubscriptionDriverAvailable() { 5691 5692 return m_subscriptionDriver != null; 5693 } 5694 5695 /** 5696 * Checks if a project is the tempfile project.<p> 5697 * @param project the project to test 5698 * @return true if the project is the tempfile project 5699 */ 5700 public boolean isTempfileProject(CmsProject project) { 5701 5702 return project.getName().equals("tempFileProject"); 5703 } 5704 5705 /** 5706 * Checks if one of the resources (except the resource itself) 5707 * is a sibling in a "labeled" site folder.<p> 5708 * 5709 * This method is used when creating a new sibling 5710 * (use the <code>newResource</code> parameter & <code>action = 1</code>) 5711 * or deleting/importing a resource (call with <code>action = 2</code>).<p> 5712 * 5713 * @param dbc the current database context 5714 * @param resource the resource 5715 * @param newResource absolute path for a resource sibling which will be created 5716 * @param action the action which has to be performed (1: create VFS link, 2: all other actions) 5717 * 5718 * @return <code>true</code> if the flag should be set for the resource, otherwise <code>false</code> 5719 * 5720 * @throws CmsDataAccessException if something goes wrong 5721 */ 5722 public boolean labelResource(CmsDbContext dbc, CmsResource resource, String newResource, int action) 5723 throws CmsDataAccessException { 5724 5725 // get the list of labeled site folders from the runtime property 5726 List<String> labeledSites = OpenCms.getWorkplaceManager().getLabelSiteFolders(); 5727 5728 if (labeledSites.size() == 0) { 5729 // no labeled sites defined, just return false 5730 return false; 5731 } 5732 5733 if (action == 1) { 5734 // CASE 1: a new resource is created, check the sites 5735 if (!resource.isLabeled()) { 5736 // source isn't labeled yet, so check! 5737 boolean linkInside = false; 5738 boolean sourceInside = false; 5739 for (int i = 0; i < labeledSites.size(); i++) { 5740 String curSite = labeledSites.get(i); 5741 if (newResource.startsWith(curSite)) { 5742 // the link lies in a labeled site 5743 linkInside = true; 5744 } 5745 if (resource.getRootPath().startsWith(curSite)) { 5746 // the source lies in a labeled site 5747 sourceInside = true; 5748 } 5749 if (linkInside && sourceInside) { 5750 break; 5751 } 5752 } 5753 // return true when either source or link is in labeled site, otherwise false 5754 return (linkInside != sourceInside); 5755 } 5756 // resource is already labeled 5757 return false; 5758 5759 } else { 5760 // CASE 2: the resource will be deleted or created (import) 5761 // check if at least one of the other siblings resides inside a "labeled site" 5762 // and if at least one of the other siblings resides outside a "labeled site" 5763 boolean isInside = false; 5764 boolean isOutside = false; 5765 // check if one of the other vfs links lies in a labeled site folder 5766 List<CmsResource> siblings = getVfsDriver( 5767 dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, false); 5768 updateContextDates(dbc, siblings); 5769 Iterator<CmsResource> i = siblings.iterator(); 5770 while (i.hasNext() && (!isInside || !isOutside)) { 5771 CmsResource currentResource = i.next(); 5772 if (currentResource.equals(resource)) { 5773 // dont't check the resource itself! 5774 continue; 5775 } 5776 String curPath = currentResource.getRootPath(); 5777 boolean curInside = false; 5778 for (int k = 0; k < labeledSites.size(); k++) { 5779 if (curPath.startsWith(labeledSites.get(k))) { 5780 // the link is in the labeled site 5781 isInside = true; 5782 curInside = true; 5783 break; 5784 } 5785 } 5786 if (!curInside) { 5787 // the current link was not found in labeled site, so it is outside 5788 isOutside = true; 5789 } 5790 } 5791 // now check the new resource name if present 5792 if (newResource != null) { 5793 boolean curInside = false; 5794 for (int k = 0; k < labeledSites.size(); k++) { 5795 if (newResource.startsWith(labeledSites.get(k))) { 5796 // the new resource is in the labeled site 5797 isInside = true; 5798 curInside = true; 5799 break; 5800 } 5801 } 5802 if (!curInside) { 5803 // the new resource was not found in labeled site, so it is outside 5804 isOutside = true; 5805 } 5806 } 5807 return (isInside && isOutside); 5808 } 5809 } 5810 5811 /** 5812 * Returns the user, who had locked the resource.<p> 5813 * 5814 * A user can lock a resource, so he is the only one who can write this 5815 * resource. This methods checks, if a resource was locked. 5816 * 5817 * @param dbc the current database context 5818 * @param resource the resource 5819 * 5820 * @return the user, who had locked the resource 5821 * 5822 * @throws CmsException will be thrown, if the user has not the rights for this resource 5823 */ 5824 public CmsUser lockedBy(CmsDbContext dbc, CmsResource resource) throws CmsException { 5825 5826 return readUser(dbc, m_lockManager.getLock(dbc, resource).getEditionLock().getUserId()); 5827 } 5828 5829 /** 5830 * Locks a resource.<p> 5831 * 5832 * The <code>type</code> parameter controls what kind of lock is used.<br> 5833 * Possible values for this parameter are: <br> 5834 * <ul> 5835 * <li><code>{@link org.opencms.lock.CmsLockType#EXCLUSIVE}</code></li> 5836 * <li><code>{@link org.opencms.lock.CmsLockType#TEMPORARY}</code></li> 5837 * <li><code>{@link org.opencms.lock.CmsLockType#PUBLISH}</code></li> 5838 * </ul><p> 5839 * 5840 * @param dbc the current database context 5841 * @param resource the resource to lock 5842 * @param type type of the lock 5843 * 5844 * @throws CmsException if something goes wrong 5845 * 5846 * @see CmsObject#lockResource(String) 5847 * @see CmsObject#lockResourceTemporary(String) 5848 * @see org.opencms.file.types.I_CmsResourceType#lockResource(CmsObject, CmsSecurityManager, CmsResource, CmsLockType) 5849 */ 5850 public void lockResource(CmsDbContext dbc, CmsResource resource, CmsLockType type) throws CmsException { 5851 5852 // update the resource cache 5853 m_monitor.clearResourceCache(); 5854 5855 CmsProject project = dbc.currentProject(); 5856 5857 // add the resource to the lock dispatcher 5858 m_lockManager.addResource(dbc, resource, dbc.currentUser(), project, type); 5859 boolean changedProjectLastModified = false; 5860 if (!resource.getState().isUnchanged() && !resource.getState().isKeep()) { 5861 // update the project flag of a modified resource as "last modified inside the current project" 5862 getVfsDriver(dbc).writeLastModifiedProjectId(dbc, project, project.getUuid(), resource); 5863 changedProjectLastModified = true; 5864 } 5865 5866 // we must also clear the permission cache 5867 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION); 5868 5869 // fire resource modification event 5870 Map<String, Object> data = new HashMap<String, Object>(2); 5871 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 5872 data.put( 5873 I_CmsEventListener.KEY_CHANGE, 5874 Integer.valueOf(changedProjectLastModified ? CHANGED_PROJECT : NOTHING_CHANGED)); 5875 data.put(I_CmsEventListener.KEY_SKIPINDEX, Boolean.TRUE); 5876 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 5877 } 5878 5879 /** 5880 * Adds the given log entry to the current user's log.<p> 5881 * 5882 * This operation works only on memory, to get the log entries actually 5883 * written to DB you have to call the {@link #updateLog(CmsDbContext)} method.<p> 5884 * 5885 * @param dbc the current database context 5886 * @param logEntry the log entry to create 5887 * @param force forces the log entry to be counted, 5888 * if not only the first log entry in a transaction will be taken into account 5889 */ 5890 public void log(CmsDbContext dbc, CmsLogEntry logEntry, boolean force) { 5891 5892 if (dbc == null) { 5893 return; 5894 } 5895 // check log level 5896 if (!logEntry.getType().isActive()) { 5897 // do not log inactive entries 5898 return; 5899 } 5900 // if not forcing 5901 if (!force) { 5902 // operation already logged 5903 boolean abort = (dbc.getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null); 5904 // disabled logging from outside 5905 abort |= (dbc.getRequestContext().getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null); 5906 if (abort) { 5907 return; 5908 } 5909 } 5910 // prevent several entries for the same operation 5911 dbc.setAttribute(CmsLogEntry.ATTR_LOG_ENTRY, Boolean.TRUE); 5912 // keep it for later 5913 m_log.add(logEntry); 5914 } 5915 5916 /** 5917 * Attempts to authenticate a user into OpenCms with the given password. 5918 * 5919 * <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, 5920 * while check mode merely checks the login details without firing the events normally fired during login, and without modifying the user. However, 5921 * in the case an incorrect password is given, the invalid login counter is still incremented. 5922 * 5923 * @param dbc the current database context 5924 * @param userName the name of the user to be logged in 5925 * @param password the password of the user 5926 * @param secondFactorInfo the second factor information for 2FA (may be null) 5927 * @param remoteAddress the ip address of the request 5928 * @param mode the mode to use (real login or check only) 5929 * 5930 * @return the logged in user 5931 * 5932 * @throws CmsAuthentificationException if the login was not successful 5933 * @throws CmsDataAccessException in case of errors accessing the database 5934 * @throws CmsPasswordEncryptionException in case of errors encrypting the users password 5935 */ 5936 public CmsUser loginUser( 5937 CmsDbContext dbc, 5938 String userName, 5939 String password, 5940 CmsSecondFactorInfo secondFactorInfo, 5941 String remoteAddress, 5942 LoginUserMode mode) 5943 throws CmsAuthentificationException, CmsDataAccessException, CmsPasswordEncryptionException { 5944 5945 if (CmsStringUtil.isEmptyOrWhitespaceOnly(password)) { 5946 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, userName)); 5947 } 5948 CmsUser newUser; 5949 CmsUser userCopy; 5950 try { 5951 // read the user from the driver to avoid the cache 5952 newUser = getUserDriver(dbc).readUser(dbc, userName, password, remoteAddress); 5953 userCopy = newUser.clone(); 5954 userName = newUser.getName(); 5955 5956 } catch (CmsDbEntryNotFoundException e) { 5957 // this indicates that the username / password combination does not exist 5958 // any other exception indicates database issues, these are not catched here 5959 5960 // check if a user with this name exists at all 5961 CmsUser user = null; 5962 try { 5963 user = readUser(dbc, userName); 5964 userName = user.getName(); 5965 } catch (CmsDataAccessException e2) { 5966 // apparently this user does not exist in the database 5967 } 5968 5969 if (user != null) { 5970 if (dbc.currentUser().isGuestUser()) { 5971 // add an invalid login attempt for this user to the storage 5972 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 5973 } 5974 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 5975 throw new CmsAuthentificationException( 5976 org.opencms.security.Messages.get().container( 5977 org.opencms.security.Messages.ERR_LOGIN_FAILED_2, 5978 userName, 5979 remoteAddress), 5980 e); 5981 } else { 5982 String userOu = CmsOrganizationalUnit.getParentFqn(userName); 5983 if (userOu != null) { 5984 String parentOu = CmsOrganizationalUnit.getParentFqn(userOu); 5985 if (parentOu != null) { 5986 // try a higher level ou 5987 String uName = CmsOrganizationalUnit.getSimpleName(userName); 5988 return loginUser(dbc, parentOu + uName, password, secondFactorInfo, remoteAddress, mode); 5989 } 5990 } 5991 throw new CmsAuthentificationException( 5992 org.opencms.security.Messages.get().container( 5993 org.opencms.security.Messages.ERR_LOGIN_FAILED_NO_USER_2, 5994 userName, 5995 remoteAddress), 5996 e); 5997 } 5998 } 5999 // check if the "enabled" flag is set for the user 6000 if (!newUser.isEnabled()) { 6001 // user is disabled, throw a securiy exception 6002 throw new CmsAuthentificationException( 6003 org.opencms.security.Messages.get().container( 6004 org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2, 6005 userName, 6006 remoteAddress)); 6007 } 6008 6009 if (mode == LoginUserMode.standard) { 6010 CmsTwoFactorAuthenticationHandler handler = OpenCms.getTwoFactorAuthenticationHandler(); 6011 if (handler.needsTwoFactorAuthentication(newUser)) { 6012 // note that password check must already have been successful at this stage 6013 6014 if (handler.hasSecondFactor(newUser)) { 6015 if (!handler.verifySecondFactor(newUser, secondFactorInfo)) { 6016 if (dbc.currentUser().isGuestUser()) { 6017 // add an invalid login attempt for this user to the storage 6018 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 6019 } 6020 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6021 throw new CmsAuthentificationException( 6022 org.opencms.security.Messages.get().container( 6023 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 6024 userName)); 6025 } 6026 } else { 6027 try { 6028 if (handler.setUpAndVerifySecondFactor(newUser, secondFactorInfo)) { 6029 LOG.info("Second factor setup successful for user " + newUser.getName()); 6030 } else { 6031 if (dbc.currentUser().isGuestUser()) { 6032 // add an invalid login attempt for this user to the storage 6033 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 6034 } 6035 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6036 throw new CmsAuthentificationException( 6037 org.opencms.security.Messages.get().container( 6038 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 6039 userName)); 6040 } 6041 } catch (CmsSecondFactorSetupException e) { 6042 throw new CmsAuthentificationException( 6043 org.opencms.security.Messages.get().container( 6044 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 6045 userName), 6046 e); 6047 } 6048 } 6049 } 6050 } 6051 if (dbc.currentUser().isGuestUser()) { 6052 // check if this account is temporarily disabled because of too many invalid login attempts 6053 // this will throw an exception if the test fails 6054 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 6055 if (mode == LoginUserMode.standard) { 6056 // test successful, remove all previous invalid login attempts for this user from the storage 6057 OpenCms.getLoginManager().removeInvalidLogins(userName, remoteAddress); 6058 } 6059 } 6060 6061 if (!m_securityManager.hasRole( 6062 dbc, 6063 newUser, 6064 CmsRole.ADMINISTRATOR.forOrgUnit(dbc.getRequestContext().getOuFqn()))) { 6065 // new user is not Administrator, check if login is currently allowed 6066 OpenCms.getLoginManager().checkLoginAllowed(); 6067 } 6068 6069 if (mode == LoginUserMode.standard) { 6070 6071 newUser.setLastlogin(System.currentTimeMillis()); 6072 m_monitor.clearUserCache(newUser); 6073 6074 // write the changed user object back to the user driver 6075 Map<String, Object> additionalInfosForRepositories = OpenCms.getRepositoryManager().getAdditionalInfoForLogin( 6076 newUser.getName(), 6077 password); 6078 boolean requiresAddInfoUpdate = false; 6079 6080 // check for changes 6081 for (Entry<String, Object> entry : additionalInfosForRepositories.entrySet()) { 6082 Object value = entry.getValue(); 6083 Object current = newUser.getAdditionalInfo(entry.getKey()); 6084 if (((value == null) && (current != null)) || ((value != null) && !value.equals(current))) { 6085 requiresAddInfoUpdate = true; 6086 break; 6087 } 6088 } 6089 if (requiresAddInfoUpdate) { 6090 newUser.getAdditionalInfo().putAll(additionalInfosForRepositories); 6091 } 6092 String lastPasswordChange = (String)newUser.getAdditionalInfo( 6093 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE); 6094 if (lastPasswordChange == null) { 6095 requiresAddInfoUpdate = true; 6096 newUser.getAdditionalInfo().put( 6097 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 6098 "" + System.currentTimeMillis()); 6099 } 6100 if (!requiresAddInfoUpdate) { 6101 dbc.setAttribute(ATTRIBUTE_LOGIN, newUser.getName()); 6102 } 6103 6104 if (mode == LoginUserMode.standard) { 6105 OpenCms.getTwoFactorAuthenticationHandler().trackUserChange(dbc.getRequestContext(), userCopy, newUser); 6106 getUserDriver(dbc).writeUser(dbc, newUser); 6107 } 6108 int changes = CmsUser.FLAG_LAST_LOGIN; 6109 6110 // check if we need to update the password 6111 if (!OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), false) 6112 && OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), true)) { 6113 // the password does not check with the current hash algorithm but with the fall back, update the password 6114 getUserDriver(dbc).writePassword(dbc, userName, password, password); 6115 changes = changes | CmsUser.FLAG_CORE_DATA; 6116 } 6117 6118 // update cache 6119 m_monitor.cacheUser(newUser); 6120 6121 // invalidate all user dependent caches 6122 m_monitor.flushCache( 6123 CmsMemoryMonitor.CacheType.ACL, 6124 CmsMemoryMonitor.CacheType.GROUP, 6125 CmsMemoryMonitor.CacheType.ORG_UNIT, 6126 CmsMemoryMonitor.CacheType.USER_LIST, 6127 CmsMemoryMonitor.CacheType.PERMISSION, 6128 CmsMemoryMonitor.CacheType.RESOURCE_LIST); 6129 6130 // fire user modified event 6131 Map<String, Object> eventData = new HashMap<String, Object>(); 6132 eventData.put(I_CmsEventListener.KEY_USER_ID, newUser.getId().toString()); 6133 eventData.put(I_CmsEventListener.KEY_USER_NAME, newUser.getName()); 6134 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 6135 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(changes)); 6136 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 6137 } 6138 6139 // return the user object read from the driver 6140 return newUser.clone(); 6141 } 6142 6143 /** 6144 * Lookup and read the user or group with the given UUID.<p> 6145 * 6146 * @param dbc the current database context 6147 * @param principalId the UUID of the principal to lookup 6148 * 6149 * @return the principal (group or user) if found, otherwise <code>null</code> 6150 */ 6151 public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, CmsUUID principalId) { 6152 6153 try { 6154 CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalId); 6155 if (group != null) { 6156 return group; 6157 } 6158 } catch (Exception e) { 6159 // ignore this exception 6160 } 6161 6162 try { 6163 CmsUser user = readUser(dbc, principalId); 6164 if (user != null) { 6165 return user; 6166 } 6167 } catch (Exception e) { 6168 // ignore this exception 6169 } 6170 6171 return null; 6172 } 6173 6174 /** 6175 * Lookup and read the user or group with the given name.<p> 6176 * 6177 * @param dbc the current database context 6178 * @param principalName the name of the principal to lookup 6179 * 6180 * @return the principal (group or user) if found, otherwise <code>null</code> 6181 */ 6182 public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, String principalName) { 6183 6184 try { 6185 CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalName); 6186 if (group != null) { 6187 return group; 6188 } 6189 } catch (Exception e) { 6190 // ignore this exception 6191 } 6192 6193 try { 6194 CmsUser user = readUser(dbc, principalName); 6195 if (user != null) { 6196 return user; 6197 } 6198 } catch (Exception e) { 6199 // ignore this exception 6200 } 6201 6202 return null; 6203 } 6204 6205 /** 6206 * Mark the given resource as visited by the user.<p> 6207 * 6208 * @param dbc the database context 6209 * @param poolName the name of the database pool to use 6210 * @param resource the resource to mark as visited 6211 * @param user the user that visited the resource 6212 * 6213 * @throws CmsException if something goes wrong 6214 */ 6215 public void markResourceAsVisitedBy(CmsDbContext dbc, String poolName, CmsResource resource, CmsUser user) 6216 throws CmsException { 6217 6218 getSubscriptionDriver().markResourceAsVisitedBy(dbc, poolName, resource, user); 6219 } 6220 6221 /** 6222 * Moves a resource.<p> 6223 * 6224 * You must ensure that the parent of the destination path is an absolute, valid and 6225 * existing VFS path. Relative paths from the source are not supported.<p> 6226 * 6227 * The moved resource will always be locked to the current user 6228 * after the move operation.<p> 6229 * 6230 * In case the target resource already exists, it will be overwritten with the 6231 * source resource if possible.<p> 6232 * 6233 * @param dbc the current database context 6234 * @param source the resource to move 6235 * @param destination the name of the move destination with complete path 6236 * @param internal if set nothing more than the path is modified 6237 * 6238 * @throws CmsException if something goes wrong 6239 * 6240 * @see CmsSecurityManager#moveResource(CmsRequestContext, CmsResource, String) 6241 */ 6242 public void moveResource(CmsDbContext dbc, CmsResource source, String destination, boolean internal) 6243 throws CmsException { 6244 6245 CmsFolder destinationFolder = readFolder(dbc, CmsResource.getParentFolder(destination), CmsResourceFilter.ALL); 6246 m_securityManager.checkPermissions( 6247 dbc, 6248 destinationFolder, 6249 CmsPermissionSet.ACCESS_WRITE, 6250 false, 6251 CmsResourceFilter.ALL); 6252 6253 if (source.isFolder()) { 6254 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 6255 } 6256 getVfsDriver(dbc).moveResource(dbc, dbc.getRequestContext().getCurrentProject().getUuid(), source, destination); 6257 6258 if (!internal) { 6259 CmsResourceState newState = CmsResource.STATE_CHANGED; 6260 if (source.getState().isNew()) { 6261 newState = CmsResource.STATE_NEW; 6262 } else if (source.getState().isDeleted()) { 6263 newState = CmsResource.STATE_DELETED; 6264 } 6265 source.setState(newState); 6266 // safe since this operation always uses the ids instead of the resource path 6267 getVfsDriver(dbc).writeResourceState( 6268 dbc, 6269 dbc.currentProject(), 6270 source, 6271 CmsDriverManager.UPDATE_STRUCTURE_STATE, 6272 false); 6273 // log it 6274 log( 6275 dbc, 6276 new CmsLogEntry( 6277 dbc, 6278 source.getStructureId(), 6279 CmsLogEntryType.RESOURCE_MOVED, 6280 new String[] {source.getRootPath(), destination}), 6281 false); 6282 } 6283 6284 CmsResource destRes = readResource(dbc, destination, CmsResourceFilter.ALL); 6285 // move lock 6286 m_lockManager.moveResource(source.getRootPath(), destRes.getRootPath()); 6287 6288 // flush all relevant caches 6289 m_monitor.clearAccessControlListCache(); 6290 m_monitor.flushCache( 6291 CmsMemoryMonitor.CacheType.PROPERTY, 6292 CmsMemoryMonitor.CacheType.PROPERTY_LIST, 6293 CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 6294 6295 List<CmsResource> resources = new ArrayList<CmsResource>(4); 6296 // source 6297 resources.add(source); 6298 try { 6299 resources.add(readFolder(dbc, CmsResource.getParentFolder(source.getRootPath()), CmsResourceFilter.ALL)); 6300 } catch (Exception e) { 6301 if (LOG.isDebugEnabled()) { 6302 LOG.debug(e.getLocalizedMessage(), e); 6303 } 6304 } 6305 // destination 6306 resources.add(destRes); 6307 resources.add(destinationFolder); 6308 6309 Map<String, Object> eventData = new HashMap<String, Object>(); 6310 eventData.put(I_CmsEventListener.KEY_RESOURCES, resources); 6311 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 6312 6313 // fire the events 6314 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MOVED, eventData)); 6315 } 6316 6317 /** 6318 * Moves a resource to the "lost and found" folder.<p> 6319 * 6320 * The method can also be used to check get the name of a resource 6321 * in the "lost and found" folder only without actually moving the 6322 * the resource. To do this, the <code>returnNameOnly</code> flag 6323 * must be set to <code>true</code>.<p> 6324 * 6325 * @param dbc the current database context 6326 * @param resource the resource to apply this operation to 6327 * @param returnNameOnly if <code>true</code>, only the name of the resource in the "lost and found" 6328 * folder is returned, the move operation is not really performed 6329 * 6330 * @return the name of the resource inside the "lost and found" folder 6331 * 6332 * @throws CmsException if something goes wrong 6333 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 6334 * 6335 * @see CmsObject#moveToLostAndFound(String) 6336 * @see CmsObject#getLostAndFoundName(String) 6337 */ 6338 public String moveToLostAndFound(CmsDbContext dbc, CmsResource resource, boolean returnNameOnly) 6339 throws CmsException, CmsIllegalArgumentException { 6340 6341 String resourcename = dbc.removeSiteRoot(resource.getRootPath()); 6342 6343 String siteRoot = dbc.getRequestContext().getSiteRoot(); 6344 dbc.getRequestContext().setSiteRoot(""); 6345 String destination = CmsDriverManager.LOST_AND_FOUND_FOLDER + resourcename; 6346 // create the required folders if necessary 6347 try { 6348 // collect all folders... 6349 String folderPath = CmsResource.getParentFolder(destination); 6350 folderPath = folderPath.substring(1, folderPath.length() - 1); // cut out leading and trailing '/' 6351 Iterator<String> folders = CmsStringUtil.splitAsList(folderPath, '/').iterator(); 6352 // ...now create them.... 6353 folderPath = "/"; 6354 while (folders.hasNext()) { 6355 folderPath += folders.next().toString() + "/"; 6356 try { 6357 readFolder(dbc, folderPath, CmsResourceFilter.IGNORE_EXPIRATION); 6358 } catch (Exception e1) { 6359 if (returnNameOnly) { 6360 // we can use the original name without risk, and we do not need to recreate the parent folders 6361 break; 6362 } 6363 // the folder is not existing, so create it 6364 createResource( 6365 dbc, 6366 folderPath, 6367 CmsResourceTypeFolder.RESOURCE_TYPE_ID, 6368 null, 6369 new ArrayList<CmsProperty>()); 6370 } 6371 } 6372 // check if this resource name does already exist 6373 // if so add a postfix to the name 6374 String des = destination; 6375 int postfix = 1; 6376 boolean found = true; 6377 while (found) { 6378 try { 6379 // try to read the file..... 6380 found = true; 6381 readResource(dbc, des, CmsResourceFilter.ALL); 6382 // ....it's there, so add a postfix and try again 6383 String path = destination.substring(0, destination.lastIndexOf('/') + 1); 6384 String filename = destination.substring(destination.lastIndexOf('/') + 1, destination.length()); 6385 6386 des = path; 6387 6388 if (filename.lastIndexOf('.') > 0) { 6389 des += filename.substring(0, filename.lastIndexOf('.')); 6390 } else { 6391 des += filename; 6392 } 6393 des += "_" + postfix; 6394 if (filename.lastIndexOf('.') > 0) { 6395 des += filename.substring(filename.lastIndexOf('.'), filename.length()); 6396 } 6397 postfix++; 6398 } catch (CmsException e3) { 6399 // the file does not exist, so we can use this filename 6400 found = false; 6401 } 6402 } 6403 destination = des; 6404 6405 if (!returnNameOnly) { 6406 // do not use the move semantic here! to prevent links pointing to the lost & found folder 6407 copyResource(dbc, resource, destination, CmsResource.COPY_AS_SIBLING); 6408 deleteResource(dbc, resource, CmsResource.DELETE_PRESERVE_SIBLINGS); 6409 } 6410 } catch (CmsException e2) { 6411 throw e2; 6412 } finally { 6413 // set the site root to the old value again 6414 dbc.getRequestContext().setSiteRoot(siteRoot); 6415 } 6416 return destination; 6417 } 6418 6419 /** 6420 * Gets a new driver instance.<p> 6421 * 6422 * @param dbc the database context 6423 * @param configurationManager the configuration manager 6424 * @param driverName the driver name 6425 * @param successiveDrivers the list of successive drivers 6426 * 6427 * @return the driver object 6428 * @throws CmsInitException if the selected driver could not be initialized 6429 */ 6430 public Object newDriverInstance( 6431 CmsDbContext dbc, 6432 CmsConfigurationManager configurationManager, 6433 String driverName, 6434 List<String> successiveDrivers) 6435 throws CmsInitException { 6436 6437 Class<?> driverClass = null; 6438 I_CmsDriver driver = null; 6439 6440 try { 6441 // try to get the class 6442 driverClass = Class.forName(driverName); 6443 if (CmsLog.INIT.isInfoEnabled()) { 6444 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName)); 6445 } 6446 6447 // try to create a instance 6448 driver = (I_CmsDriver)driverClass.newInstance(); 6449 if (CmsLog.INIT.isInfoEnabled()) { 6450 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName)); 6451 } 6452 6453 // invoke the init-method of this access class 6454 driver.init(dbc, configurationManager, successiveDrivers, this); 6455 if (CmsLog.INIT.isInfoEnabled()) { 6456 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_0)); 6457 } 6458 6459 } catch (Throwable t) { 6460 CmsMessageContainer message = Messages.get().container( 6461 Messages.ERR_ERROR_INITIALIZING_DRIVER_1, 6462 driverName); 6463 if (LOG.isErrorEnabled()) { 6464 LOG.error(message.key(), t); 6465 } 6466 throw new CmsInitException(message, t); 6467 } 6468 6469 return driver; 6470 } 6471 6472 /** 6473 * Method to create a new instance of a driver.<p> 6474 * 6475 * @param configuration the configurations from the propertyfile 6476 * @param driverName the class name of the driver 6477 * @param driverPoolUrl the pool url for the driver 6478 * @return an initialized instance of the driver 6479 * @throws CmsException if something goes wrong 6480 */ 6481 public Object newDriverInstance(CmsParameterConfiguration configuration, String driverName, String driverPoolUrl) 6482 throws CmsException { 6483 6484 Class<?>[] initParamClasses = {CmsParameterConfiguration.class, String.class, CmsDriverManager.class}; 6485 Object[] initParams = {configuration, driverPoolUrl, this}; 6486 6487 Class<?> driverClass = null; 6488 Object driver = null; 6489 6490 try { 6491 // try to get the class 6492 driverClass = Class.forName(driverName); 6493 if (CmsLog.INIT.isInfoEnabled()) { 6494 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName)); 6495 } 6496 6497 // try to create a instance 6498 driver = driverClass.newInstance(); 6499 if (CmsLog.INIT.isInfoEnabled()) { 6500 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName)); 6501 } 6502 6503 // invoke the init-method of this access class 6504 driver.getClass().getMethod("init", initParamClasses).invoke(driver, initParams); 6505 if (CmsLog.INIT.isInfoEnabled()) { 6506 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_1, driverPoolUrl)); 6507 } 6508 6509 } catch (Exception exc) { 6510 6511 CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_DRIVER_MANAGER_1); 6512 if (LOG.isFatalEnabled()) { 6513 LOG.fatal(message.key(), exc); 6514 } 6515 throw new CmsDbException(message, exc); 6516 6517 } 6518 6519 return driver; 6520 } 6521 6522 /** 6523 * Method to create a new instance of a pool.<p> 6524 * 6525 * @param configuration the configurations from the propertyfile 6526 * @param poolName the configuration name of the pool 6527 * 6528 * @throws CmsInitException if the pools could not be initialized 6529 */ 6530 public void newPoolInstance(CmsParameterConfiguration configuration, String poolName) throws CmsInitException { 6531 6532 CmsDbPoolV11 pool; 6533 6534 try { 6535 pool = new CmsDbPoolV11(configuration, poolName); 6536 } catch (Exception e) { 6537 6538 CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_CONN_POOL_1, poolName); 6539 if (LOG.isErrorEnabled()) { 6540 LOG.error(message.key(), e); 6541 } 6542 throw new CmsInitException(message, e); 6543 } 6544 addPool(pool); 6545 } 6546 6547 /** 6548 * Publishes the given publish job.<p> 6549 * 6550 * @param cms the cms context 6551 * @param dbc the db context 6552 * @param publishList the list of resources to publish 6553 * @param report the report to write to 6554 * 6555 * @throws CmsException if something goes wrong 6556 */ 6557 public void publishJob(CmsObject cms, CmsDbContext dbc, CmsPublishList publishList, I_CmsReport report) 6558 throws CmsException { 6559 6560 try { 6561 // check state and lock 6562 List<CmsResource> allResources = new ArrayList<CmsResource>(publishList.getFolderList()); 6563 allResources.addAll(publishList.getDeletedFolderList()); 6564 allResources.addAll(publishList.getFileList()); 6565 Iterator<CmsResource> itResources = allResources.iterator(); 6566 while (itResources.hasNext()) { 6567 CmsResource resource = itResources.next(); 6568 try { 6569 resource = readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL); 6570 } catch (CmsVfsResourceNotFoundException e) { 6571 continue; 6572 } 6573 if (resource.getState().isUnchanged()) { 6574 // remove files that were published by a concurrent job 6575 if (LOG.isDebugEnabled()) { 6576 LOG.debug( 6577 Messages.get().getBundle().key( 6578 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6579 dbc.removeSiteRoot(resource.getRootPath()))); 6580 } 6581 publishList.remove(resource); 6582 unlockResource(dbc, resource, true, true); 6583 continue; 6584 } 6585 if (!CmsModificationContext.isInOnlineFolder(resource.getRootPath())) { 6586 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6587 if (!lock.getSystemLock().isPublish()) { 6588 // remove files that are not locked for publishing 6589 if (LOG.isDebugEnabled()) { 6590 LOG.debug( 6591 Messages.get().getBundle().key( 6592 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6593 dbc.removeSiteRoot(resource.getRootPath()))); 6594 } 6595 publishList.remove(resource); 6596 continue; 6597 } 6598 } 6599 } 6600 6601 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 6602 6603 // clear the cache 6604 m_monitor.clearCacheForPublishing(); 6605 6606 int publishTag = getNextPublishTag(dbc); 6607 getProjectDriver(dbc).publishProject(dbc, report, onlineProject, publishList, publishTag); 6608 6609 // iterate the initialized module action instances 6610 Iterator<String> i = OpenCms.getModuleManager().getModuleNames().iterator(); 6611 while (i.hasNext()) { 6612 CmsModule module = OpenCms.getModuleManager().getModule(i.next()); 6613 if ((module != null) && (module.getActionInstance() != null)) { 6614 module.getActionInstance().publishProject(cms, publishList, publishTag, report); 6615 } 6616 } 6617 6618 boolean temporaryProject = (cms.getRequestContext().getCurrentProject().getType() == CmsProject.PROJECT_TYPE_TEMPORARY); 6619 // the project was stored in the history tables for history 6620 // it will be deleted if the project_flag is PROJECT_TYPE_TEMPORARY 6621 if ((temporaryProject) && (!publishList.isDirectPublish())) { 6622 try { 6623 getProjectDriver(dbc).deleteProject(dbc, dbc.currentProject()); 6624 } catch (CmsException e) { 6625 LOG.error( 6626 Messages.get().getBundle().key( 6627 Messages.LOG_DELETE_TEMP_PROJECT_FAILED_1, 6628 cms.getRequestContext().getCurrentProject().getName())); 6629 } 6630 // if project was temporary set context to online project 6631 cms.getRequestContext().setCurrentProject(onlineProject); 6632 } 6633 } finally { 6634 // clear the cache again 6635 m_monitor.clearCacheForPublishing(); 6636 } 6637 } 6638 6639 /** 6640 * Publishes the resources of a specified publish list.<p> 6641 * 6642 * @param cms the current request context 6643 * @param dbc the current database context 6644 * @param publishList a publish list 6645 * @param report an instance of <code>{@link I_CmsReport}</code> to print messages 6646 * 6647 * @throws CmsException if something goes wrong 6648 * 6649 * @see #fillPublishList(CmsDbContext, CmsPublishList) 6650 */ 6651 public synchronized void publishProject( 6652 CmsObject cms, 6653 CmsDbContext dbc, 6654 CmsPublishList publishList, 6655 I_CmsReport report) 6656 throws CmsException { 6657 6658 // check the parent folders 6659 checkParentFolders(dbc, publishList); 6660 ensureSubResourcesOfMovedFoldersPublished(cms, dbc, publishList); 6661 OpenCms.getPublishManager().getPublishListVerifier().checkPublishList(publishList); 6662 6663 try { 6664 // fire an event that a project is to be published 6665 Map<String, Object> eventData = new HashMap<String, Object>(); 6666 eventData.put(I_CmsEventListener.KEY_REPORT, report); 6667 eventData.put(I_CmsEventListener.KEY_PUBLISHLIST, publishList); 6668 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 6669 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 6670 CmsEvent beforePublishEvent = new CmsEvent(I_CmsEventListener.EVENT_BEFORE_PUBLISH_PROJECT, eventData); 6671 OpenCms.fireCmsEvent(beforePublishEvent); 6672 } catch (Throwable t) { 6673 if (report != null) { 6674 report.addError(t); 6675 report.println(t); 6676 } 6677 if (LOG.isErrorEnabled()) { 6678 LOG.error(t.getLocalizedMessage(), t); 6679 } 6680 } 6681 6682 // lock all resources with the special publish lock 6683 Iterator<CmsResource> itResources = new ArrayList<CmsResource>(publishList.getAllResources()).iterator(); 6684 while (itResources.hasNext()) { 6685 CmsResource resource = itResources.next(); 6686 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6687 if (lock.getSystemLock().isUnlocked() && lock.isLockableBy(dbc.currentUser())) { 6688 if (getLock(dbc, resource).getEditionLock().isNullLock()) { 6689 lockResource(dbc, resource, CmsLockType.PUBLISH); 6690 } else { 6691 changeLock(dbc, resource, CmsLockType.PUBLISH); 6692 } 6693 } else if (lock.getSystemLock().isPublish()) { 6694 if (LOG.isWarnEnabled()) { 6695 LOG.warn( 6696 Messages.get().getBundle().key( 6697 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6698 dbc.removeSiteRoot(resource.getRootPath()))); 6699 } 6700 // remove files that are already waiting to be published 6701 publishList.remove(resource); 6702 continue; 6703 } else { 6704 // this is needed to fix TestPublishIsssues#testPublishScenarioE 6705 changeLock(dbc, resource, CmsLockType.PUBLISH); 6706 } 6707 // now re-check the lock state 6708 lock = m_lockManager.getLock(dbc, resource, false); 6709 if (!lock.getSystemLock().isPublish()) { 6710 if (report != null) { 6711 report.println( 6712 Messages.get().container( 6713 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6714 dbc.removeSiteRoot(resource.getRootPath())), 6715 I_CmsReport.FORMAT_WARNING); 6716 } 6717 if (LOG.isWarnEnabled()) { 6718 LOG.warn( 6719 Messages.get().getBundle().key( 6720 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6721 dbc.removeSiteRoot(resource.getRootPath()))); 6722 } 6723 // remove files that could not be locked 6724 publishList.remove(resource); 6725 } 6726 } 6727 6728 // enqueue the publish job 6729 CmsException enqueueException = null; 6730 try { 6731 m_publishEngine.enqueuePublishJob(cms, publishList, report); 6732 } catch (CmsException exc) { 6733 enqueueException = exc; 6734 } 6735 6736 // if an exception was raised, remove the publish locks 6737 // and throw the exception again 6738 if (enqueueException != null) { 6739 itResources = publishList.getAllResources().iterator(); 6740 while (itResources.hasNext()) { 6741 CmsResource resource = itResources.next(); 6742 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6743 if (lock.getSystemLock().isPublish() 6744 && lock.getSystemLock().isOwnedInProjectBy( 6745 cms.getRequestContext().getCurrentUser(), 6746 cms.getRequestContext().getCurrentProject())) { 6747 unlockResource(dbc, resource, true, true); 6748 } 6749 } 6750 6751 throw enqueueException; 6752 } 6753 } 6754 6755 /** 6756 * Transfers the new URL name mappings (if any) for a given resource to the online project.<p> 6757 * 6758 * @param dbc the current database context 6759 * @param res the resource whose new URL name mappings should be transferred to the online project 6760 * 6761 * @throws CmsDataAccessException if something goes wrong 6762 */ 6763 public void publishUrlNameMapping(CmsDbContext dbc, CmsResource res) throws CmsDataAccessException { 6764 6765 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 6766 6767 if (res.getState().isDeleted()) { 6768 // remove both offline and online mappings 6769 CmsUrlNameMappingFilter idFilter = CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()); 6770 vfsDriver.deleteUrlNameMappingEntries(dbc, true, idFilter); 6771 vfsDriver.deleteUrlNameMappingEntries(dbc, false, idFilter); 6772 } else { 6773 // copy the new entries to the online table 6774 List<CmsUrlNameMappingEntry> entries = vfsDriver.readUrlNameMappingEntries( 6775 dbc, 6776 false, 6777 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates( 6778 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 6779 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 6780 6781 boolean isReplaceOnPublish = false; 6782 for (CmsUrlNameMappingEntry entry : entries) { 6783 isReplaceOnPublish |= entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH; 6784 } 6785 6786 if (!entries.isEmpty()) { 6787 6788 long now = System.currentTimeMillis(); 6789 if (isReplaceOnPublish) { 6790 vfsDriver.deleteUrlNameMappingEntries( 6791 dbc, 6792 true, 6793 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId())); 6794 vfsDriver.deleteUrlNameMappingEntries( 6795 dbc, 6796 false, 6797 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId())); 6798 } 6799 6800 for (CmsUrlNameMappingEntry entry : entries) { 6801 CmsUrlNameMappingFilter nameFilter = CmsUrlNameMappingFilter.ALL.filterName(entry.getName()); 6802 if (!isReplaceOnPublish) { // we already handled the other case above 6803 vfsDriver.deleteUrlNameMappingEntries(dbc, true, nameFilter); 6804 vfsDriver.deleteUrlNameMappingEntries(dbc, false, nameFilter); 6805 } 6806 } 6807 for (CmsUrlNameMappingEntry entry : entries) { 6808 CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry( 6809 entry.getName(), 6810 entry.getStructureId(), 6811 entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_NEW 6812 ? CmsUrlNameMappingEntry.MAPPING_STATUS_PUBLISHED 6813 : CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH_PUBLISHED, 6814 now, 6815 entry.getLocale()); 6816 vfsDriver.addUrlNameMappingEntry(dbc, true, newEntry); 6817 vfsDriver.addUrlNameMappingEntry(dbc, false, newEntry); 6818 } 6819 } 6820 } 6821 } 6822 6823 /** 6824 * Reads an access control entry from the cms.<p> 6825 * 6826 * The access control entries of a resource are readable by everyone. 6827 * 6828 * @param dbc the current database context 6829 * @param resource the resource 6830 * @param principal the id of a group or a user any other entity 6831 * @return an access control entry that defines the permissions of the entity for the given resource 6832 * @throws CmsException if something goes wrong 6833 */ 6834 public CmsAccessControlEntry readAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) 6835 throws CmsException { 6836 6837 return getUserDriver( 6838 dbc).readAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal); 6839 } 6840 6841 /** 6842 * Finds the alias with a given path.<p> 6843 * 6844 * If no alias is found, null is returned.<p> 6845 * 6846 * @param dbc the current database context 6847 * @param project the current project 6848 * @param siteRoot the site root 6849 * @param path the path of the alias 6850 * 6851 * @return the alias with the given path 6852 * 6853 * @throws CmsException if something goes wrong 6854 */ 6855 6856 public CmsAlias readAliasByPath(CmsDbContext dbc, CmsProject project, String siteRoot, String path) 6857 throws CmsException { 6858 6859 List<CmsAlias> aliases = getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(siteRoot, path, null)); 6860 if (aliases.isEmpty()) { 6861 return null; 6862 } else { 6863 return aliases.get(0); 6864 } 6865 } 6866 6867 /** 6868 * Reads the aliases for a given site root.<p> 6869 * 6870 * @param dbc the current database context 6871 * @param currentProject the current project 6872 * @param siteRoot the site root 6873 * 6874 * @return the list of aliases for the given site root 6875 * 6876 * @throws CmsException if something goes wrong 6877 */ 6878 public List<CmsAlias> readAliasesBySite(CmsDbContext dbc, CmsProject currentProject, String siteRoot) 6879 throws CmsException { 6880 6881 return getVfsDriver(dbc).readAliases(dbc, currentProject, new CmsAliasFilter(siteRoot, null, null)); 6882 } 6883 6884 /** 6885 * Reads the aliases which point to a given structure id.<p> 6886 * 6887 * @param dbc the current database context 6888 * @param project the current project 6889 * @param structureId the structure id for which we want to read the aliases 6890 * 6891 * @return the list of aliases pointing to the structure id 6892 * @throws CmsException if something goes wrong 6893 */ 6894 public List<CmsAlias> readAliasesByStructureId(CmsDbContext dbc, CmsProject project, CmsUUID structureId) 6895 throws CmsException { 6896 6897 return getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(null, null, structureId)); 6898 } 6899 6900 /** 6901 * Reads all versions of the given resource.<br> 6902 * 6903 * This method returns a list with the history of the given resource, i.e. 6904 * the historical resource entries, independent of the project they were attached to.<br> 6905 * 6906 * The reading excludes the file content.<p> 6907 * 6908 * @param dbc the current database context 6909 * @param resource the resource to read the history for 6910 * 6911 * @return a list of file headers, as <code>{@link I_CmsHistoryResource}</code> objects 6912 * 6913 * @throws CmsException if something goes wrong 6914 */ 6915 public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsResource resource) 6916 throws CmsException { 6917 6918 // read the historical resources 6919 List<I_CmsHistoryResource> versions = getHistoryDriver(dbc).readAllAvailableVersions( 6920 dbc, 6921 resource.getStructureId()); 6922 if ((versions.size() > OpenCms.getSystemInfo().getHistoryVersions()) 6923 && (OpenCms.getSystemInfo().getHistoryVersions() > -1)) { 6924 return versions.subList(0, OpenCms.getSystemInfo().getHistoryVersions()); 6925 } 6926 return versions; 6927 } 6928 6929 /** 6930 * Reads all property definitions for the given mapping type.<p> 6931 * 6932 * @param dbc the current database context 6933 * 6934 * @return a list with the <code>{@link CmsPropertyDefinition}</code> objects (may be empty) 6935 * 6936 * @throws CmsException if something goes wrong 6937 */ 6938 public List<CmsPropertyDefinition> readAllPropertyDefinitions(CmsDbContext dbc) throws CmsException { 6939 6940 List<CmsPropertyDefinition> result = getVfsDriver(dbc).readPropertyDefinitions( 6941 dbc, 6942 dbc.currentProject().getUuid()); 6943 Collections.sort(result); 6944 return result; 6945 } 6946 6947 /** 6948 * Returns all resources subscribed by the given user or group.<p> 6949 * 6950 * @param dbc the database context 6951 * @param poolName the name of the database pool to use 6952 * @param principal the principal to read the subscribed resources 6953 * 6954 * @return all resources subscribed by the given user or group 6955 * 6956 * @throws CmsException if something goes wrong 6957 */ 6958 public List<CmsResource> readAllSubscribedResources(CmsDbContext dbc, String poolName, CmsPrincipal principal) 6959 throws CmsException { 6960 6961 List<CmsResource> result = getSubscriptionDriver().readAllSubscribedResources(dbc, poolName, principal); 6962 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 6963 return result; 6964 } 6965 6966 /** 6967 * Selects the best url name for a given resource and locale.<p> 6968 * 6969 * @param dbc the database context 6970 * @param id the resource's structure id 6971 * @param locale the requested locale 6972 * @param defaultLocales the default locales to use if the locale isn't available 6973 * 6974 * @return the URL name which was found 6975 * 6976 * @throws CmsDataAccessException if the database operation failed 6977 */ 6978 public String readBestUrlName(CmsDbContext dbc, CmsUUID id, Locale locale, List<Locale> defaultLocales) 6979 throws CmsDataAccessException { 6980 6981 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 6982 dbc, 6983 dbc.currentProject().isOnlineProject(), 6984 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 6985 if (entries.isEmpty()) { 6986 return null; 6987 } 6988 6989 ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create(); 6990 for (CmsUrlNameMappingEntry entry : entries) { 6991 entriesByLocale.put(entry.getLocale(), entry); 6992 } 6993 List<CmsUrlNameMappingEntry> lastEntries = new ArrayList<CmsUrlNameMappingEntry>(); 6994 Comparator<CmsUrlNameMappingEntry> dateChangedComparator = new UrlNameMappingComparator(); 6995 for (String localeKey : entriesByLocale.keySet()) { 6996 // for each locale select the latest mapping entry 6997 CmsUrlNameMappingEntry latestEntryForLocale = Collections.max( 6998 entriesByLocale.get(localeKey), 6999 dateChangedComparator); 7000 lastEntries.add(latestEntryForLocale); 7001 } 7002 CmsLocaleManager localeManager = OpenCms.getLocaleManager(); 7003 List<Locale> availableLocales = new ArrayList<Locale>(); 7004 for (CmsUrlNameMappingEntry entry : lastEntries) { 7005 availableLocales.add(CmsLocaleManager.getLocale(entry.getLocale())); 7006 } 7007 Locale bestLocale = localeManager.getBestMatchingLocale(locale, defaultLocales, availableLocales); 7008 String bestLocaleStr = bestLocale.toString(); 7009 for (CmsUrlNameMappingEntry entry : lastEntries) { 7010 if (entry.getLocale().equals(bestLocaleStr)) { 7011 return entry.getName(); 7012 } 7013 } 7014 return null; 7015 } 7016 7017 /** 7018 * Returns the child resources of a resource, that is the resources 7019 * contained in a folder.<p> 7020 * 7021 * With the parameters <code>getFolders</code> and <code>getFiles</code> 7022 * you can control what type of resources you want in the result list: 7023 * files, folders, or both.<p> 7024 * 7025 * This method is mainly used by the workplace explorer.<p> 7026 * 7027 * @param dbc the current database context 7028 * @param resource the resource to return the child resources for 7029 * @param filter the resource filter to use 7030 * @param getFolders if true the child folders are included in the result 7031 * @param getFiles if true the child files are included in the result 7032 * @param checkPermissions if the resources should be filtered with the current user permissions 7033 * 7034 * @return a list of all child resources 7035 * 7036 * @throws CmsException if something goes wrong 7037 */ 7038 public List<CmsResource> readChildResources( 7039 CmsDbContext dbc, 7040 CmsResource resource, 7041 CmsResourceFilter filter, 7042 boolean getFolders, 7043 boolean getFiles, 7044 boolean checkPermissions) 7045 throws CmsException { 7046 7047 String cacheKey = null; 7048 List<CmsResource> resourceList = null; 7049 if (m_monitor.isEnabled(CmsMemoryMonitor.CacheType.RESOURCE_LIST)) { // check this here to skip the complex cache key generation 7050 String time = ""; 7051 if (checkPermissions) { 7052 // ensure correct caching if site time offset is set 7053 if ((dbc.getRequestContext() != null) 7054 && (OpenCms.getSiteManager().getSiteForSiteRoot(dbc.getRequestContext().getSiteRoot()) != null)) { 7055 time += OpenCms.getSiteManager().getSiteForSiteRoot( 7056 dbc.getRequestContext().getSiteRoot()).getSiteMatcher().getTimeOffset(); 7057 } 7058 } 7059 // try to get the sub resources from the cache 7060 cacheKey = getCacheKey( 7061 new String[] { 7062 dbc.currentUser().getName(), 7063 getFolders 7064 ? (getFiles ? CmsCacheKey.CACHE_KEY_SUBALL : CmsCacheKey.CACHE_KEY_SUBFOLDERS) 7065 : CmsCacheKey.CACHE_KEY_SUBFILES, 7066 checkPermissions ? "+" + time : "-", 7067 filter.getCacheId(), 7068 resource.getRootPath()}, 7069 dbc); 7070 7071 resourceList = m_monitor.getCachedResourceList(cacheKey); 7072 } 7073 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 7074 // read the result form the database 7075 resourceList = getVfsDriver( 7076 dbc).readChildResources(dbc, dbc.currentProject(), resource, getFolders, getFiles); 7077 7078 if (checkPermissions) { 7079 // apply the permission filter 7080 resourceList = filterPermissions(dbc, resourceList, filter); 7081 } 7082 // cache the sub resources 7083 if (dbc.getProjectId().isNullUUID()) { 7084 m_monitor.cacheResourceList(cacheKey, resourceList); 7085 } 7086 } 7087 7088 // we must always apply the result filter and update the context dates 7089 return updateContextDates(dbc, resourceList, filter); 7090 } 7091 7092 /** 7093 * Returns the default file for the given folder.<p> 7094 * 7095 * If the given resource is a file, then this file is returned.<p> 7096 * 7097 * Otherwise, in case of a folder:<br> 7098 * <ol> 7099 * <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and 7100 * <li>if still no file could be found, the configured default files in the 7101 * <code>opencms-vfs.xml</code> configuration are iterated until a match is 7102 * found, and 7103 * <li>if still no file could be found, <code>null</code> is retuned 7104 * </ol> 7105 * 7106 * @param dbc the database context 7107 * @param resource the folder to get the default file for 7108 * @param resourceFilter the resource filter 7109 * 7110 * @return the default file for the given folder 7111 */ 7112 public CmsResource readDefaultFile(CmsDbContext dbc, CmsResource resource, CmsResourceFilter resourceFilter) { 7113 7114 // resource exists, lets check if we have a file or a folder 7115 if (resource.isFolder()) { 7116 // the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder 7117 try { 7118 String defaultFileName = readPropertyObject( 7119 dbc, 7120 resource, 7121 CmsPropertyDefinition.PROPERTY_DEFAULT_FILE, 7122 false).getValue(); 7123 // check if the default file property does not match the navigation level folder marker value 7124 if ((defaultFileName != null) && !CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER.equals(defaultFileName)) { 7125 // property was set, so look up this file first 7126 String folderName = CmsResource.getFolderPath(resource.getRootPath()); 7127 resource = readResource(dbc, folderName + defaultFileName, resourceFilter.addRequireFile()); 7128 } 7129 } catch (CmsException e) { 7130 // ignore all other exceptions and continue the lookup process 7131 if (LOG.isDebugEnabled()) { 7132 LOG.debug(e.getLocalizedMessage(), e); 7133 } 7134 } 7135 if (resource.isFolder()) { 7136 String folderName = CmsResource.getFolderPath(resource.getRootPath()); 7137 // resource is (still) a folder, check default files specified in configuration 7138 Iterator<String> it = OpenCms.getDefaultFiles().iterator(); 7139 while (it.hasNext()) { 7140 String tmpResourceName = folderName + it.next(); 7141 try { 7142 resource = readResource(dbc, tmpResourceName, resourceFilter.addRequireFile()); 7143 // no exception? So we have found the default file 7144 // stop looking for default files 7145 break; 7146 } catch (CmsException e) { 7147 // ignore all other exceptions and continue the lookup process 7148 if (LOG.isDebugEnabled()) { 7149 LOG.debug(e.getLocalizedMessage(), e); 7150 } 7151 } 7152 } 7153 } 7154 } 7155 if (resource.isFolder()) { 7156 // we only want files as a result for further processing 7157 resource = null; 7158 } 7159 return resource; 7160 } 7161 7162 /** 7163 * Reads all deleted (historical) resources below the given path, 7164 * including the full tree below the path, if required.<p> 7165 * 7166 * @param dbc the current db context 7167 * @param resource the parent resource to read the resources from 7168 * @param readTree <code>true</code> to read all subresources 7169 * @param isVfsManager <code>true</code> if the current user has the vfs manager role 7170 * 7171 * @return a list of <code>{@link I_CmsHistoryResource}</code> objects 7172 * 7173 * @throws CmsException if something goes wrong 7174 * 7175 * @see CmsObject#readResource(CmsUUID, int) 7176 * @see CmsObject#readResources(String, CmsResourceFilter, boolean) 7177 * @see CmsObject#readDeletedResources(String, boolean) 7178 */ 7179 public List<I_CmsHistoryResource> readDeletedResources( 7180 CmsDbContext dbc, 7181 CmsResource resource, 7182 boolean readTree, 7183 boolean isVfsManager) 7184 throws CmsException { 7185 7186 Set<I_CmsHistoryResource> result = new HashSet<I_CmsHistoryResource>(); 7187 List<I_CmsHistoryResource> deletedResources; 7188 dbc.getRequestContext().setAttribute("ATTR_RESOURCE_NAME", resource.getRootPath()); 7189 try { 7190 deletedResources = getHistoryDriver(dbc).readDeletedResources( 7191 dbc, 7192 resource.getStructureId(), 7193 isVfsManager ? null : dbc.currentUser().getId()); 7194 } finally { 7195 dbc.getRequestContext().removeAttribute("ATTR_RESOURCE_NAME"); 7196 } 7197 result.addAll(deletedResources); 7198 Set<I_CmsHistoryResource> newResult = new HashSet<I_CmsHistoryResource>(result.size()); 7199 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 7200 Iterator<I_CmsHistoryResource> it = result.iterator(); 7201 while (it.hasNext()) { 7202 I_CmsHistoryResource histRes = it.next(); 7203 // adjust the paths 7204 try { 7205 if (vfsDriver.validateStructureIdExists( 7206 dbc, 7207 dbc.currentProject().getUuid(), 7208 histRes.getStructureId())) { 7209 newResult.add(histRes); 7210 continue; 7211 } 7212 // adjust the path in case of deleted files 7213 String resourcePath = histRes.getRootPath(); 7214 String resName = CmsResource.getName(resourcePath); 7215 String path = CmsResource.getParentFolder(resourcePath); 7216 7217 CmsUUID parentId = histRes.getParentId(); 7218 try { 7219 // first look for the path through the parent id 7220 path = readResource(dbc, parentId, CmsResourceFilter.IGNORE_EXPIRATION).getRootPath(); 7221 } catch (CmsDataAccessException e) { 7222 // if the resource with the parent id is not found, try to get a new parent id with the path 7223 try { 7224 parentId = readResource(dbc, path, CmsResourceFilter.IGNORE_EXPIRATION).getStructureId(); 7225 } catch (CmsDataAccessException e1) { 7226 // ignore, the parent folder has been completely deleted 7227 } 7228 } 7229 resourcePath = path + resName; 7230 7231 boolean isFolder = resourcePath.endsWith("/"); 7232 if (isFolder) { 7233 newResult.add( 7234 new CmsHistoryFolder( 7235 histRes.getPublishTag(), 7236 histRes.getStructureId(), 7237 histRes.getResourceId(), 7238 resourcePath, 7239 histRes.getTypeId(), 7240 histRes.getFlags(), 7241 histRes.getProjectLastModified(), 7242 histRes.getState(), 7243 histRes.getDateCreated(), 7244 histRes.getUserCreated(), 7245 histRes.getDateLastModified(), 7246 histRes.getUserLastModified(), 7247 histRes.getDateReleased(), 7248 histRes.getDateExpired(), 7249 histRes.getVersion(), 7250 parentId, 7251 histRes.getResourceVersion(), 7252 histRes.getStructureVersion())); 7253 } else { 7254 newResult.add( 7255 new CmsHistoryFile( 7256 histRes.getPublishTag(), 7257 histRes.getStructureId(), 7258 histRes.getResourceId(), 7259 resourcePath, 7260 histRes.getTypeId(), 7261 histRes.getFlags(), 7262 histRes.getProjectLastModified(), 7263 histRes.getState(), 7264 histRes.getDateCreated(), 7265 histRes.getUserCreated(), 7266 histRes.getDateLastModified(), 7267 histRes.getUserLastModified(), 7268 histRes.getDateReleased(), 7269 histRes.getDateExpired(), 7270 histRes.getLength(), 7271 histRes.getDateContent(), 7272 histRes.getVersion(), 7273 parentId, 7274 null, 7275 histRes.getResourceVersion(), 7276 histRes.getStructureVersion())); 7277 } 7278 } catch (CmsDataAccessException e) { 7279 // should never happen 7280 if (LOG.isErrorEnabled()) { 7281 LOG.error(e.getLocalizedMessage(), e); 7282 } 7283 } 7284 } 7285 if (readTree) { 7286 Iterator<I_CmsHistoryResource> itDeleted = deletedResources.iterator(); 7287 while (itDeleted.hasNext()) { 7288 I_CmsHistoryResource delResource = itDeleted.next(); 7289 if (delResource.isFolder()) { 7290 newResult.addAll(readDeletedResources(dbc, (CmsFolder)delResource, readTree, isVfsManager)); 7291 } 7292 } 7293 try { 7294 readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL); 7295 // resource exists, so recurse 7296 Iterator<CmsResource> itResources = readResources( 7297 dbc, 7298 resource, 7299 CmsResourceFilter.ALL.addRequireFolder(), 7300 readTree).iterator(); 7301 while (itResources.hasNext()) { 7302 CmsResource subResource = itResources.next(); 7303 if (subResource.isFolder()) { 7304 newResult.addAll(readDeletedResources(dbc, subResource, readTree, isVfsManager)); 7305 } 7306 } 7307 } catch (Exception e) { 7308 // resource does not exists 7309 if (LOG.isDebugEnabled()) { 7310 LOG.debug(e.getLocalizedMessage(), e); 7311 } 7312 } 7313 } 7314 List<I_CmsHistoryResource> finalRes = new ArrayList<I_CmsHistoryResource>(newResult); 7315 Collections.sort(finalRes, I_CmsResource.COMPARE_ROOT_PATH); 7316 return finalRes; 7317 } 7318 7319 /** 7320 * Reads a file resource (including it's binary content) from the VFS, 7321 * using the specified resource filter.<p> 7322 * 7323 * In case you do not need the file content, 7324 * use <code>{@link #readResource(CmsDbContext, String, CmsResourceFilter)}</code> instead.<p> 7325 * 7326 * The specified filter controls what kind of resources should be "found" 7327 * during the read operation. This will depend on the application. For example, 7328 * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently 7329 * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code> 7330 * will ignore the date release / date expired information of the resource.<p> 7331 * 7332 * @param dbc the current database context 7333 * @param resource the base file resource (without content) 7334 * @return the file read from the VFS 7335 * @throws CmsException if operation was not successful 7336 */ 7337 public CmsFile readFile(CmsDbContext dbc, CmsResource resource) throws CmsException { 7338 7339 if (resource.isFolder()) { 7340 throw new CmsVfsResourceNotFoundException( 7341 Messages.get().container( 7342 Messages.ERR_ACCESS_FOLDER_AS_FILE_1, 7343 dbc.removeSiteRoot(resource.getRootPath()))); 7344 } 7345 7346 CmsUUID projectId = dbc.currentProject().getUuid(); 7347 CmsFile file = null; 7348 if (resource instanceof I_CmsHistoryResource) { 7349 file = new CmsHistoryFile((I_CmsHistoryResource)resource); 7350 file.setContents( 7351 getHistoryDriver(dbc).readContent( 7352 dbc, 7353 resource.getResourceId(), 7354 ((I_CmsHistoryResource)resource).getPublishTag())); 7355 } else { 7356 file = new CmsFile(resource); 7357 file.setContents(getVfsDriver(dbc).readContent(dbc, projectId, resource.getResourceId())); 7358 } 7359 return file; 7360 } 7361 7362 /** 7363 * Reads a folder from the VFS, 7364 * using the specified resource filter.<p> 7365 * 7366 * @param dbc the current database context 7367 * @param resourcename the name of the folder to read (full path) 7368 * @param filter the resource filter to use while reading 7369 * 7370 * @return the folder that was read 7371 * 7372 * @throws CmsDataAccessException if something goes wrong 7373 * 7374 * @see #readResource(CmsDbContext, String, CmsResourceFilter) 7375 * @see CmsObject#readFolder(String) 7376 * @see CmsObject#readFolder(String, CmsResourceFilter) 7377 */ 7378 public CmsFolder readFolder(CmsDbContext dbc, String resourcename, CmsResourceFilter filter) 7379 throws CmsDataAccessException { 7380 7381 CmsResource resource = readResource(dbc, resourcename, filter); 7382 7383 return convertResourceToFolder(resource); 7384 } 7385 7386 public List<CmsFolderSizeEntry> readFolderSizeStats(CmsDbContext dbc, CmsFolderSizeOptions options) 7387 throws CmsException { 7388 7389 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 7390 List<CmsFolderSizeEntry> stats = vfsDriver.readFolderSizeStats(dbc, false, options); 7391 return stats; 7392 7393 } 7394 7395 /** 7396 * Reads the group of a project.<p> 7397 * 7398 * @param dbc the current database context 7399 * @param project the project to read from 7400 * 7401 * @return the group of a resource 7402 */ 7403 public CmsGroup readGroup(CmsDbContext dbc, CmsProject project) { 7404 7405 try { 7406 return readGroup(dbc, project.getGroupId()); 7407 } catch (CmsException exc) { 7408 return new CmsGroup( 7409 CmsUUID.getNullUUID(), 7410 CmsUUID.getNullUUID(), 7411 project.getGroupId() + "", 7412 "deleted group", 7413 0); 7414 } 7415 } 7416 7417 /** 7418 * Reads a group based on its id.<p> 7419 * 7420 * @param dbc the current database context 7421 * @param groupId the id of the group that is to be read 7422 * 7423 * @return the requested group 7424 * 7425 * @throws CmsException if operation was not successful 7426 */ 7427 public CmsGroup readGroup(CmsDbContext dbc, CmsUUID groupId) throws CmsException { 7428 7429 CmsGroup group = null; 7430 // try to read group from cache 7431 group = m_monitor.getCachedGroup(groupId.toString()); 7432 if (group == null) { 7433 group = getUserDriver(dbc).readGroup(dbc, groupId); 7434 m_monitor.cacheGroup(group); 7435 } 7436 return group; 7437 } 7438 7439 /** 7440 * Reads a group based on its name.<p> 7441 * 7442 * @param dbc the current database context 7443 * @param groupname the name of the group that is to be read 7444 * 7445 * @return the requested group 7446 * 7447 * @throws CmsDataAccessException if operation was not successful 7448 */ 7449 public CmsGroup readGroup(CmsDbContext dbc, String groupname) throws CmsDataAccessException { 7450 7451 CmsGroup group = null; 7452 // try to read group from cache 7453 group = m_monitor.getCachedGroup(groupname); 7454 if (group == null) { 7455 group = getUserDriver(dbc).readGroup(dbc, groupname); 7456 m_monitor.cacheGroup(group); 7457 } 7458 return group; 7459 } 7460 7461 /** 7462 * Reads a principal (an user or group) from the historical archive based on its ID.<p> 7463 * 7464 * @param dbc the current database context 7465 * @param principalId the id of the principal to read 7466 * 7467 * @return the historical principal entry with the given id 7468 * 7469 * @throws CmsException if something goes wrong, ie. {@link CmsDbEntryNotFoundException} 7470 * 7471 * @see CmsObject#readUser(CmsUUID) 7472 * @see CmsObject#readGroup(CmsUUID) 7473 * @see CmsObject#readHistoryPrincipal(CmsUUID) 7474 */ 7475 public CmsHistoryPrincipal readHistoricalPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsException { 7476 7477 return getHistoryDriver(dbc).readPrincipal(dbc, principalId); 7478 } 7479 7480 /** 7481 * Returns the latest historical project entry with the given id.<p> 7482 * 7483 * @param dbc the current database context 7484 * @param projectId the project id 7485 * 7486 * @return the requested historical project entry 7487 * 7488 * @throws CmsException if something goes wrong 7489 */ 7490 public CmsHistoryProject readHistoryProject(CmsDbContext dbc, CmsUUID projectId) throws CmsException { 7491 7492 return getHistoryDriver(dbc).readProject(dbc, projectId); 7493 } 7494 7495 /** 7496 * Returns a historical project entry.<p> 7497 * 7498 * @param dbc the current database context 7499 * @param publishTag the publish tag of the project 7500 * 7501 * @return the requested historical project entry 7502 * 7503 * @throws CmsException if something goes wrong 7504 */ 7505 public CmsHistoryProject readHistoryProject(CmsDbContext dbc, int publishTag) throws CmsException { 7506 7507 return getHistoryDriver(dbc).readProject(dbc, publishTag); 7508 } 7509 7510 /** 7511 * Reads the list of all <code>{@link CmsProperty}</code> objects that belongs to the given historical resource.<p> 7512 * 7513 * @param dbc the current database context 7514 * @param historyResource the historical resource to read the properties for 7515 * 7516 * @return the list of <code>{@link CmsProperty}</code> objects 7517 * 7518 * @throws CmsException if something goes wrong 7519 */ 7520 public List<CmsProperty> readHistoryPropertyObjects(CmsDbContext dbc, I_CmsHistoryResource historyResource) 7521 throws CmsException { 7522 7523 return getHistoryDriver(dbc).readProperties(dbc, historyResource); 7524 } 7525 7526 /** 7527 * Reads the structure id which is mapped to a given URL name.<p> 7528 * 7529 * @param dbc the current database context 7530 * @param name the name for which the mapped structure id should be looked up 7531 * 7532 * @return the structure id which is mapped to the given name, or null if there is no such id 7533 * 7534 * @throws CmsDataAccessException if something goes wrong 7535 */ 7536 public CmsUUID readIdForUrlName(CmsDbContext dbc, String name) throws CmsDataAccessException { 7537 7538 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7539 dbc, 7540 dbc.currentProject().isOnlineProject(), 7541 CmsUrlNameMappingFilter.ALL.filterName(name)); 7542 if (entries.isEmpty()) { 7543 return null; 7544 } 7545 return entries.get(0).getStructureId(); 7546 } 7547 7548 /** 7549 * Reads the locks that were saved to the database in the previous run of OpenCms.<p> 7550 * 7551 * @param dbc the current database context 7552 * 7553 * @throws CmsException if something goes wrong 7554 */ 7555 public void readLocks(CmsDbContext dbc) throws CmsException { 7556 7557 m_lockManager.readLocks(dbc); 7558 } 7559 7560 /** 7561 * Reads the manager group of a project.<p> 7562 * 7563 * @param dbc the current database context 7564 * @param project the project to read from 7565 * 7566 * @return the group of a resource 7567 */ 7568 public CmsGroup readManagerGroup(CmsDbContext dbc, CmsProject project) { 7569 7570 try { 7571 return readGroup(dbc, project.getManagerGroupId()); 7572 } catch (CmsException exc) { 7573 // the group does not exist any more - return a dummy-group 7574 return new CmsGroup( 7575 CmsUUID.getNullUUID(), 7576 CmsUUID.getNullUUID(), 7577 project.getManagerGroupId() + "", 7578 "deleted group", 7579 0); 7580 } 7581 } 7582 7583 /** 7584 * Reads the URL name which has been most recently mapped to the given structure id, or null 7585 * if no URL name is mapped to the id.<p> 7586 * 7587 * @param dbc the current database context 7588 * @param id a structure id 7589 * @return the name which has been most recently mapped to the given structure id 7590 * 7591 * @throws CmsDataAccessException if something goes wrong 7592 */ 7593 public String readNewestUrlNameForId(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 7594 7595 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7596 dbc, 7597 dbc.currentProject().isOnlineProject(), 7598 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 7599 if (entries.isEmpty()) { 7600 return null; 7601 } 7602 7603 Collections.sort(entries, new UrlNameMappingComparator()); 7604 CmsUrlNameMappingEntry lastEntry = entries.get(entries.size() - 1); 7605 return lastEntry.getName(); 7606 } 7607 7608 /** 7609 * Reads an organizational Unit based on its fully qualified name.<p> 7610 * 7611 * @param dbc the current db context 7612 * @param ouFqn the fully qualified name of the organizational Unit to be read 7613 * 7614 * @return the organizational Unit that with the provided fully qualified name 7615 * 7616 * @throws CmsException if something goes wrong 7617 */ 7618 public CmsOrganizationalUnit readOrganizationalUnit(CmsDbContext dbc, String ouFqn) throws CmsException { 7619 7620 CmsOrganizationalUnit organizationalUnit = null; 7621 // try to read organizational unit from cache 7622 organizationalUnit = m_monitor.getCachedOrgUnit(ouFqn); 7623 if (organizationalUnit == null) { 7624 organizationalUnit = getUserDriver(dbc).readOrganizationalUnit(dbc, ouFqn); 7625 m_monitor.cacheOrgUnit(organizationalUnit); 7626 } 7627 return organizationalUnit; 7628 } 7629 7630 /** 7631 * Reads the owner of a project.<p> 7632 * 7633 * @param dbc the current database context 7634 * @param project the project to get the owner from 7635 * 7636 * @return the owner of a resource 7637 * @throws CmsException if something goes wrong 7638 */ 7639 public CmsUser readOwner(CmsDbContext dbc, CmsProject project) throws CmsException { 7640 7641 return readUser(dbc, project.getOwnerId()); 7642 } 7643 7644 /** 7645 * Reads the parent folder to a given structure id.<p> 7646 * 7647 * @param dbc the current database context 7648 * @param structureId the structure id of the child 7649 * 7650 * @return the parent folder resource 7651 * 7652 * @throws CmsDataAccessException if something goes wrong 7653 */ 7654 public CmsResource readParentFolder(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException { 7655 7656 return getVfsDriver(dbc).readParentFolder(dbc, dbc.currentProject().getUuid(), structureId); 7657 } 7658 7659 /** 7660 * Builds a list of resources for a given path.<p> 7661 * 7662 * @param dbc the current database context 7663 * @param path the requested path 7664 * @param filter a filter object (only "includeDeleted" information is used!) 7665 * 7666 * @return list of <code>{@link CmsResource}</code>s 7667 * 7668 * @throws CmsException if something goes wrong 7669 */ 7670 public List<CmsResource> readPath(CmsDbContext dbc, String path, CmsResourceFilter filter) throws CmsException { 7671 7672 // splits the path into folder and filename tokens 7673 List<String> tokens = CmsStringUtil.splitAsList(path, '/'); 7674 7675 // the root folder is no token in the path but a resource which has to be added to the path 7676 int count = tokens.size() + 1; 7677 // holds the CmsResource instances in the path 7678 List<CmsResource> pathList = new ArrayList<CmsResource>(count); 7679 7680 // true if the path doesn't end with a folder 7681 boolean lastResourceIsFile = false; 7682 // number of folders in the path 7683 int folderCount = count; 7684 if (!path.endsWith("/")) { 7685 folderCount--; 7686 lastResourceIsFile = true; 7687 } 7688 7689 // read the root folder, because it's ID is required to read any sub-resources 7690 String currentResourceName = "/"; 7691 StringBuffer currentPath = new StringBuffer(64); 7692 currentPath.append('/'); 7693 7694 String cp = currentPath.toString(); 7695 CmsUUID projectId = getProjectIdForContext(dbc); 7696 7697 // key to cache the resources 7698 String cacheKey = getCacheKey(null, false, projectId, cp); 7699 // the current resource 7700 CmsResource currentResource = m_monitor.getCachedResource(cacheKey); 7701 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7702 currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp); 7703 if (dbc.getProjectId().isNullUUID()) { 7704 m_monitor.cacheResource(cacheKey, currentResource); 7705 } 7706 } 7707 7708 pathList.add(0, currentResource); 7709 7710 if (count == 1) { 7711 // the root folder was requested- no further operations required 7712 return pathList; 7713 } 7714 7715 Iterator<String> it = tokens.iterator(); 7716 currentResourceName = it.next(); 7717 7718 // read the folder resources in the path /a/b/c/ 7719 int i = 0; 7720 for (i = 1; i < folderCount; i++) { 7721 currentPath.append(currentResourceName); 7722 currentPath.append('/'); 7723 // read the folder 7724 cp = currentPath.toString(); 7725 cacheKey = getCacheKey(null, false, projectId, cp); 7726 currentResource = m_monitor.getCachedResource(cacheKey); 7727 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7728 currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp); 7729 if (dbc.getProjectId().isNullUUID()) { 7730 m_monitor.cacheResource(cacheKey, currentResource); 7731 } 7732 } 7733 7734 pathList.add(i, currentResource); 7735 7736 if (i < (folderCount - 1)) { 7737 currentResourceName = it.next(); 7738 } 7739 } 7740 7741 // read the (optional) last file resource in the path /x.html 7742 if (lastResourceIsFile) { 7743 if (it.hasNext()) { 7744 // this will only be false if a resource in the 7745 // top level root folder (e.g. "/index.html") was requested 7746 currentResourceName = it.next(); 7747 } 7748 currentPath.append(currentResourceName); 7749 7750 // read the file 7751 cp = currentPath.toString(); 7752 cacheKey = getCacheKey(null, false, projectId, cp); 7753 currentResource = m_monitor.getCachedResource(cacheKey); 7754 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7755 currentResource = getVfsDriver(dbc).readResource(dbc, projectId, cp, filter.includeDeleted()); 7756 if (dbc.getProjectId().isNullUUID()) { 7757 m_monitor.cacheResource(cacheKey, currentResource); 7758 } 7759 } 7760 7761 pathList.add(i, currentResource); 7762 } 7763 7764 return pathList; 7765 } 7766 7767 /** 7768 * Reads a project given the projects id.<p> 7769 * 7770 * @param dbc the current database context 7771 * @param id the id of the project 7772 * 7773 * @return the project read 7774 * 7775 * @throws CmsDataAccessException if something goes wrong 7776 */ 7777 public CmsProject readProject(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 7778 7779 CmsProject project = null; 7780 project = m_monitor.getCachedProject(id.toString()); 7781 if (project == null) { 7782 project = getProjectDriver(dbc).readProject(dbc, id); 7783 m_monitor.cacheProject(project); 7784 } 7785 return project; 7786 } 7787 7788 /** 7789 * Reads a project.<p> 7790 * 7791 * Important: Since a project name can be used multiple times, this is NOT the most efficient 7792 * way to read the project. This is only a convenience for front end developing. 7793 * Reading a project by name will return the first project with that name. 7794 * All core classes must use the id version {@link #readProject(CmsDbContext, CmsUUID)} to ensure the right project is read.<p> 7795 * 7796 * @param dbc the current database context 7797 * @param name the name of the project 7798 * 7799 * @return the project read 7800 * 7801 * @throws CmsException if something goes wrong 7802 */ 7803 public CmsProject readProject(CmsDbContext dbc, String name) throws CmsException { 7804 7805 CmsProject project = null; 7806 project = m_monitor.getCachedProject(name); 7807 if (project == null) { 7808 project = getProjectDriver(dbc).readProject(dbc, name); 7809 m_monitor.cacheProject(project); 7810 } 7811 return project; 7812 } 7813 7814 /** 7815 * Returns the list of all resource names that define the "view" of the given project.<p> 7816 * 7817 * @param dbc the current database context 7818 * @param project the project to get the project resources for 7819 * 7820 * @return the list of all resources, as <code>{@link String}</code> objects 7821 * that define the "view" of the given project. 7822 * 7823 * @throws CmsException if something goes wrong 7824 */ 7825 public List<String> readProjectResources(CmsDbContext dbc, CmsProject project) throws CmsException { 7826 7827 return getProjectDriver(dbc).readProjectResources(dbc, project); 7828 } 7829 7830 /** 7831 * Reads all resources of a project that match a given state from the VFS.<p> 7832 * 7833 * Possible values for the <code>state</code> parameter are:<br> 7834 * <ul> 7835 * <li><code>{@link CmsResource#STATE_CHANGED}</code>: Read all "changed" resources in the project</li> 7836 * <li><code>{@link CmsResource#STATE_NEW}</code>: Read all "new" resources in the project</li> 7837 * <li><code>{@link CmsResource#STATE_DELETED}</code>: Read all "deleted" resources in the project</li> 7838 * <li><code>{@link CmsResource#STATE_KEEP}</code>: Read all resources either "changed", "new" or "deleted" in the project</li> 7839 * </ul><p> 7840 * 7841 * @param dbc the current database context 7842 * @param projectId the id of the project to read the file resources for 7843 * @param state the resource state to match 7844 * 7845 * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria 7846 * 7847 * @throws CmsException if something goes wrong 7848 * 7849 * @see CmsObject#readProjectView(CmsUUID, CmsResourceState) 7850 */ 7851 public List<CmsResource> readProjectView(CmsDbContext dbc, CmsUUID projectId, CmsResourceState state) 7852 throws CmsException { 7853 7854 List<CmsResource> resources; 7855 if (state.isNew() || state.isChanged() || state.isDeleted()) { 7856 // get all resources form the database that match the selected state 7857 resources = getVfsDriver(dbc).readResources(dbc, projectId, state, CmsDriverManager.READMODE_MATCHSTATE); 7858 } else { 7859 // get all resources form the database that are somehow changed (i.e. not unchanged) 7860 resources = getVfsDriver( 7861 dbc).readResources(dbc, projectId, CmsResource.STATE_UNCHANGED, CmsDriverManager.READMODE_UNMATCHSTATE); 7862 } 7863 7864 // filter the permissions 7865 List<CmsResource> result = filterPermissions(dbc, resources, CmsResourceFilter.ALL); 7866 // sort the result 7867 Collections.sort(result); 7868 // set the full resource names 7869 return updateContextDates(dbc, result); 7870 } 7871 7872 /** 7873 * Reads a property definition.<p> 7874 * 7875 * If no property definition with the given name is found, 7876 * <code>null</code> is returned.<p> 7877 * 7878 * @param dbc the current database context 7879 * @param name the name of the property definition to read 7880 * 7881 * @return the property definition that was read 7882 * 7883 * @throws CmsException a CmsDbEntryNotFoundException is thrown if the property definition does not exist 7884 */ 7885 public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 7886 7887 return getVfsDriver(dbc).readPropertyDefinition(dbc, name, dbc.currentProject().getUuid()); 7888 } 7889 7890 /** 7891 * Reads a property object from a resource specified by a property name.<p> 7892 * 7893 * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p> 7894 * 7895 * @param dbc the current database context 7896 * @param resource the resource where the property is read from 7897 * @param key the property key name 7898 * @param search if <code>true</code>, the property is searched on all parent folders of the resource. 7899 * if it's not found attached directly to the resource. 7900 * 7901 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 7902 * 7903 * @throws CmsException if something goes wrong 7904 */ 7905 public CmsProperty readPropertyObject(CmsDbContext dbc, CmsResource resource, String key, boolean search) 7906 throws CmsException { 7907 7908 // NOTE: Do not call readPropertyObject(dbc, resource, key, search, null) for performance reasons 7909 7910 // use the list reading method to obtain all properties for the resource 7911 List<CmsProperty> properties = readPropertyObjects(dbc, resource, search); 7912 7913 int i = properties.indexOf(new CmsProperty(key, null, null)); 7914 if (i >= 0) { 7915 // property has been found in the map 7916 CmsProperty result = properties.get(i); 7917 // ensure the result value is not frozen 7918 return result.cloneAsProperty(); 7919 } 7920 return CmsProperty.getNullProperty(); 7921 7922 } 7923 7924 /** 7925 * Reads a property object from a resource specified by a property name.<p> 7926 * 7927 * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p> 7928 * 7929 * @param dbc the current database context 7930 * @param resource the resource where the property is read from 7931 * @param key the property key name 7932 * @param search if <code>true</code>, the property is searched on all parent folders of the resource. 7933 * if it's not found attached directly to the resource. 7934 * @param locale the locale for which the property should be read. 7935 * 7936 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 7937 * 7938 * @throws CmsException if something goes wrong 7939 */ 7940 public CmsProperty readPropertyObject( 7941 CmsDbContext dbc, 7942 CmsResource resource, 7943 String key, 7944 boolean search, 7945 Locale locale) 7946 throws CmsException { 7947 7948 // use the list reading method to obtain all properties for the resource 7949 List<CmsProperty> properties = readPropertyObjects(dbc, resource, search); 7950 // create a lookup property object and look this up in the result map 7951 CmsProperty result = null; 7952 // handle the case without locale separately to improve performance 7953 for (String localizedKey : CmsLocaleManager.getLocaleVariants(key, locale, true, false)) { 7954 int i = properties.indexOf(new CmsProperty(localizedKey, null, null)); 7955 if (i >= 0) { 7956 // property has been found in the map 7957 result = properties.get(i); 7958 // ensure the result value is not frozen 7959 return result.cloneAsProperty(); 7960 } 7961 } 7962 return CmsProperty.getNullProperty(); 7963 } 7964 7965 /** 7966 * Reads all property objects mapped to a specified resource from the database.<p> 7967 * 7968 * All properties in the result List will be in frozen (read only) state, so you can't change the values.<p> 7969 * 7970 * Returns an empty list if no properties are found at all.<p> 7971 * 7972 * @param dbc the current database context 7973 * @param resource the resource where the properties are read from 7974 * @param search true, if the properties should be searched on all parent folders if not found on the resource 7975 * 7976 * @return a list of CmsProperty objects containing the structure and/or resource value 7977 * 7978 * @throws CmsException if something goes wrong 7979 * 7980 * @see CmsObject#readPropertyObjects(String, boolean) 7981 */ 7982 public List<CmsProperty> readPropertyObjects(CmsDbContext dbc, CmsResource resource, boolean search) 7983 throws CmsException { 7984 7985 // check if we have the result already cached 7986 CmsUUID projectId = getProjectIdForContext(dbc); 7987 String cacheKey = getCacheKey(CACHE_ALL_PROPERTIES, search, projectId, resource.getRootPath()); 7988 7989 List<CmsProperty> properties = m_monitor.getCachedPropertyList(cacheKey); 7990 7991 if ((properties == null) || !dbc.getProjectId().isNullUUID()) { 7992 // result not cached, let's look it up in the DB 7993 if (search) { 7994 boolean cont; 7995 properties = new ArrayList<CmsProperty>(); 7996 List<CmsProperty> parentProperties = null; 7997 7998 do { 7999 try { 8000 parentProperties = readPropertyObjects(dbc, resource, false); 8001 8002 // make sure properties from lower folders "overwrite" properties from upper folders 8003 parentProperties.removeAll(properties); 8004 parentProperties.addAll(properties); 8005 8006 properties.clear(); 8007 properties.addAll(parentProperties); 8008 8009 cont = resource.getRootPath().length() > 1; 8010 } catch (CmsSecurityException se) { 8011 // a security exception (probably no read permission) we return the current result 8012 cont = false; 8013 } 8014 if (cont) { 8015 // no permission check on parent folder is required since we must have "read" 8016 // permissions to read the child resource anyway 8017 resource = readResource( 8018 dbc, 8019 CmsResource.getParentFolder(resource.getRootPath()), 8020 CmsResourceFilter.ALL); 8021 } 8022 } while (cont); 8023 } else { 8024 properties = getVfsDriver(dbc).readPropertyObjects(dbc, dbc.currentProject(), resource); 8025 // for (CmsProperty prop : properties) { 8026 // prop.setOrigin(resource.getRootPath()); 8027 // } 8028 } 8029 8030 // set all properties in the result list as frozen 8031 CmsProperty.setFrozen(properties); 8032 if (dbc.getProjectId().isNullUUID()) { 8033 // store the result in the cache if needed 8034 m_monitor.cachePropertyList(cacheKey, properties); 8035 } 8036 } 8037 8038 return new ArrayList<CmsProperty>(properties); 8039 } 8040 8041 /** 8042 * Reads the resources that were published in a publish task for a given publish history ID.<p> 8043 * 8044 * @param dbc the current database context 8045 * @param publishHistoryId unique int ID to identify each publish task in the publish history 8046 * 8047 * @return a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects 8048 * 8049 * @throws CmsException if something goes wrong 8050 */ 8051 public List<CmsPublishedResource> readPublishedResources(CmsDbContext dbc, CmsUUID publishHistoryId) 8052 throws CmsException { 8053 8054 String cacheKey = publishHistoryId.toString(); 8055 List<CmsPublishedResource> resourceList = m_monitor.getCachedPublishedResources(cacheKey); 8056 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8057 resourceList = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId); 8058 // store the result in the cache 8059 if (dbc.getProjectId().isNullUUID()) { 8060 m_monitor.cachePublishedResources(cacheKey, resourceList); 8061 } 8062 } 8063 return resourceList; 8064 } 8065 8066 /** 8067 * Reads a single publish job identified by its publish history id.<p> 8068 * 8069 * @param dbc the current database context 8070 * @param publishHistoryId unique id to identify the publish job in the publish history 8071 * @return an object of type <code>{@link CmsPublishJobInfoBean}</code> 8072 * 8073 * @throws CmsException if something goes wrong 8074 */ 8075 public CmsPublishJobInfoBean readPublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8076 8077 return getProjectDriver(dbc).readPublishJob(dbc, publishHistoryId); 8078 } 8079 8080 /** 8081 * Reads all available publish jobs.<p> 8082 * 8083 * @param dbc the current database context 8084 * @param startTime the start of the time range for finish time 8085 * @param endTime the end of the time range for finish time 8086 * @return a list of objects of type <code>{@link CmsPublishJobInfoBean}</code> 8087 * 8088 * @throws CmsException if something goes wrong 8089 */ 8090 public List<CmsPublishJobInfoBean> readPublishJobs(CmsDbContext dbc, long startTime, long endTime) 8091 throws CmsException { 8092 8093 return getProjectDriver(dbc).readPublishJobs(dbc, startTime, endTime); 8094 } 8095 8096 /** 8097 * Reads the publish list assigned to a publish job.<p> 8098 * 8099 * @param dbc the current database context 8100 * @param publishHistoryId the history id identifying the publish job 8101 * @return the assigned publish list 8102 * @throws CmsException if something goes wrong 8103 */ 8104 public CmsPublishList readPublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8105 8106 return getProjectDriver(dbc).readPublishList(dbc, publishHistoryId); 8107 } 8108 8109 /** 8110 * Reads the publish report assigned to a publish job.<p> 8111 * 8112 * @param dbc the current database context 8113 * @param publishHistoryId the history id identifying the publish job 8114 * @return the content of the assigned publish report 8115 * @throws CmsException if something goes wrong 8116 */ 8117 public byte[] readPublishReportContents(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8118 8119 return getProjectDriver(dbc).readPublishReportContents(dbc, publishHistoryId); 8120 } 8121 8122 /** 8123 * Reads an historical resource entry for the given resource and with the given version number.<p> 8124 * 8125 * @param dbc the current db context 8126 * @param resource the resource to be read 8127 * @param version the version number to retrieve 8128 * 8129 * @return the resource that was read 8130 * 8131 * @throws CmsException if the resource could not be read for any reason 8132 * 8133 * @see CmsObject#restoreResourceVersion(CmsUUID, int) 8134 * @see CmsObject#readResource(CmsUUID, int) 8135 */ 8136 public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException { 8137 8138 Iterator<I_CmsHistoryResource> itVersions = getHistoryDriver(dbc).readAllAvailableVersions( 8139 dbc, 8140 resource.getStructureId()).iterator(); 8141 while (itVersions.hasNext()) { 8142 I_CmsHistoryResource histRes = itVersions.next(); 8143 if (histRes.getVersion() == version) { 8144 return histRes; 8145 } 8146 } 8147 throw new CmsVfsResourceNotFoundException( 8148 org.opencms.db.generic.Messages.get().container( 8149 org.opencms.db.generic.Messages.ERR_HISTORY_FILE_NOT_FOUND_1, 8150 resource.getStructureId())); 8151 } 8152 8153 /** 8154 * Reads a resource from the VFS, using the specified resource filter.<p> 8155 * 8156 * @param dbc the current database context 8157 * @param structureID the structure id of the resource to read 8158 * @param filter the resource filter to use while reading 8159 * 8160 * @return the resource that was read 8161 * 8162 * @throws CmsDataAccessException if something goes wrong 8163 * 8164 * @see CmsObject#readResource(CmsUUID, CmsResourceFilter) 8165 * @see CmsObject#readResource(CmsUUID) 8166 */ 8167 public CmsResource readResource(CmsDbContext dbc, CmsUUID structureID, CmsResourceFilter filter) 8168 throws CmsDataAccessException { 8169 8170 CmsUUID projectId = getProjectIdForContext(dbc); 8171 // please note: the filter will be applied in the security manager later 8172 CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, structureID, filter.includeDeleted()); 8173 8174 // context dates need to be updated 8175 updateContextDates(dbc, resource); 8176 8177 // return the resource 8178 return resource; 8179 } 8180 8181 /** 8182 * Reads a resource from the VFS, using the specified resource filter.<p> 8183 * 8184 * @param dbc the current database context 8185 * @param resourcePath the name of the resource to read (full path) 8186 * @param filter the resource filter to use while reading 8187 * 8188 * @return the resource that was read 8189 * 8190 * @throws CmsDataAccessException if something goes wrong 8191 * 8192 * @see CmsObject#readResource(String, CmsResourceFilter) 8193 * @see CmsObject#readResource(String) 8194 * @see CmsObject#readFile(CmsResource) 8195 */ 8196 public CmsResource readResource(CmsDbContext dbc, String resourcePath, CmsResourceFilter filter) 8197 throws CmsDataAccessException { 8198 8199 CmsUUID projectId = getProjectIdForContext(dbc); 8200 // please note: the filter will be applied in the security manager later 8201 CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, resourcePath, filter.includeDeleted()); 8202 8203 // context dates need to be updated 8204 updateContextDates(dbc, resource); 8205 8206 // return the resource 8207 return resource; 8208 } 8209 8210 /** 8211 * Reads all resources below the given path matching the filter criteria, 8212 * including the full tree below the path only in case the <code>readTree</code> 8213 * parameter is <code>true</code>.<p> 8214 * 8215 * @param dbc the current database context 8216 * @param parent the parent path to read the resources from 8217 * @param filter the filter 8218 * @param readTree <code>true</code> to read all subresources 8219 * 8220 * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria 8221 * 8222 * @throws CmsDataAccessException if the bare reading of the resources fails 8223 * @throws CmsException if security and permission checks for the resources read fail 8224 */ 8225 public List<CmsResource> readResources( 8226 CmsDbContext dbc, 8227 CmsResource parent, 8228 CmsResourceFilter filter, 8229 boolean readTree) 8230 throws CmsException, CmsDataAccessException { 8231 8232 // try to get the sub resources from the cache 8233 String cacheKey = getCacheKey( 8234 new String[] {dbc.currentUser().getName(), filter.getCacheId(), readTree ? "+" : "-", parent.getRootPath()}, 8235 dbc); 8236 8237 List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey); 8238 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8239 // read the result from the database 8240 resourceList = getVfsDriver(dbc).readResourceTree( 8241 dbc, 8242 dbc.currentProject().getUuid(), 8243 (readTree ? parent.getRootPath() : parent.getStructureId().toString()), 8244 filter.getType(), 8245 filter.getState(), 8246 filter.getModifiedAfter(), 8247 filter.getModifiedBefore(), 8248 filter.getReleaseAfter(), 8249 filter.getReleaseBefore(), 8250 filter.getExpireAfter(), 8251 filter.getExpireBefore(), 8252 (readTree ? CmsDriverManager.READMODE_INCLUDE_TREE : CmsDriverManager.READMODE_EXCLUDE_TREE) 8253 | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0) 8254 | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0) 8255 | ((filter.getOnlyFolders() != null) 8256 ? (filter.getOnlyFolders().booleanValue() 8257 ? CmsDriverManager.READMODE_ONLY_FOLDERS 8258 : CmsDriverManager.READMODE_ONLY_FILES) 8259 : 0)); 8260 8261 // HACK: do not take care of permissions if reading organizational units 8262 if (!parent.getRootPath().startsWith("/system/orgunits/")) { 8263 // apply permission filter 8264 resourceList = filterPermissions(dbc, resourceList, filter); 8265 } 8266 // store the result in the resourceList cache 8267 if (dbc.getProjectId().isNullUUID()) { 8268 m_monitor.cacheResourceList(cacheKey, resourceList); 8269 } 8270 } 8271 // we must always apply the result filter and update the context dates 8272 return updateContextDates(dbc, resourceList, filter); 8273 } 8274 8275 /** 8276 * Returns the resources that were visited by a user set in the filter.<p> 8277 * 8278 * @param dbc the database context 8279 * @param poolName the name of the database pool to use 8280 * @param filter the filter that is used to get the visited resources 8281 * 8282 * @return the resources that were visited by a user set in the filter 8283 * 8284 * @throws CmsException if something goes wrong 8285 */ 8286 public List<CmsResource> readResourcesVisitedBy(CmsDbContext dbc, String poolName, CmsVisitedByFilter filter) 8287 throws CmsException { 8288 8289 List<CmsResource> result = getSubscriptionDriver().readResourcesVisitedBy(dbc, poolName, filter); 8290 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 8291 return result; 8292 } 8293 8294 /** 8295 * Reads all resources that have a value (containing the given value string) set 8296 * for the specified property (definition) in the given path.<p> 8297 * 8298 * Both individual and shared properties of a resource are checked.<p> 8299 * 8300 * If the <code>value</code> parameter is <code>null</code>, all resources having the 8301 * given property set are returned.<p> 8302 * 8303 * @param dbc the current database context 8304 * @param folder the folder to get the resources with the property from 8305 * @param propertyDefinition the name of the property (definition) to check for 8306 * @param value the string to search in the value of the property 8307 * @param filter the resource filter to apply to the result set 8308 * 8309 * @return a list of all <code>{@link CmsResource}</code> objects 8310 * that have a value set for the specified property. 8311 * 8312 * @throws CmsException if something goes wrong 8313 */ 8314 public List<CmsResource> readResourcesWithProperty( 8315 CmsDbContext dbc, 8316 CmsResource folder, 8317 String propertyDefinition, 8318 String value, 8319 CmsResourceFilter filter) 8320 throws CmsException { 8321 8322 String cacheKey; 8323 if (value == null) { 8324 cacheKey = getCacheKey( 8325 new String[] { 8326 dbc.currentUser().getName(), 8327 folder.getRootPath(), 8328 propertyDefinition, 8329 filter.getCacheId()}, 8330 dbc); 8331 } else { 8332 cacheKey = getCacheKey( 8333 new String[] { 8334 dbc.currentUser().getName(), 8335 folder.getRootPath(), 8336 propertyDefinition, 8337 value, 8338 filter.getCacheId()}, 8339 dbc); 8340 } 8341 List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey); 8342 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8343 8344 CmsPropertyDefinition propDef = null; 8345 try { 8346 // first read the property definition 8347 propDef = readPropertyDefinition(dbc, propertyDefinition); 8348 } catch (CmsDbEntryNotFoundException e) { 8349 LOG.debug(e.getLocalizedMessage(), e); 8350 } 8351 if (propDef != null) { 8352 // now read the list of resources that have a value set for the property definition 8353 resourceList = getVfsDriver(dbc).readResourcesWithProperty( 8354 dbc, 8355 dbc.currentProject().getUuid(), 8356 propDef.getId(), 8357 folder.getRootPath(), 8358 value); 8359 // apply permission filter 8360 resourceList = filterPermissions(dbc, resourceList, filter); 8361 } else { 8362 resourceList = new ArrayList<>(); 8363 } 8364 // store the result in the resourceList cache 8365 if (dbc.getProjectId().isNullUUID()) { 8366 m_monitor.cacheResourceList(cacheKey, resourceList); 8367 } 8368 } 8369 // we must always apply the result filter and update the context dates 8370 return updateContextDates(dbc, resourceList, filter); 8371 } 8372 8373 /** 8374 * Returns the set of users that are responsible for a specific resource.<p> 8375 * 8376 * @param dbc the current database context 8377 * @param resource the resource to get the responsible users from 8378 * 8379 * @return the set of users that are responsible for a specific resource 8380 * 8381 * @throws CmsException if something goes wrong 8382 */ 8383 public Set<I_CmsPrincipal> readResponsiblePrincipals(CmsDbContext dbc, CmsResource resource) throws CmsException { 8384 8385 Set<I_CmsPrincipal> result = new HashSet<I_CmsPrincipal>(); 8386 Iterator<CmsAccessControlEntry> aces = getAccessControlEntries(dbc, resource, true).iterator(); 8387 while (aces.hasNext()) { 8388 CmsAccessControlEntry ace = aces.next(); 8389 if (ace.isResponsible()) { 8390 I_CmsPrincipal p = lookupPrincipal(dbc, ace.getPrincipal()); 8391 if (p != null) { 8392 result.add(p); 8393 } 8394 } 8395 } 8396 return result; 8397 } 8398 8399 /** 8400 * Returns the set of users that are responsible for a specific resource.<p> 8401 * 8402 * @param dbc the current database context 8403 * @param resource the resource to get the responsible users from 8404 * 8405 * @return the set of users that are responsible for a specific resource 8406 * 8407 * @throws CmsException if something goes wrong 8408 */ 8409 public Set<CmsUser> readResponsibleUsers(CmsDbContext dbc, CmsResource resource) throws CmsException { 8410 8411 Set<CmsUser> result = new HashSet<CmsUser>(); 8412 Iterator<I_CmsPrincipal> principals = readResponsiblePrincipals(dbc, resource).iterator(); 8413 while (principals.hasNext()) { 8414 I_CmsPrincipal principal = principals.next(); 8415 if (principal.isGroup()) { 8416 try { 8417 result.addAll(getUsersOfGroup(dbc, principal.getName(), true, false, false)); 8418 } catch (CmsException e) { 8419 if (LOG.isInfoEnabled()) { 8420 LOG.info(e.getLocalizedMessage(), e); 8421 } 8422 } 8423 } else { 8424 result.add((CmsUser)principal); 8425 } 8426 } 8427 return result; 8428 } 8429 8430 /** 8431 * Returns a List of all siblings of the specified resource, 8432 * the specified resource being always part of the result set.<p> 8433 * 8434 * The result is a list of <code>{@link CmsResource}</code> objects.<p> 8435 * 8436 * @param dbc the current database context 8437 * @param resource the resource to read the siblings for 8438 * @param filter a filter object 8439 * 8440 * @return a list of <code>{@link CmsResource}</code> Objects that 8441 * are siblings to the specified resource, 8442 * including the specified resource itself 8443 * 8444 * @throws CmsException if something goes wrong 8445 */ 8446 public List<CmsResource> readSiblings(CmsDbContext dbc, CmsResource resource, CmsResourceFilter filter) 8447 throws CmsException { 8448 8449 List<CmsResource> siblings = getVfsDriver( 8450 dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, filter.includeDeleted()); 8451 8452 // important: there is no permission check done on the returned list of siblings 8453 // this is because of possible issues with the "publish all siblings" option, 8454 // moreover the user has read permission for the content through 8455 // the selected sibling anyway 8456 return updateContextDates(dbc, siblings, filter); 8457 } 8458 8459 /** 8460 * Returns the parameters of a resource in the table of all published template resources.<p> 8461 * 8462 * @param dbc the current database context 8463 * @param rfsName the rfs name of the resource 8464 * 8465 * @return the parameter string of the requested resource 8466 * 8467 * @throws CmsException if something goes wrong 8468 */ 8469 public String readStaticExportPublishedResourceParameters(CmsDbContext dbc, String rfsName) throws CmsException { 8470 8471 return getProjectDriver(dbc).readStaticExportPublishedResourceParameters(dbc, rfsName); 8472 } 8473 8474 /** 8475 * Returns a list of all template resources which must be processed during a static export.<p> 8476 * 8477 * @param dbc the current database context 8478 * @param parameterResources flag for reading resources with parameters (1) or without (0) 8479 * @param timestamp for reading the data from the db 8480 * 8481 * @return a list of template resources as <code>{@link String}</code> objects 8482 * 8483 * @throws CmsException if something goes wrong 8484 */ 8485 public List<String> readStaticExportResources(CmsDbContext dbc, int parameterResources, long timestamp) 8486 throws CmsException { 8487 8488 return getProjectDriver(dbc).readStaticExportResources(dbc, parameterResources, timestamp); 8489 } 8490 8491 /** 8492 * Returns the subscribed history resources that were deleted.<p> 8493 * 8494 * @param dbc the database context 8495 * @param poolName the name of the database pool to use 8496 * @param user the user that subscribed to the resource 8497 * @param groups the groups to check subscribed resources for 8498 * @param parent the parent resource (folder) of the deleted resources, if <code>null</code> all deleted resources will be returned 8499 * @param includeSubFolders indicates if the sub folders of the specified folder path should be considered, too 8500 * @param deletedFrom the time stamp from which the resources should have been deleted 8501 * 8502 * @return the subscribed history resources that were deleted 8503 * 8504 * @throws CmsException if something goes wrong 8505 */ 8506 public List<I_CmsHistoryResource> readSubscribedDeletedResources( 8507 CmsDbContext dbc, 8508 String poolName, 8509 CmsUser user, 8510 List<CmsGroup> groups, 8511 CmsResource parent, 8512 boolean includeSubFolders, 8513 long deletedFrom) 8514 throws CmsException { 8515 8516 List<I_CmsHistoryResource> result = getSubscriptionDriver().readSubscribedDeletedResources( 8517 dbc, 8518 poolName, 8519 user, 8520 groups, 8521 parent, 8522 includeSubFolders, 8523 deletedFrom); 8524 8525 return result; 8526 } 8527 8528 /** 8529 * Returns the resources that were subscribed by a user or group set in the filter.<p> 8530 * 8531 * @param dbc the database context 8532 * @param poolName the name of the database pool to use 8533 * @param filter the filter that is used to get the subscribed resources 8534 * 8535 * @return the resources that were subscribed by a user or group set in the filter 8536 * 8537 * @throws CmsException if something goes wrong 8538 */ 8539 public List<CmsResource> readSubscribedResources(CmsDbContext dbc, String poolName, CmsSubscriptionFilter filter) 8540 throws CmsException { 8541 8542 List<CmsResource> result = getSubscriptionDriver().readSubscribedResources(dbc, poolName, filter); 8543 8544 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 8545 return result; 8546 } 8547 8548 /** 8549 * Reads URL name mapping entries which match the given filter.<p> 8550 * 8551 * @param dbc the database context 8552 * @param online if true, read online URL name mappings, else offline ones 8553 * @param filter the filter for matching the URL name entries 8554 * 8555 * @return the list of URL name mapping entries which match the given filter 8556 * 8557 * @throws CmsDataAccessException if something goes wrong 8558 */ 8559 public List<CmsUrlNameMappingEntry> readUrlNameMappingEntries( 8560 CmsDbContext dbc, 8561 boolean online, 8562 CmsUrlNameMappingFilter filter) 8563 throws CmsDataAccessException { 8564 8565 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 8566 return vfsDriver.readUrlNameMappingEntries(dbc, online, filter); 8567 } 8568 8569 /** 8570 * Reads the URL name mappings matching the given filter.<p> 8571 * 8572 * @param dbc the DB context to use 8573 * @param filter the filter used to select the mapping entries 8574 * @return the entries matching the given filter 8575 * 8576 * @throws CmsDataAccessException if something goes wrong 8577 */ 8578 public List<CmsUrlNameMappingEntry> readUrlNameMappings(CmsDbContext dbc, CmsUrlNameMappingFilter filter) 8579 throws CmsDataAccessException { 8580 8581 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 8582 dbc, 8583 dbc.currentProject().isOnlineProject(), 8584 filter); 8585 return entries; 8586 } 8587 8588 /** 8589 * Reads the newest URL names of a resource for all locales.<p> 8590 * 8591 * @param dbc the database context 8592 * @param id the resource's structure id 8593 * 8594 * @return the url names for the locales 8595 * 8596 * @throws CmsDataAccessException if the database operation failed 8597 */ 8598 public List<String> readUrlNamesForAllLocales(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 8599 8600 List<String> result = new ArrayList<String>(); 8601 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 8602 dbc, 8603 dbc.currentProject().isOnlineProject(), 8604 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 8605 ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create(); 8606 for (CmsUrlNameMappingEntry entry : entries) { 8607 String localeKey = entry.getLocale(); 8608 entriesByLocale.put(localeKey, entry); 8609 } 8610 8611 for (String localeKey : entriesByLocale.keySet()) { 8612 List<CmsUrlNameMappingEntry> entrs = entriesByLocale.get(localeKey); 8613 CmsUrlNameMappingEntry maxEntryForLocale = Collections.max(entrs, new UrlNameMappingComparator()); 8614 result.add(maxEntryForLocale.getName()); 8615 } 8616 return result; 8617 } 8618 8619 /** 8620 * Returns a user object based on the id of a user.<p> 8621 * 8622 * @param dbc the current database context 8623 * @param id the id of the user to read 8624 * 8625 * @return the user read 8626 * 8627 * @throws CmsException if something goes wrong 8628 */ 8629 public CmsUser readUser(CmsDbContext dbc, CmsUUID id) throws CmsException { 8630 8631 CmsUser user = m_monitor.getCachedUser(id.toString()); 8632 if (user == null) { 8633 user = getUserDriver(dbc).readUser(dbc, id); 8634 m_monitor.cacheUser(user); 8635 } 8636 // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects 8637 return user.clone(); 8638 } 8639 8640 /** 8641 * Returns a user object.<p> 8642 * 8643 * @param dbc the current database context 8644 * @param username the name of the user that is to be read 8645 * 8646 * @return user read 8647 * 8648 * @throws CmsDataAccessException if operation was not successful 8649 */ 8650 public CmsUser readUser(CmsDbContext dbc, String username) throws CmsDataAccessException { 8651 8652 CmsUser user = m_monitor.getCachedUser(username); 8653 if (user == null) { 8654 user = getUserDriver(dbc).readUser(dbc, username); 8655 m_monitor.cacheUser(user); 8656 } 8657 // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects 8658 return user.clone(); 8659 } 8660 8661 /** 8662 * Returns a user object if the password for the user is correct.<p> 8663 * 8664 * If the user/pwd pair is not valid a <code>{@link CmsException}</code> is thrown.<p> 8665 * 8666 * @param dbc the current database context 8667 * @param username the username of the user that is to be read 8668 * @param password the password of the user that is to be read 8669 * 8670 * @return user read 8671 * 8672 * @throws CmsException if operation was not successful 8673 */ 8674 public CmsUser readUser(CmsDbContext dbc, String username, String password) throws CmsException { 8675 8676 // don't read user from cache here because password may have changed 8677 CmsUser user = getUserDriver(dbc).readUser(dbc, username, password, null); 8678 m_monitor.cacheUser(user); 8679 return user; 8680 } 8681 8682 /** 8683 * Removes an access control entry for a given resource and principal.<p> 8684 * 8685 * @param dbc the current database context 8686 * @param resource the resource 8687 * @param principal the id of the principal to remove the the access control entry for 8688 * 8689 * @throws CmsException if something goes wrong 8690 */ 8691 public void removeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) 8692 throws CmsException { 8693 8694 // remove the ace 8695 getUserDriver(dbc).removeAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal); 8696 8697 // log it 8698 log( 8699 dbc, 8700 new CmsLogEntry( 8701 dbc, 8702 resource.getStructureId(), 8703 CmsLogEntryType.RESOURCE_PERMISSIONS, 8704 new String[] {resource.getRootPath()}), 8705 false); 8706 8707 // update the "last modified" information 8708 setDateLastModified(dbc, resource, resource.getDateLastModified()); 8709 8710 // clear the cache 8711 m_monitor.clearAccessControlListCache(); 8712 8713 // fire a resource modification event 8714 Map<String, Object> data = new HashMap<String, Object>(2); 8715 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 8716 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 8717 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 8718 } 8719 8720 /** 8721 * Removes a resource from the given organizational unit.<p> 8722 * 8723 * @param dbc the current db context 8724 * @param orgUnit the organizational unit to remove the resource from 8725 * @param resource the resource that is to be removed from the organizational unit 8726 * 8727 * @throws CmsException if something goes wrong 8728 * 8729 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 8730 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 8731 */ 8732 public void removeResourceFromOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource) 8733 throws CmsException { 8734 8735 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 8736 getUserDriver(dbc).removeResourceFromOrganizationalUnit(dbc, orgUnit, resource); 8737 } 8738 8739 /** 8740 * Removes a resource from the current project of the user.<p> 8741 * 8742 * @param dbc the current database context 8743 * @param resource the resource to apply this operation to 8744 * 8745 * @throws CmsException if something goes wrong 8746 * 8747 * @see CmsObject#copyResourceToProject(String) 8748 * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource) 8749 */ 8750 public void removeResourceFromProject(CmsDbContext dbc, CmsResource resource) throws CmsException { 8751 8752 // remove the resource to the project only if the resource is already in the project 8753 if (isInsideCurrentProject(dbc, resource.getRootPath())) { 8754 // check if there are already any subfolders of this resource 8755 I_CmsProjectDriver projectDriver = getProjectDriver(dbc); 8756 if (resource.isFolder()) { 8757 List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject()); 8758 for (int i = 0; i < projectResources.size(); i++) { 8759 String resname = projectResources.get(i); 8760 if (resname.startsWith(resource.getRootPath())) { 8761 // delete the existing project resource first 8762 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname); 8763 } 8764 } 8765 } 8766 try { 8767 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath()); 8768 } catch (CmsException exc) { 8769 // if the subfolder exists already - all is ok 8770 } finally { 8771 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 8772 8773 OpenCms.fireCmsEvent( 8774 new CmsEvent( 8775 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 8776 Collections.<String, Object> singletonMap("project", dbc.currentProject()))); 8777 } 8778 } 8779 } 8780 8781 /** 8782 * Removes the given resource to the given user's publish list.<p> 8783 * 8784 * @param dbc the database context 8785 * @param userId the user's id 8786 * @param structureIds the collection of structure IDs to remove 8787 * 8788 * @throws CmsDataAccessException if something goes wrong 8789 */ 8790 public void removeResourceFromUsersPubList(CmsDbContext dbc, CmsUUID userId, Collection<CmsUUID> structureIds) 8791 throws CmsDataAccessException { 8792 8793 for (CmsUUID structureId : structureIds) { 8794 CmsLogEntry entry = new CmsLogEntry( 8795 userId, 8796 System.currentTimeMillis(), 8797 structureId, 8798 CmsLogEntryType.RESOURCE_HIDDEN, 8799 new String[] {readResource(dbc, structureId, CmsResourceFilter.ALL).getRootPath()}); 8800 log(dbc, entry, true); 8801 } 8802 } 8803 8804 /** 8805 * Removes a user from a group.<p> 8806 * 8807 * @param dbc the current database context 8808 * @param username the name of the user that is to be removed from the group 8809 * @param groupname the name of the group 8810 * @param readRoles if to read roles or groups 8811 * 8812 * @throws CmsException if operation was not successful 8813 * @throws CmsIllegalArgumentException if the given user was not member in the given group 8814 * @throws CmsDbEntryNotFoundException if the given group was not found 8815 * @throws CmsSecurityException if the given user was <b>read as 'null' from the database</b> 8816 * 8817 * @see #addUserToGroup(CmsDbContext, String, String, boolean) 8818 */ 8819 public void removeUserFromGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 8820 throws CmsException, CmsIllegalArgumentException, CmsDbEntryNotFoundException, CmsSecurityException { 8821 8822 CmsGroup group = readGroup(dbc, groupname); 8823 //check if group exists 8824 if (group == null) { 8825 // the group does not exists 8826 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 8827 } 8828 if (group.isVirtual() && !readRoles) { 8829 // if removing a user from a virtual role treat it as removing the user from the role 8830 removeUserFromGroup(dbc, username, CmsRole.valueOf(group).getGroupName(), true); 8831 return; 8832 } 8833 if (group.isVirtual()) { 8834 // this is an hack so to prevent a unlimited recursive calls 8835 readRoles = false; 8836 } 8837 if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) { 8838 // we want a role but we got a group, or the other way 8839 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 8840 } 8841 8842 boolean skipRemove = false; 8843 // test if this user is existing in the group 8844 if (!userInGroup(dbc, username, groupname, readRoles)) { 8845 if (readRoles) { 8846 // Sometimes users can end up with the default groups corresponding to roles (Administrators, Users) without the actual roles. 8847 // 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 8848 // want to throw an exception then, because it would prevent the code that actually removes the user from the group from running. 8849 LOG.warn( 8850 "Trying to remove user from role that they are not a member of (user: " 8851 + username 8852 + ", group: " 8853 + groupname 8854 + ")"); 8855 skipRemove = true; 8856 } else { 8857 // user is not in the group, throw exception 8858 throw new CmsIllegalArgumentException( 8859 Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname)); 8860 } 8861 } 8862 8863 CmsUser user = readUser(dbc, username); 8864 //check if the user exists 8865 if (user == null) { 8866 // the user does not exists 8867 throw new CmsIllegalArgumentException( 8868 Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname)); 8869 } 8870 8871 if (readRoles) { 8872 CmsRole role = CmsRole.valueOf(group); 8873 // update virtual groups 8874 Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator(); 8875 while (it.hasNext()) { 8876 CmsGroup virtualGroup = it.next(); 8877 if (userInGroup(dbc, username, virtualGroup.getName(), false)) { 8878 // here we say readroles = true, to prevent an unlimited recursive calls 8879 removeUserFromGroup(dbc, username, virtualGroup.getName(), true); 8880 } 8881 } 8882 } 8883 if (!skipRemove) { 8884 getUserDriver(dbc).deleteUserInGroup(dbc, user.getId(), group.getId()); 8885 } 8886 8887 // flush relevant caches 8888 if (readRoles) { 8889 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 8890 } 8891 m_monitor.flushUserGroups(user.getId()); 8892 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 8893 8894 if (!dbc.getProjectId().isNullUUID()) { 8895 // user modified event is not needed 8896 return; 8897 } 8898 // fire user modified event 8899 Map<String, Object> eventData = new HashMap<String, Object>(); 8900 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 8901 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 8902 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 8903 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 8904 eventData.put( 8905 I_CmsEventListener.KEY_USER_ACTION, 8906 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP); 8907 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 8908 8909 } 8910 8911 /** 8912 * Repairs broken categories.<p> 8913 * 8914 * @param dbc the database context 8915 * @param projectId the project id 8916 * @param resource the resource to repair the categories for 8917 * 8918 * @throws CmsException if something goes wrong 8919 */ 8920 public void repairCategories(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsException { 8921 8922 CmsObject cms = OpenCms.initCmsObject(new CmsObject(getSecurityManager(), dbc.getRequestContext())); 8923 cms.getRequestContext().setSiteRoot(""); 8924 cms.getRequestContext().setCurrentProject(readProject(dbc, projectId)); 8925 CmsCategoryService.getInstance().repairRelations(cms, resource); 8926 } 8927 8928 /** 8929 * Replaces the content, type and properties of a resource.<p> 8930 * 8931 * @param dbc the current database context 8932 * @param resource the name of the resource to apply this operation to 8933 * @param type the new type of the resource 8934 * @param content the new content of the resource 8935 * @param properties the new properties of the resource 8936 * 8937 * @throws CmsException if something goes wrong 8938 * 8939 * @see CmsObject#replaceResource(String, int, byte[], List) 8940 * @see I_CmsResourceType#replaceResource(CmsObject, CmsSecurityManager, CmsResource, int, byte[], List) 8941 */ 8942 @SuppressWarnings("javadoc") 8943 public void replaceResource( 8944 CmsDbContext dbc, 8945 CmsResource resource, 8946 int type, 8947 byte[] content, 8948 List<CmsProperty> properties) 8949 throws CmsException { 8950 8951 // replace the existing with the new file content 8952 getVfsDriver(dbc).replaceResource(dbc, resource, content, type); 8953 8954 if ((properties != null) && !properties.isEmpty()) { 8955 // write the properties 8956 getVfsDriver(dbc).writePropertyObjects(dbc, dbc.currentProject(), resource, properties); 8957 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 8958 } 8959 8960 // update the resource state 8961 if (resource.getState().isUnchanged()) { 8962 resource.setState(CmsResource.STATE_CHANGED); 8963 } 8964 resource.setUserLastModified(dbc.currentUser().getId()); 8965 8966 // log it 8967 log( 8968 dbc, 8969 new CmsLogEntry( 8970 dbc, 8971 resource.getStructureId(), 8972 CmsLogEntryType.RESOURCE_CONTENT_MODIFIED, 8973 new String[] {resource.getRootPath()}), 8974 false); 8975 8976 setDateLastModified(dbc, resource, System.currentTimeMillis()); 8977 8978 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false); 8979 8980 deleteRelationsWithSiblings(dbc, resource); 8981 8982 // clear the cache 8983 m_monitor.clearResourceCache(); 8984 8985 if ((properties != null) && !properties.isEmpty()) { 8986 // resource and properties were modified 8987 OpenCms.fireCmsEvent( 8988 new CmsEvent( 8989 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 8990 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource))); 8991 } else { 8992 // only the resource was modified 8993 Map<String, Object> data = new HashMap<String, Object>(2); 8994 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 8995 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 8996 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 8997 } 8998 } 8999 9000 /** 9001 * Resets the password for a specified user.<p> 9002 * 9003 * @param dbc the current database context 9004 * @param username the name of the user 9005 * @param oldPassword the old password 9006 * @param secondFactor the second factor data used for 2FA 9007 * @param newPassword the new password 9008 * 9009 * @throws CmsException if the user data could not be read from the database 9010 * @throws CmsSecurityException if the specified username and old password could not be verified 9011 */ 9012 public void resetPassword( 9013 CmsDbContext dbc, 9014 String username, 9015 String oldPassword, 9016 CmsSecondFactorInfo secondFactor, 9017 String newPassword) 9018 throws CmsException, CmsSecurityException { 9019 9020 if ((oldPassword != null) && (newPassword != null)) { 9021 9022 CmsUser user = null; 9023 9024 if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) { 9025 validatePassword(newPassword); 9026 } 9027 9028 // read the user as a system user to verify that the specified old password is correct 9029 try { 9030 user = getUserDriver(dbc).readUser(dbc, username, oldPassword, null); 9031 } catch (CmsDbEntryNotFoundException e) { 9032 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), e); 9033 } 9034 9035 if ((user == null) || user.isManaged()) { 9036 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username)); 9037 } 9038 9039 CmsTwoFactorAuthenticationHandler twoFactorHandler = OpenCms.getTwoFactorAuthenticationHandler(); 9040 if (twoFactorHandler.needsTwoFactorAuthentication(user) && twoFactorHandler.hasSecondFactor(user)) { 9041 if (!twoFactorHandler.verifySecondFactor(user, secondFactor)) { 9042 throw new CmsDataAccessException( 9043 Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), 9044 new RuntimeException("Verification code mismatch")); 9045 } 9046 } 9047 9048 getUserDriver(dbc).writePassword(dbc, username, oldPassword, newPassword); 9049 user.getAdditionalInfo().put( 9050 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 9051 "" + System.currentTimeMillis()); 9052 user.deleteAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_PASSWORD_RESET); 9053 getUserDriver(dbc).writeUser(dbc, user); 9054 9055 if (!dbc.getProjectId().isNullUUID()) { 9056 // user modified event is not needed 9057 return; 9058 } 9059 // fire user modified event 9060 Map<String, Object> eventData = new HashMap<String, Object>(); 9061 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9062 eventData.put( 9063 I_CmsEventListener.KEY_USER_ACTION, 9064 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD); 9065 eventData.put( 9066 I_CmsEventListener.KEY_USER_CHANGES, 9067 Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA)); 9068 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9069 9070 } else if (CmsStringUtil.isEmpty(oldPassword)) { 9071 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_OLD_MISSING_0)); 9072 } else if (CmsStringUtil.isEmpty(newPassword)) { 9073 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_NEW_MISSING_0)); 9074 } 9075 } 9076 9077 /** 9078 * Restores a deleted resource identified by its structure id from the historical archive.<p> 9079 * 9080 * @param dbc the current database context 9081 * @param structureId the structure id of the resource to restore 9082 * 9083 * @throws CmsException if something goes wrong 9084 * 9085 * @see CmsObject#restoreDeletedResource(CmsUUID) 9086 */ 9087 public CmsResource restoreDeletedResource(CmsDbContext dbc, CmsUUID structureId) throws CmsException { 9088 9089 // get the last version, which should be the deleted one 9090 int version = getHistoryDriver(dbc).readLastVersion(dbc, structureId); 9091 // get that version 9092 I_CmsHistoryResource histRes = getHistoryDriver(dbc).readResource(dbc, structureId, version); 9093 9094 // check the parent path 9095 CmsResource parent; 9096 try { 9097 // try to read the parent resource by id 9098 parent = getVfsDriver(dbc).readResource(dbc, dbc.currentProject().getUuid(), histRes.getParentId(), true); 9099 } catch (CmsVfsResourceNotFoundException e) { 9100 // if not found try to read the parent resource by name 9101 try { 9102 // try to read the parent resource by id 9103 parent = getVfsDriver(dbc).readResource( 9104 dbc, 9105 dbc.currentProject().getUuid(), 9106 CmsResource.getParentFolder(histRes.getRootPath()), 9107 true); 9108 } catch (CmsVfsResourceNotFoundException e1) { 9109 // if not found try to restore the parent resource 9110 restoreDeletedResource(dbc, histRes.getParentId()); 9111 parent = readResource(dbc, histRes.getParentId(), CmsResourceFilter.IGNORE_EXPIRATION); 9112 } 9113 } 9114 // check write permissions 9115 m_securityManager.checkPermissions( 9116 dbc, 9117 parent, 9118 CmsPermissionSet.ACCESS_WRITE, 9119 false, 9120 CmsResourceFilter.IGNORE_EXPIRATION); 9121 9122 // check the name 9123 String path = parent.getRootPath(); 9124 String resName = CmsResource.getName(histRes.getRootPath()); // name 9125 String ext = ""; 9126 if (resName.charAt(resName.length() - 1) == '/') { 9127 resName = resName.substring(0, resName.length() - 1); 9128 } else { 9129 ext = CmsFileUtil.getExtension(resName); // extension 9130 } 9131 String nameWOExt = resName.substring(0, resName.length() - ext.length()); // name without extension 9132 for (int i = 1; true; i++) { 9133 try { 9134 readResource(dbc, path + resName, CmsResourceFilter.ALL); 9135 resName = nameWOExt + "_" + i + ext; 9136 // try the next resource name with following schema: path/name_{i}.ext 9137 } catch (CmsVfsResourceNotFoundException e) { 9138 // ok, we found a not used resource name 9139 break; 9140 } 9141 } 9142 9143 // check structure id 9144 CmsUUID id = structureId; 9145 if (getVfsDriver(dbc).validateStructureIdExists(dbc, dbc.currentProject().getUuid(), structureId)) { 9146 // should never happen, but if already exists create a new one 9147 id = new CmsUUID(); 9148 } 9149 9150 byte[] contents = null; 9151 boolean isFolder = true; 9152 9153 // do we need the contents? 9154 if (histRes instanceof CmsFile) { 9155 contents = ((CmsFile)histRes).getContents(); 9156 if ((contents == null) || (contents.length == 0)) { 9157 contents = getHistoryDriver(dbc).readContent(dbc, histRes.getResourceId(), histRes.getPublishTag()); 9158 } 9159 isFolder = false; 9160 } 9161 9162 // now read the historical properties 9163 List<CmsProperty> properties = getHistoryDriver(dbc).readProperties(dbc, histRes); 9164 9165 // create the object to create 9166 CmsResource newResource = new CmsResource( 9167 id, 9168 histRes.getResourceId(), 9169 path + resName, 9170 histRes.getTypeId(), 9171 isFolder, 9172 histRes.getFlags(), 9173 dbc.currentProject().getUuid(), 9174 CmsResource.STATE_NEW, 9175 histRes.getDateCreated(), 9176 histRes.getUserCreated(), 9177 histRes.getDateLastModified(), 9178 dbc.currentUser().getId(), 9179 histRes.getDateReleased(), 9180 histRes.getDateExpired(), 9181 histRes.getSiblingCount(), 9182 histRes.getLength(), 9183 histRes.getDateContent(), 9184 histRes.getVersion()); 9185 9186 // log it 9187 log( 9188 dbc, 9189 new CmsLogEntry( 9190 dbc, 9191 newResource.getStructureId(), 9192 CmsLogEntryType.RESOURCE_RESTORE_DELETED, 9193 new String[] {newResource.getRootPath()}), 9194 false); 9195 9196 // prevent the date last modified is set to the current time 9197 newResource.setDateLastModified(newResource.getDateLastModified()); 9198 // restore the resource! 9199 CmsResource resource = createResource(dbc, path + resName, newResource, contents, properties, true); 9200 // set resource state to changed 9201 newResource.setState(CmsResource.STATE_CHANGED); 9202 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), newResource, UPDATE_RESOURCE_STATE, false); 9203 newResource.setState(CmsResource.STATE_NEW); 9204 // fire the event 9205 Map<String, Object> data = new HashMap<String, Object>(2); 9206 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9207 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9208 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9209 return newResource; 9210 } 9211 9212 /** 9213 * Restores a resource in the current project with a version from the historical archive.<p> 9214 * 9215 * @param dbc the current database context 9216 * @param resource the resource to restore from the archive 9217 * @param version the version number to restore from the archive 9218 * 9219 * @throws CmsException if something goes wrong 9220 * 9221 * @see CmsObject#restoreResourceVersion(CmsUUID, int) 9222 * @see I_CmsResourceType#restoreResource(CmsObject, CmsSecurityManager, CmsResource, int) 9223 */ 9224 public CmsResource restoreResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException { 9225 9226 I_CmsHistoryResource historyResource = readResource(dbc, resource, version); 9227 CmsResourceState state = CmsResource.STATE_CHANGED; 9228 if (resource.getState().isNew()) { 9229 state = CmsResource.STATE_NEW; 9230 } 9231 int newVersion = resource.getVersion(); 9232 if (resource.getState().isUnchanged()) { 9233 newVersion++; 9234 } 9235 CmsResource newResource = null; 9236 // is the resource a file? 9237 if (historyResource instanceof CmsFile) { 9238 // get the historical up flags 9239 int flags = historyResource.getFlags(); 9240 if (resource.isLabeled()) { 9241 // set the flag for labeled links on the restored file 9242 flags |= CmsResource.FLAG_LABELED; 9243 } 9244 CmsFile newFile = new CmsFile( 9245 resource.getStructureId(), 9246 resource.getResourceId(), 9247 resource.getRootPath(), 9248 historyResource.getTypeId(), 9249 flags, 9250 dbc.currentProject().getUuid(), 9251 state, 9252 resource.getDateCreated(), 9253 historyResource.getUserCreated(), 9254 resource.getDateLastModified(), 9255 dbc.currentUser().getId(), 9256 historyResource.getDateReleased(), 9257 historyResource.getDateExpired(), 9258 resource.getSiblingCount(), 9259 historyResource.getLength(), 9260 historyResource.getDateContent(), 9261 newVersion, 9262 readFile(dbc, (CmsHistoryFile)historyResource).getContents()); 9263 9264 // log it 9265 log( 9266 dbc, 9267 new CmsLogEntry( 9268 dbc, 9269 newFile.getStructureId(), 9270 CmsLogEntryType.RESOURCE_HISTORY, 9271 new String[] {newFile.getRootPath()}), 9272 false); 9273 9274 newResource = writeFile(dbc, newFile); 9275 } else { 9276 // it is a folder! 9277 newResource = new CmsFolder( 9278 resource.getStructureId(), 9279 resource.getResourceId(), 9280 resource.getRootPath(), 9281 historyResource.getTypeId(), 9282 historyResource.getFlags(), 9283 dbc.currentProject().getUuid(), 9284 state, 9285 resource.getDateCreated(), 9286 historyResource.getUserCreated(), 9287 resource.getDateLastModified(), 9288 dbc.currentUser().getId(), 9289 historyResource.getDateReleased(), 9290 historyResource.getDateExpired(), 9291 newVersion); 9292 9293 // log it 9294 log( 9295 dbc, 9296 new CmsLogEntry( 9297 dbc, 9298 newResource.getStructureId(), 9299 CmsLogEntryType.RESOURCE_HISTORY, 9300 new String[] {newResource.getRootPath()}), 9301 false); 9302 9303 writeResource(dbc, newResource); 9304 } 9305 if (newResource != null) { 9306 // now read the historical properties 9307 List<CmsProperty> historyProperties = getHistoryDriver(dbc).readProperties(dbc, historyResource); 9308 // remove all properties 9309 deleteAllProperties(dbc, newResource.getRootPath()); 9310 // write them to the restored resource 9311 writePropertyObjects(dbc, newResource, historyProperties, false); 9312 9313 m_monitor.clearResourceCache(); 9314 } 9315 9316 Map<String, Object> data = new HashMap<String, Object>(2); 9317 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9318 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9319 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9320 return newResource; 9321 } 9322 9323 /** 9324 * Saves a list of aliases for the same structure id, replacing any aliases for the same structure id.<p> 9325 * 9326 * @param dbc the current database context 9327 * @param project the current project 9328 * @param structureId the structure id for which the aliases should be saved 9329 * @param aliases the list of aliases to save 9330 * 9331 * @throws CmsException if something goes wrong 9332 */ 9333 public void saveAliases(CmsDbContext dbc, CmsProject project, CmsUUID structureId, List<CmsAlias> aliases) 9334 throws CmsException { 9335 9336 for (CmsAlias alias : aliases) { 9337 if (!structureId.equals(alias.getStructureId())) { 9338 throw new IllegalArgumentException("Aliases to replace must have the same structure id!"); 9339 } 9340 } 9341 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 9342 vfsDriver.deleteAliases(dbc, project, new CmsAliasFilter(null, null, structureId)); 9343 for (CmsAlias alias : aliases) { 9344 String aliasPath = alias.getAliasPath(); 9345 if (CmsAlias.ALIAS_PATTERN.matcher(aliasPath).matches()) { 9346 vfsDriver.insertAlias(dbc, project, alias); 9347 } else { 9348 LOG.error("Invalid alias path: " + aliasPath); 9349 } 9350 } 9351 } 9352 9353 /** 9354 * Replaces the complete list of rewrite aliases for a given site root.<p> 9355 * 9356 * @param dbc the current database context 9357 * @param siteRoot the site root for which the rewrite aliases should be replaced 9358 * @param newAliases the new aliases for the given site root 9359 * @throws CmsException if something goes wrong 9360 */ 9361 public void saveRewriteAliases(CmsDbContext dbc, String siteRoot, List<CmsRewriteAlias> newAliases) 9362 throws CmsException { 9363 9364 CmsRewriteAliasFilter filter = new CmsRewriteAliasFilter().setSiteRoot(siteRoot); 9365 getVfsDriver(dbc).deleteRewriteAliases(dbc, filter); 9366 getVfsDriver(dbc).insertRewriteAliases(dbc, newAliases); 9367 } 9368 9369 /** 9370 * Searches for users which fit the given criteria.<p> 9371 * 9372 * @param dbc the database context 9373 * @param searchParams the search criteria 9374 * 9375 * @return the users which fit the search criteria 9376 * 9377 * @throws CmsDataAccessException if something goes wrong 9378 */ 9379 public List<CmsUser> searchUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams 9380 9381 ) throws CmsDataAccessException { 9382 9383 return getUserDriver(dbc).searchUsers(dbc, searchParams); 9384 } 9385 9386 /** 9387 * Changes the "expire" date of a resource.<p> 9388 * 9389 * @param dbc the current database context 9390 * @param resource the resource to touch 9391 * @param dateExpired the new expire date of the resource 9392 * 9393 * @throws CmsDataAccessException if something goes wrong 9394 * 9395 * @see CmsObject#setDateExpired(String, long, boolean) 9396 * @see I_CmsResourceType#setDateExpired(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9397 */ 9398 public void setDateExpired(CmsDbContext dbc, CmsResource resource, long dateExpired) throws CmsDataAccessException { 9399 9400 resource.setDateExpired(dateExpired); 9401 if (resource.getState().isUnchanged()) { 9402 resource.setState(CmsResource.STATE_CHANGED); 9403 } 9404 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false); 9405 9406 // modify the last modified project reference 9407 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false); 9408 // log 9409 log( 9410 dbc, 9411 new CmsLogEntry( 9412 dbc, 9413 resource.getStructureId(), 9414 CmsLogEntryType.RESOURCE_DATE_EXPIRED, 9415 new String[] {resource.getRootPath()}), 9416 false); 9417 9418 // clear the cache 9419 m_monitor.clearResourceCache(); 9420 9421 // fire the event 9422 Map<String, Object> data = new HashMap<String, Object>(2); 9423 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9424 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_TIMEFRAME)); 9425 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9426 } 9427 9428 /** 9429 * Changes the "last modified" timestamp of a resource.<p> 9430 * 9431 * @param dbc the current database context 9432 * @param resource the resource to touch 9433 * @param dateLastModified the new last modified date of the resource 9434 * 9435 * @throws CmsDataAccessException if something goes wrong 9436 * 9437 * @see CmsObject#setDateLastModified(String, long, boolean) 9438 * @see I_CmsResourceType#setDateLastModified(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9439 */ 9440 public void setDateLastModified(CmsDbContext dbc, CmsResource resource, long dateLastModified) 9441 throws CmsDataAccessException { 9442 9443 // modify the last modification date 9444 resource.setDateLastModified(dateLastModified); 9445 if (resource.getState().isUnchanged()) { 9446 resource.setState(CmsResource.STATE_CHANGED); 9447 } else if (resource.getState().isNew() && (resource.getSiblingCount() > 1)) { 9448 // in case of new resources with siblings make sure the state is correct 9449 resource.setState(CmsResource.STATE_CHANGED); 9450 } 9451 resource.setUserLastModified(dbc.currentUser().getId()); 9452 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false); 9453 9454 log( 9455 dbc, 9456 new CmsLogEntry( 9457 dbc, 9458 resource.getStructureId(), 9459 CmsLogEntryType.RESOURCE_TOUCHED, 9460 new String[] {resource.getRootPath()}), 9461 false); 9462 9463 // clear the cache 9464 m_monitor.clearResourceCache(); 9465 9466 // fire the event 9467 Map<String, Object> data = new HashMap<String, Object>(2); 9468 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9469 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_LASTMODIFIED)); 9470 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9471 } 9472 9473 /** 9474 * Changes the "release" date of a resource.<p> 9475 * 9476 * @param dbc the current database context 9477 * @param resource the resource to touch 9478 * @param dateReleased the new release date of the resource 9479 * 9480 * @throws CmsDataAccessException if something goes wrong 9481 * 9482 * @see CmsObject#setDateReleased(String, long, boolean) 9483 * @see I_CmsResourceType#setDateReleased(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9484 */ 9485 public void setDateReleased(CmsDbContext dbc, CmsResource resource, long dateReleased) 9486 throws CmsDataAccessException { 9487 9488 // modify the last modification date 9489 resource.setDateReleased(dateReleased); 9490 if (resource.getState().isUnchanged()) { 9491 resource.setState(CmsResource.STATE_CHANGED); 9492 } 9493 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false); 9494 9495 // modify the last modified project reference 9496 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false); 9497 // log it 9498 log( 9499 dbc, 9500 new CmsLogEntry( 9501 dbc, 9502 resource.getStructureId(), 9503 CmsLogEntryType.RESOURCE_DATE_RELEASED, 9504 new String[] {resource.getRootPath()}), 9505 false); 9506 9507 // clear the cache 9508 m_monitor.clearResourceCache(); 9509 9510 // fire the event 9511 Map<String, Object> data = new HashMap<String, Object>(2); 9512 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9513 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_TIMEFRAME)); 9514 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9515 } 9516 9517 /** 9518 * Sets a new parent group for an already existing group.<p> 9519 * 9520 * @param dbc the current database context 9521 * @param groupName the name of the group that should be written 9522 * @param parentGroupName the name of the parent group to set, 9523 * or <code>null</code> if the parent 9524 * group should be deleted. 9525 * 9526 * @throws CmsException if operation was not successful 9527 * @throws CmsDataAccessException if the group with <code>groupName</code> could not be read from VFS 9528 */ 9529 public void setParentGroup(CmsDbContext dbc, String groupName, String parentGroupName) 9530 throws CmsException, CmsDataAccessException { 9531 9532 CmsGroup group = readGroup(dbc, groupName); 9533 CmsUUID parentGroupId = CmsUUID.getNullUUID(); 9534 9535 // if the group exists, use its id, else set to unknown. 9536 if (parentGroupName != null) { 9537 parentGroupId = readGroup(dbc, parentGroupName).getId(); 9538 } 9539 9540 group.setParentId(parentGroupId); 9541 9542 // write the changes to the cms 9543 writeGroup(dbc, group); 9544 } 9545 9546 /** 9547 * Sets the password for a user.<p> 9548 * 9549 * @param dbc the current database context 9550 * @param username the name of the user 9551 * @param newPassword the new password 9552 * 9553 * @throws CmsException if operation was not successful 9554 * @throws CmsIllegalArgumentException if the user with the <code>username</code> was not found 9555 */ 9556 public void setPassword(CmsDbContext dbc, String username, String newPassword) 9557 throws CmsException, CmsIllegalArgumentException { 9558 9559 if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) { 9560 validatePassword(newPassword); 9561 } 9562 9563 // read the user as a system user to verify that the specified old password is correct 9564 CmsUser user = getUserDriver(dbc).readUser(dbc, username); 9565 // only continue if not found and read user from web might succeed 9566 getUserDriver(dbc).writePassword(dbc, username, null, newPassword); 9567 user.getAdditionalInfo().put( 9568 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 9569 "" + System.currentTimeMillis()); 9570 getUserDriver(dbc).writeUser(dbc, user); 9571 9572 // fire user modified event 9573 Map<String, Object> eventData = new HashMap<String, Object>(); 9574 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9575 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD); 9576 eventData.put( 9577 I_CmsEventListener.KEY_USER_CHANGES, 9578 Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA)); 9579 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9580 } 9581 9582 /** 9583 * Marks a subscribed resource as deleted.<p> 9584 * 9585 * @param dbc the database context 9586 * @param poolName the name of the database pool to use 9587 * @param resource the subscribed resource to mark as deleted 9588 * 9589 * @throws CmsException if something goes wrong 9590 */ 9591 public void setSubscribedResourceAsDeleted(CmsDbContext dbc, String poolName, CmsResource resource) 9592 throws CmsException { 9593 9594 getSubscriptionDriver().setSubscribedResourceAsDeleted(dbc, poolName, resource); 9595 } 9596 9597 /** 9598 * Moves an user to the given organizational unit.<p> 9599 * 9600 * @param dbc the current db context 9601 * @param orgUnit the organizational unit to add the resource to 9602 * @param user the user that is to be moved to the organizational unit 9603 * 9604 * @throws CmsException if something goes wrong 9605 * 9606 * @see org.opencms.security.CmsOrgUnitManager#setUsersOrganizationalUnit(CmsObject, String, String) 9607 */ 9608 public void setUsersOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsUser user) 9609 throws CmsException { 9610 9611 if (!getGroupsOfUser(dbc, user.getName(), false).isEmpty()) { 9612 throw new CmsDbConsistencyException( 9613 Messages.get().container(Messages.ERR_ORGUNIT_MOVE_USER_2, orgUnit.getName(), user.getName())); 9614 } 9615 9616 // move the principal 9617 getUserDriver(dbc).setUsersOrganizationalUnit(dbc, orgUnit, user); 9618 // remove the principal from cache 9619 m_monitor.clearUserCache(user); 9620 9621 if (!dbc.getProjectId().isNullUUID()) { 9622 // user modified event is not needed 9623 return; 9624 } 9625 // fire user modified event 9626 Map<String, Object> eventData = new HashMap<String, Object>(); 9627 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9628 eventData.put(I_CmsEventListener.KEY_OU_NAME, user.getOuFqn()); 9629 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU); 9630 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9631 } 9632 9633 /** 9634 * Subscribes the user or group to the resource.<p> 9635 * 9636 * @param dbc the database context 9637 * @param poolName the name of the database pool to use 9638 * @param principal the principal that subscribes to the resource 9639 * @param resource the resource to subscribe to 9640 * 9641 * @throws CmsException if something goes wrong 9642 */ 9643 public void subscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource) 9644 throws CmsException { 9645 9646 getSubscriptionDriver().subscribeResourceFor(dbc, poolName, principal, resource); 9647 } 9648 9649 /** 9650 * Undelete the resource.<p> 9651 * 9652 * @param dbc the current database context 9653 * @param resource the name of the resource to apply this operation to 9654 * 9655 * @throws CmsException if something goes wrong 9656 * 9657 * @see CmsObject#undeleteResource(String, boolean) 9658 * @see I_CmsResourceType#undelete(CmsObject, CmsSecurityManager, CmsResource, boolean) 9659 */ 9660 public void undelete(CmsDbContext dbc, CmsResource resource) throws CmsException { 9661 9662 if (!resource.getState().isDeleted()) { 9663 throw new CmsVfsException( 9664 Messages.get().container( 9665 Messages.ERR_UNDELETE_FOR_RESOURCE_DELETED_1, 9666 dbc.removeSiteRoot(resource.getRootPath()))); 9667 } 9668 9669 // set the state to changed 9670 resource.setState(CmsResourceState.STATE_CHANGED); 9671 // perform the changes 9672 updateState(dbc, resource, false); 9673 // log it 9674 log( 9675 dbc, 9676 new CmsLogEntry( 9677 dbc, 9678 resource.getStructureId(), 9679 CmsLogEntryType.RESOURCE_UNDELETED, 9680 new String[] {resource.getRootPath()}), 9681 false); 9682 // clear the cache 9683 m_monitor.clearResourceCache(); 9684 9685 // fire change event 9686 Map<String, Object> data = new HashMap<String, Object>(2); 9687 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9688 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE)); 9689 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9690 } 9691 9692 /** 9693 * Undos all changes in the resource by restoring the version from the 9694 * online project to the current offline project.<p> 9695 * 9696 * @param dbc the current database context 9697 * @param resource the name of the resource to apply this operation to 9698 * @param mode the undo mode, one of the <code>{@link org.opencms.file.CmsResource.CmsResourceUndoMode}#UNDO_XXX</code> constants 9699 * please note that the recursive flag is ignored at this level 9700 * 9701 * @throws CmsException if something goes wrong 9702 * 9703 * @see CmsObject#undoChanges(String, CmsResource.CmsResourceUndoMode) 9704 * @see I_CmsResourceType#undoChanges(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode) 9705 */ 9706 public void undoChanges(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceUndoMode mode) 9707 throws CmsException { 9708 9709 if (resource.getState().isNew()) { 9710 // undo changes is impossible on a new resource 9711 throw new CmsVfsException(Messages.get().container(Messages.ERR_UNDO_CHANGES_FOR_RESOURCE_NEW_0)); 9712 } 9713 9714 // we need this for later use 9715 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 9716 // read the resource from the online project 9717 CmsResource onlineResource = getVfsDriver( 9718 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getStructureId(), true); 9719 9720 CmsResource onlineResourceByPath = null; 9721 try { 9722 // this is needed to figure out if a moved resource overwrote a deleted one 9723 onlineResourceByPath = getVfsDriver( 9724 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getRootPath(), true); 9725 9726 // force undo move operation if needed 9727 if (!mode.isUndoMove() && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) { 9728 mode = mode.includeMove(); 9729 } 9730 } catch (Exception e) { 9731 // ok 9732 } 9733 9734 boolean moved = !onlineResource.getRootPath().equals(resource.getRootPath()); 9735 // undo move operation if required 9736 if (moved && mode.isUndoMove()) { 9737 moveResource(dbc, resource, onlineResource.getRootPath(), true); 9738 if ((onlineResourceByPath != null) 9739 && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) { 9740 // was moved over deleted, so the deleted file has to be undone 9741 undoContentChanges(dbc, onlineProject, null, onlineResourceByPath, CmsResource.STATE_UNCHANGED, true); 9742 } 9743 } 9744 // undo content changes 9745 CmsResourceState newState = CmsResource.STATE_UNCHANGED; 9746 if (moved && !mode.isUndoMove()) { 9747 newState = CmsResource.STATE_CHANGED; 9748 } 9749 undoContentChanges(dbc, onlineProject, resource, onlineResource, newState, moved && mode.isUndoMove()); 9750 // because undoContentChanges deletes the offline resource internally, we have 9751 // to write an entry to the log table to prevent the resource from appearing in the 9752 // user's publish list. 9753 log( 9754 dbc, 9755 new CmsLogEntry( 9756 dbc, 9757 resource.getStructureId(), 9758 CmsLogEntryType.RESOURCE_CHANGES_UNDONE, 9759 new String[] {resource.getRootPath()}), 9760 true); 9761 9762 } 9763 9764 /** 9765 * Unlocks all resources in the given project.<p> 9766 * 9767 * @param project the project to unlock the resources in 9768 */ 9769 public void unlockProject(CmsProject project) { 9770 9771 // unlock all resources in the project 9772 m_lockManager.removeResourcesInProject(project.getUuid(), false); 9773 m_monitor.clearResourceCache(); 9774 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT, CmsMemoryMonitor.CacheType.PERMISSION); 9775 } 9776 9777 /** 9778 * Unlocks a resource.<p> 9779 * 9780 * @param dbc the current database context 9781 * @param resource the resource to unlock 9782 * @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 9783 * @param removeSystemLock <code>true</code>, if you also want to remove system locks 9784 * 9785 * @throws CmsException if something goes wrong 9786 * 9787 * @see CmsObject#unlockResource(String) 9788 * @see I_CmsResourceType#unlockResource(CmsObject, CmsSecurityManager, CmsResource) 9789 */ 9790 public void unlockResource(CmsDbContext dbc, CmsResource resource, boolean force, boolean removeSystemLock) 9791 throws CmsException { 9792 9793 // update the resource cache 9794 m_monitor.clearResourceCache(); 9795 9796 // now update lock status 9797 m_lockManager.removeResource(dbc, resource, force, removeSystemLock); 9798 9799 // we must also clear the permission cache 9800 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION); 9801 9802 // fire resource modification event 9803 Map<String, Object> data = new HashMap<String, Object>(2); 9804 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9805 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(NOTHING_CHANGED)); 9806 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9807 } 9808 9809 /** 9810 * Unsubscribes all deleted resources that were deleted before the specified time stamp.<p> 9811 * 9812 * @param dbc the database context 9813 * @param poolName the name of the database pool to use 9814 * @param deletedTo the time stamp to which the resources have been deleted 9815 * 9816 * @throws CmsException if something goes wrong 9817 */ 9818 public void unsubscribeAllDeletedResources(CmsDbContext dbc, String poolName, long deletedTo) throws CmsException { 9819 9820 getSubscriptionDriver().unsubscribeAllDeletedResources(dbc, poolName, deletedTo); 9821 } 9822 9823 /** 9824 * Unsubscribes the principal from all resources.<p> 9825 * 9826 * @param dbc the database context 9827 * @param poolName the name of the database pool to use 9828 * @param principal the principal that unsubscribes from all resources 9829 * 9830 * @throws CmsException if something goes wrong 9831 */ 9832 public void unsubscribeAllResourcesFor(CmsDbContext dbc, String poolName, CmsPrincipal principal) 9833 throws CmsException { 9834 9835 getSubscriptionDriver().unsubscribeAllResourcesFor(dbc, poolName, principal); 9836 9837 } 9838 9839 /** 9840 * Unsubscribes the principal from the resource.<p> 9841 * 9842 * @param dbc the database context 9843 * @param poolName the name of the database pool to use 9844 * @param principal the principal that unsubscribes from the resource 9845 * @param resource the resource to unsubscribe from 9846 * 9847 * @throws CmsException if something goes wrong 9848 */ 9849 public void unsubscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource) 9850 throws CmsException { 9851 9852 getSubscriptionDriver().unsubscribeResourceFor(dbc, poolName, principal, resource); 9853 } 9854 9855 /** 9856 * Unsubscribes all groups and users from the resource.<p> 9857 * 9858 * @param dbc the database context 9859 * @param poolName the name of the database pool to use 9860 * @param resource the resource to unsubscribe all groups and users from 9861 * 9862 * @throws CmsException if something goes wrong 9863 */ 9864 public void unsubscribeResourceForAll(CmsDbContext dbc, String poolName, CmsResource resource) throws CmsException { 9865 9866 getSubscriptionDriver().unsubscribeResourceForAll(dbc, poolName, resource); 9867 } 9868 9869 /** 9870 * Update the export points.<p> 9871 * 9872 * All files and folders "inside" an export point are written.<p> 9873 * 9874 * @param dbc the current database context 9875 */ 9876 public void updateExportPoints(CmsDbContext dbc) { 9877 9878 try { 9879 // read the export points and return immediately if there are no export points at all 9880 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 9881 exportPoints.addAll(OpenCms.getExportPoints()); 9882 exportPoints.addAll(OpenCms.getModuleManager().getExportPoints()); 9883 if (exportPoints.size() == 0) { 9884 if (LOG.isWarnEnabled()) { 9885 LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0)); 9886 } 9887 return; 9888 } 9889 9890 // create the driver to write the export points 9891 I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver( 9892 exportPoints); 9893 9894 // the export point hash table contains RFS export paths keyed by their internal VFS paths 9895 Iterator<String> i = exportPointDriver.getExportPointPaths().iterator(); 9896 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 9897 while (i.hasNext()) { 9898 String currentExportPoint = i.next(); 9899 9900 // print some report messages 9901 if (LOG.isInfoEnabled()) { 9902 LOG.info(Messages.get().getBundle().key(Messages.LOG_WRITE_EXPORT_POINT_1, currentExportPoint)); 9903 } 9904 9905 try { 9906 CmsResourceFilter filter = CmsResourceFilter.DEFAULT; 9907 List<CmsResource> resources = vfsDriver.readResourceTree( 9908 dbc, 9909 CmsProject.ONLINE_PROJECT_ID, 9910 currentExportPoint, 9911 filter.getType(), 9912 filter.getState(), 9913 filter.getModifiedAfter(), 9914 filter.getModifiedBefore(), 9915 filter.getReleaseAfter(), 9916 filter.getReleaseBefore(), 9917 filter.getExpireAfter(), 9918 filter.getExpireBefore(), 9919 CmsDriverManager.READMODE_INCLUDE_TREE 9920 | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0) 9921 | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0)); 9922 9923 Iterator<CmsResource> j = resources.iterator(); 9924 while (j.hasNext()) { 9925 CmsResource currentResource = j.next(); 9926 9927 if (currentResource.isFolder()) { 9928 // export the folder 9929 exportPointDriver.createFolder(currentResource.getRootPath(), currentExportPoint); 9930 } else { 9931 // try to create the exportpoint folder 9932 exportPointDriver.createFolder(currentExportPoint, currentExportPoint); 9933 byte[] onlineContent = vfsDriver.readContent( 9934 dbc, 9935 CmsProject.ONLINE_PROJECT_ID, 9936 currentResource.getResourceId()); 9937 // export the file content online 9938 exportPointDriver.writeFile( 9939 currentResource.getRootPath(), 9940 currentExportPoint, 9941 onlineContent); 9942 } 9943 } 9944 } catch (CmsException e) { 9945 // there might exist export points without corresponding resources in the VFS 9946 // -> ignore exceptions which are not "resource not found" exception quiet here 9947 if (e instanceof CmsVfsResourceNotFoundException) { 9948 if (LOG.isErrorEnabled()) { 9949 LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e); 9950 } 9951 } 9952 } 9953 } 9954 } catch (Exception e) { 9955 if (LOG.isErrorEnabled()) { 9956 LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e); 9957 } 9958 } 9959 } 9960 9961 /** 9962 * Updates the last login date on the given user to the current time.<p> 9963 * 9964 * @param dbc the current database context 9965 * @param user the user to be updated 9966 * 9967 * @throws CmsException if operation was not successful 9968 */ 9969 public void updateLastLoginDate(CmsDbContext dbc, CmsUser user) throws CmsException { 9970 9971 m_monitor.clearUserCache(user); 9972 // set the last login time to the current time 9973 user.setLastlogin(System.currentTimeMillis()); 9974 dbc.setAttribute(ATTRIBUTE_LOGIN, user.getName()); 9975 getUserDriver(dbc).writeUser(dbc, user); 9976 // update cache 9977 m_monitor.cacheUser(user); 9978 9979 // invalidate all user dependent caches 9980 m_monitor.flushCache( 9981 CmsMemoryMonitor.CacheType.ACL, 9982 CmsMemoryMonitor.CacheType.GROUP, 9983 CmsMemoryMonitor.CacheType.ORG_UNIT, 9984 CmsMemoryMonitor.CacheType.USER_LIST, 9985 CmsMemoryMonitor.CacheType.PERMISSION, 9986 CmsMemoryMonitor.CacheType.RESOURCE_LIST); 9987 9988 // fire user modified event 9989 Map<String, Object> eventData = new HashMap<String, Object>(); 9990 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9991 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 9992 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 9993 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(CmsUser.FLAG_LAST_LOGIN)); 9994 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9995 } 9996 9997 /** 9998 * Logs everything that has not been written to DB jet.<p> 9999 * 10000 * @param dbc the current db context 10001 * 10002 * @throws CmsDataAccessException if something goes wrong 10003 */ 10004 public void updateLog(CmsDbContext dbc) throws CmsDataAccessException { 10005 10006 synchronized (m_publishListUpdateLock) { 10007 10008 if (m_log.isEmpty()) { 10009 return; 10010 } 10011 10012 List<CmsLogEntry> log = new ArrayList<CmsLogEntry>(m_log); 10013 m_log.clear(); 10014 String logTableEnabledStr = (String)OpenCms.getRuntimeProperty(PARAM_LOG_TABLE_ENABLED); 10015 if (Boolean.parseBoolean(logTableEnabledStr)) { // defaults to 'false' if value not set 10016 m_projectDriver.log(dbc, log); 10017 } 10018 A_CmsLogPublishListConverter converter = null; 10019 switch (OpenCms.getPublishManager().getPublishListRemoveMode()) { 10020 case currentUser: 10021 converter = new CmsLogPublishListConverterCurrentUser(); 10022 break; 10023 case allUsers: 10024 default: 10025 converter = new CmsLogPublishListConverterAllUsers(); 10026 break; 10027 } 10028 for (CmsLogEntry entry : log) { 10029 converter.add(entry); 10030 } 10031 converter.writeChangesToDatabase(dbc, m_projectDriver); 10032 } 10033 } 10034 10035 /** 10036 * Updates/Creates the given relations for the given resource.<p> 10037 * 10038 * @param dbc the db context 10039 * @param resource the resource to update the relations for 10040 * @param links the links to consider for updating 10041 * @param updateSiblingState if true, sets the state of siblings whose relations have changed to 'changed' (unless they are new or deleted) 10042 * 10043 * @throws CmsException if something goes wrong 10044 * 10045 * @see CmsSecurityManager#updateRelationsForResource(CmsRequestContext, CmsResource, List) 10046 */ 10047 public void updateRelationsForResource( 10048 CmsDbContext dbc, 10049 CmsResource resource, 10050 List<CmsLink> links, 10051 boolean updateSiblingState) 10052 throws CmsException { 10053 10054 if (links == null) { 10055 links = new ArrayList<>(); 10056 } 10057 10058 // create new relation information 10059 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10060 Iterator<CmsLink> itLinks = links.iterator(); 10061 Set<CmsRelation> relationsForOriginalResource = new HashSet<>(); 10062 while (itLinks.hasNext()) { 10063 CmsLink link = itLinks.next(); 10064 if (link.isInternal()) { // only update internal links 10065 if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) { 10066 // only an anchor 10067 continue; 10068 } 10069 CmsUUID targetId = link.getStructureId(); 10070 String destPath = link.getTarget(); 10071 10072 if (targetId != null) { 10073 // the link target may not be a VFS path even if the link id is a structure id, 10074 // so if possible, we read the resource for the id and set the relation target to its 10075 // real root path. 10076 try { 10077 CmsResource destRes = readResource(dbc, targetId, CmsResourceFilter.ALL); 10078 destPath = destRes.getRootPath(); 10079 } catch (CmsVfsResourceNotFoundException e) { 10080 // ignore 10081 } 10082 } 10083 10084 CmsRelation originalRelation = new CmsRelation( 10085 resource.getStructureId(), 10086 resource.getRootPath(), 10087 link.getStructureId(), 10088 destPath, 10089 link.getType()); 10090 relationsForOriginalResource.add(originalRelation); 10091 } 10092 } 10093 List<CmsResource> siblings = resource.getSiblingCount() == 1 10094 ? Arrays.asList(resource) 10095 : readSiblings(dbc, resource, CmsResourceFilter.ALL); 10096 10097 for (CmsResource sibling : siblings) { 10098 // For each sibling, we determine which 'defined in content' relations it SHOULD have, 10099 // and only update the relations if that set differs from the ones it actually has. 10100 // If the updateSiblingState flag is set, then for siblings, we update the structure 10101 // state to changed (unless the state was 'deleted' or 'new'). 10102 // This is so that even if the user later publishes only one sibling, the other sibling will still 10103 // show up as changed in the GUI, so the user can publish it separately (with its updated relations). 10104 Set<CmsRelation> relationsForSibling = relationsForOriginalResource.stream().map( 10105 relation -> new CmsRelation( 10106 sibling.getStructureId(), 10107 sibling.getRootPath(), 10108 relation.getTargetId(), 10109 relation.getTargetPath(), 10110 relation.getType())).collect(Collectors.toSet()); 10111 Set<CmsRelation> existingRelations = new HashSet<>( 10112 vfsDriver.readRelations( 10113 dbc, 10114 dbc.currentProject().getUuid(), 10115 sibling, 10116 CmsRelationFilter.TARGETS.filterDefinedInContent())); 10117 if (!existingRelations.equals(relationsForSibling)) { 10118 vfsDriver.deleteRelations( 10119 dbc, 10120 dbc.currentProject().getUuid(), 10121 sibling, 10122 CmsRelationFilter.TARGETS.filterDefinedInContent()); 10123 for (CmsRelation relation : relationsForSibling) { 10124 vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation); 10125 } 10126 if (!sibling.getState().isDeleted() 10127 && !sibling.getState().isNew() 10128 && (siblings.size() > 1) 10129 && updateSiblingState) { 10130 sibling.setState(CmsResource.STATE_CHANGED); 10131 vfsDriver.writeResourceState( 10132 dbc, 10133 dbc.currentProject(), 10134 sibling, 10135 CmsDriverManager.UPDATE_STRUCTURE_STATE, 10136 false); 10137 } 10138 } 10139 } 10140 } 10141 10142 /** 10143 * Returns <code>true</code> if a user is member of the given group.<p> 10144 * 10145 * @param dbc the current database context 10146 * @param username the name of the user to check 10147 * @param groupname the name of the group to check 10148 * @param readRoles if to read roles or groups 10149 * 10150 * @return <code>true</code>, if the user is in the group, <code>false</code> otherwise 10151 * 10152 * @throws CmsException if something goes wrong 10153 */ 10154 public boolean userInGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 10155 throws CmsException { 10156 10157 List<CmsGroup> groups = getGroupsOfUser(dbc, username, readRoles); 10158 for (int i = 0; i < groups.size(); i++) { 10159 CmsGroup group = groups.get(i); 10160 if (groupname.equals(group.getName()) || groupname.substring(1).equals(group.getName())) { 10161 return true; 10162 } 10163 } 10164 return false; 10165 } 10166 10167 /** 10168 * This method checks if a new password follows the rules for 10169 * new passwords, which are defined by a Class implementing the 10170 * <code>{@link org.opencms.security.I_CmsPasswordHandler}</code> 10171 * interface and configured in the opencms.properties file.<p> 10172 * 10173 * If this method throws no exception the password is valid.<p> 10174 * 10175 * @param password the new password that has to be checked 10176 * 10177 * @throws CmsSecurityException if the password is not valid 10178 */ 10179 public void validatePassword(String password) throws CmsSecurityException { 10180 10181 OpenCms.getPasswordHandler().validatePassword(password); 10182 } 10183 10184 /** 10185 * Validates the relations for the given resources.<p> 10186 * 10187 * @param dbc the database context 10188 * @param publishList the resources to validate during publishing 10189 * @param report a report to write the messages to 10190 * 10191 * @return a map with lists of invalid links 10192 * (<code>{@link org.opencms.relations.CmsRelation}}</code> objects) 10193 * keyed by root paths 10194 * 10195 * @throws Exception if something goes wrong 10196 */ 10197 public Map<String, List<CmsRelation>> validateRelations( 10198 CmsDbContext dbc, 10199 CmsPublishList publishList, 10200 I_CmsReport report) 10201 throws Exception { 10202 10203 return m_htmlLinkValidator.validateResources(dbc, publishList, report); 10204 } 10205 10206 /** 10207 * Writes an access control entries to a given resource.<p> 10208 * 10209 * @param dbc the current database context 10210 * @param resource the resource 10211 * @param ace the entry to write 10212 * 10213 * @throws CmsException if something goes wrong 10214 */ 10215 public void writeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsAccessControlEntry ace) 10216 throws CmsException { 10217 10218 // write the new ace 10219 getUserDriver(dbc).writeAccessControlEntry(dbc, dbc.currentProject(), ace); 10220 10221 // log it 10222 log( 10223 dbc, 10224 new CmsLogEntry( 10225 dbc, 10226 resource.getStructureId(), 10227 CmsLogEntryType.RESOURCE_PERMISSIONS, 10228 new String[] {resource.getRootPath()}), 10229 false); 10230 10231 // update the "last modified" information 10232 setDateLastModified(dbc, resource, resource.getDateLastModified()); 10233 10234 // clear the cache 10235 m_monitor.clearAccessControlListCache(); 10236 10237 // fire a resource modification event 10238 Map<String, Object> data = new HashMap<String, Object>(2); 10239 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10240 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 10241 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10242 } 10243 10244 /** 10245 * Writes all export points into the file system for the publish task 10246 * specified by trhe given publish history ID.<p> 10247 * 10248 * @param dbc the current database context 10249 * @param report an I_CmsReport instance to print output message, or null to write messages to the log file 10250 * @param publishHistoryId ID to identify the publish task in the publish history 10251 */ 10252 public void writeExportPoints(CmsDbContext dbc, I_CmsReport report, CmsUUID publishHistoryId) { 10253 10254 boolean printReportHeaders = false; 10255 List<CmsPublishedResource> publishedResources = null; 10256 try { 10257 // read the "published resources" for the specified publish history ID 10258 publishedResources = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId); 10259 } catch (CmsException e) { 10260 if (LOG.isErrorEnabled()) { 10261 LOG.error( 10262 Messages.get().getBundle().key(Messages.ERR_READ_PUBLISHED_RESOURCES_FOR_ID_1, publishHistoryId), 10263 e); 10264 } 10265 } 10266 if ((publishedResources == null) || publishedResources.isEmpty()) { 10267 if (LOG.isWarnEnabled()) { 10268 LOG.warn(Messages.get().getBundle().key(Messages.LOG_EMPTY_PUBLISH_HISTORY_1, publishHistoryId)); 10269 } 10270 return; 10271 } 10272 10273 // read the export points and return immediately if there are no export points at all 10274 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 10275 exportPoints.addAll(OpenCms.getExportPoints()); 10276 exportPoints.addAll(OpenCms.getModuleManager().getExportPoints()); 10277 if (exportPoints.size() == 0) { 10278 if (LOG.isWarnEnabled()) { 10279 LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0)); 10280 } 10281 return; 10282 } 10283 10284 // create the driver to write the export points 10285 I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver( 10286 exportPoints); 10287 10288 // the report may be null if the export point write was started by an event 10289 if (report == null) { 10290 if (dbc.getRequestContext() != null) { 10291 report = new CmsLogReport(dbc.getRequestContext().getLocale(), getClass()); 10292 } else { 10293 report = new CmsLogReport(CmsLocaleManager.getDefaultLocale(), getClass()); 10294 } 10295 } 10296 10297 // iterate over all published resources to export them 10298 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10299 Iterator<CmsPublishedResource> i = publishedResources.iterator(); 10300 while (i.hasNext()) { 10301 CmsPublishedResource currentPublishedResource = i.next(); 10302 String currentExportPoint = exportPointDriver.getExportPoint(currentPublishedResource.getRootPath()); 10303 10304 if (currentExportPoint != null) { 10305 if (!printReportHeaders) { 10306 report.println( 10307 Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_BEGIN_0), 10308 I_CmsReport.FORMAT_HEADLINE); 10309 printReportHeaders = true; 10310 } 10311 10312 // print report message 10313 if (currentPublishedResource.getState().isDeleted()) { 10314 report.print( 10315 Messages.get().container(Messages.RPT_EXPORT_POINTS_DELETE_0), 10316 I_CmsReport.FORMAT_NOTE); 10317 } else { 10318 report.print(Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_0), I_CmsReport.FORMAT_NOTE); 10319 } 10320 report.print( 10321 org.opencms.report.Messages.get().container( 10322 org.opencms.report.Messages.RPT_ARGUMENT_1, 10323 currentPublishedResource.getRootPath())); 10324 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 10325 10326 if (currentPublishedResource.isFolder()) { 10327 // export the folder 10328 if (currentPublishedResource.getState().isDeleted()) { 10329 exportPointDriver.deleteResource(currentPublishedResource.getRootPath(), currentExportPoint); 10330 } else { 10331 exportPointDriver.createFolder(currentPublishedResource.getRootPath(), currentExportPoint); 10332 } 10333 report.println( 10334 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 10335 I_CmsReport.FORMAT_OK); 10336 } else { 10337 // export the file 10338 try { 10339 if (currentPublishedResource.getState().isDeleted()) { 10340 exportPointDriver.deleteResource( 10341 currentPublishedResource.getRootPath(), 10342 currentExportPoint); 10343 } else { 10344 // read the file content online 10345 byte[] onlineContent = vfsDriver.readContent( 10346 dbc, 10347 CmsProject.ONLINE_PROJECT_ID, 10348 currentPublishedResource.getResourceId()); 10349 exportPointDriver.writeFile( 10350 currentPublishedResource.getRootPath(), 10351 currentExportPoint, 10352 onlineContent); 10353 } 10354 report.println( 10355 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 10356 I_CmsReport.FORMAT_OK); 10357 } catch (CmsException e) { 10358 if (LOG.isErrorEnabled()) { 10359 LOG.error( 10360 Messages.get().getBundle().key( 10361 Messages.LOG_WRITE_EXPORT_POINT_ERROR_1, 10362 currentPublishedResource.getRootPath()), 10363 e); 10364 } 10365 report.println( 10366 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 10367 I_CmsReport.FORMAT_ERROR); 10368 } 10369 } 10370 } 10371 } 10372 if (printReportHeaders) { 10373 report.println( 10374 Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_END_0), 10375 I_CmsReport.FORMAT_HEADLINE); 10376 } 10377 } 10378 10379 /** 10380 * Writes a resource to the OpenCms VFS, including it's content.<p> 10381 * 10382 * Applies only to resources of type <code>{@link CmsFile}</code> 10383 * i.e. resources that have a binary content attached.<p> 10384 * 10385 * Certain resource types might apply content validation or transformation rules 10386 * before the resource is actually written to the VFS. The returned result 10387 * might therefore be a modified version from the provided original.<p> 10388 * 10389 * @param dbc the current database context 10390 * @param resource the resource to apply this operation to 10391 * 10392 * @return the written resource (may have been modified) 10393 * 10394 * @throws CmsException if something goes wrong 10395 * 10396 * @see CmsObject#writeFile(CmsFile) 10397 * @see I_CmsResourceType#writeFile(CmsObject, CmsSecurityManager, CmsFile) 10398 */ 10399 public CmsFile writeFile(CmsDbContext dbc, CmsFile resource) throws CmsException { 10400 10401 resource.setUserLastModified(dbc.currentUser().getId()); 10402 resource.setContents(resource.getContents()); // to be sure the content date is updated 10403 10404 getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), resource, UPDATE_RESOURCE_STATE); 10405 10406 byte[] contents = resource.getContents(); 10407 getVfsDriver(dbc).writeContent(dbc, resource.getResourceId(), contents); 10408 // log it 10409 log( 10410 dbc, 10411 new CmsLogEntry( 10412 dbc, 10413 resource.getStructureId(), 10414 CmsLogEntryType.RESOURCE_CONTENT_MODIFIED, 10415 new String[] {resource.getRootPath()}), 10416 false); 10417 10418 // read the file back from db 10419 resource = new CmsFile(readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL)); 10420 resource.setContents(contents); 10421 10422 deleteRelationsWithSiblings(dbc, resource); 10423 10424 // update the cache 10425 m_monitor.clearResourceCache(); 10426 10427 Map<String, Object> data = new HashMap<String, Object>(2); 10428 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10429 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_CONTENT)); 10430 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10431 10432 return resource; 10433 } 10434 10435 /** 10436 * Writes an already existing group.<p> 10437 * 10438 * The group id has to be a valid OpenCms group id.<br> 10439 * 10440 * The group with the given id will be completely overridden 10441 * by the given data.<p> 10442 * 10443 * @param dbc the current database context 10444 * @param group the group that should be written 10445 * 10446 * @throws CmsException if operation was not successful 10447 */ 10448 public void writeGroup(CmsDbContext dbc, CmsGroup group) throws CmsException { 10449 10450 CmsGroup oldGroup = readGroup(dbc, group.getName()); 10451 m_monitor.uncacheGroup(oldGroup); 10452 getUserDriver(dbc).writeGroup(dbc, group); 10453 m_monitor.cacheGroup(group); 10454 10455 if (!dbc.getProjectId().isNullUUID()) { 10456 // group modified event is not needed 10457 return; 10458 } 10459 // fire group modified event 10460 Map<String, Object> eventData = new HashMap<String, Object>(); 10461 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 10462 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, oldGroup.getName()); 10463 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_WRITE); 10464 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 10465 } 10466 10467 /** 10468 * Creates an historical entry of the current project.<p> 10469 * 10470 * @param dbc the current database context 10471 * @param publishTag the version 10472 * @param publishDate the date of publishing 10473 * 10474 * @throws CmsDataAccessException if operation was not successful 10475 */ 10476 public void writeHistoryProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException { 10477 10478 getHistoryDriver(dbc).writeProject(dbc, publishTag, publishDate); 10479 } 10480 10481 /** 10482 * Writes the locks that are currently stored in-memory to the database to allow restoring them 10483 * in future server startups.<p> 10484 * 10485 * This overwrites the locks previously stored in the underlying database table.<p> 10486 * 10487 * @param dbc the current database context 10488 * 10489 * @throws CmsException if something goes wrong 10490 */ 10491 public void writeLocks(CmsDbContext dbc) throws CmsException { 10492 10493 m_lockManager.writeLocks(dbc); 10494 } 10495 10496 /** 10497 * Writes an already existing organizational unit.<p> 10498 * 10499 * The organizational unit id has to be a valid OpenCms organizational unit id.<br> 10500 * 10501 * The organizational unit with the given id will be completely overridden 10502 * by the given data.<p> 10503 * 10504 * @param dbc the current db context 10505 * @param organizationalUnit the organizational unit that should be written 10506 * 10507 * @throws CmsException if operation was not successful 10508 * 10509 * @see org.opencms.security.CmsOrgUnitManager#writeOrganizationalUnit(CmsObject, CmsOrganizationalUnit) 10510 */ 10511 public void writeOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) 10512 throws CmsException { 10513 10514 m_monitor.uncacheOrgUnit(organizationalUnit); 10515 getUserDriver(dbc).writeOrganizationalUnit(dbc, organizationalUnit); 10516 10517 // create a publish list for the 'virtual' publish event 10518 CmsResource ouRes = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT); 10519 CmsPublishList pl = new CmsPublishList(ouRes, false); 10520 pl.add(ouRes, false); 10521 10522 getProjectDriver(dbc).writePublishHistory( 10523 dbc, 10524 pl.getPublishHistoryId(), 10525 new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW)); 10526 10527 // fire the 'virtual' publish event 10528 Map<String, Object> eventData = new HashMap<String, Object>(); 10529 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 10530 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 10531 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 10532 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 10533 OpenCms.fireCmsEvent(afterPublishEvent); 10534 10535 m_monitor.cacheOrgUnit(organizationalUnit); 10536 } 10537 10538 /** 10539 * Writes an already existing project.<p> 10540 * 10541 * The project id has to be a valid OpenCms project id.<br> 10542 * 10543 * The project with the given id will be completely overridden 10544 * by the given data.<p> 10545 * 10546 * @param dbc the current database context 10547 * @param project the project that should be written 10548 * 10549 * @throws CmsException if operation was not successful 10550 */ 10551 public void writeProject(CmsDbContext dbc, CmsProject project) throws CmsException { 10552 10553 m_monitor.uncacheProject(project); 10554 getProjectDriver(dbc).writeProject(dbc, project); 10555 m_monitor.cacheProject(project); 10556 } 10557 10558 /** 10559 * Writes a new project into the PROJECT_LASTMODIFIED field of a resource record.<p> 10560 * 10561 * @param dbc the current database context 10562 * @param resource the resource which should be modified 10563 * @param projectId the project id to write 10564 * 10565 * @throws CmsDataAccessException if the database access fails 10566 */ 10567 public void writeProjectLastModified(CmsDbContext dbc, CmsResource resource, CmsUUID projectId) 10568 throws CmsDataAccessException { 10569 10570 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10571 vfsDriver.writeLastModifiedProjectId(dbc, dbc.currentProject(), projectId, resource); 10572 } 10573 10574 /** 10575 * Writes a property for a specified resource.<p> 10576 * 10577 * @param dbc the current database context 10578 * @param resource the resource to write the property for 10579 * @param property the property to write 10580 * 10581 * @throws CmsException if something goes wrong 10582 * 10583 * @see CmsObject#writePropertyObject(String, CmsProperty) 10584 * @see I_CmsResourceType#writePropertyObject(CmsObject, CmsSecurityManager, CmsResource, CmsProperty) 10585 */ 10586 public void writePropertyObject(CmsDbContext dbc, CmsResource resource, CmsProperty property) throws CmsException { 10587 10588 try { 10589 if (property == CmsProperty.getNullProperty()) { 10590 // skip empty or null properties 10591 return; 10592 } 10593 10594 // test if and what state should be updated 10595 // 0: none, 1: structure, 2: resource 10596 int updateState = getUpdateState(dbc, resource, Collections.singletonList(property)); 10597 10598 // write the property 10599 getVfsDriver(dbc).writePropertyObject(dbc, dbc.currentProject(), resource, property); 10600 10601 if (updateState > 0) { 10602 updateState(dbc, resource, updateState == 2); 10603 } 10604 // log it 10605 log( 10606 dbc, 10607 new CmsLogEntry( 10608 dbc, 10609 resource.getStructureId(), 10610 CmsLogEntryType.RESOURCE_PROPERTIES, 10611 new String[] {resource.getRootPath()}), 10612 false); 10613 10614 } finally { 10615 // update the driver manager cache 10616 m_monitor.clearResourceCache(); 10617 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 10618 10619 // fire an event that a property of a resource has been modified 10620 Map<String, Object> data = new HashMap<String, Object>(); 10621 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10622 data.put("property", property); 10623 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_PROPERTY_MODIFIED, data)); 10624 } 10625 } 10626 10627 /** 10628 * Writes a list of properties for a specified resource.<p> 10629 * 10630 * Code calling this method has to ensure that the no properties 10631 * <code>a, b</code> are contained in the specified list so that <code>a.equals(b)</code>, 10632 * otherwise an exception is thrown.<p> 10633 * 10634 * @param dbc the current database context 10635 * @param resource the resource to write the properties for 10636 * @param properties the list of properties to write 10637 * @param updateState if <code>true</code> the state of the resource will be updated 10638 * 10639 * @throws CmsException if something goes wrong 10640 * 10641 * @see CmsObject#writePropertyObjects(String, List) 10642 * @see I_CmsResourceType#writePropertyObjects(CmsObject, CmsSecurityManager, CmsResource, List) 10643 */ 10644 public void writePropertyObjects( 10645 CmsDbContext dbc, 10646 CmsResource resource, 10647 List<CmsProperty> properties, 10648 boolean updateState) 10649 throws CmsException { 10650 10651 if ((properties == null) || (properties.size() == 0)) { 10652 // skip empty or null lists 10653 return; 10654 } 10655 10656 try { 10657 // the specified list must not contain two or more equal property objects 10658 for (int i = 0, n = properties.size(); i < n; i++) { 10659 Set<String> keyValidationSet = new HashSet<String>(); 10660 CmsProperty property = properties.get(i); 10661 if (!keyValidationSet.contains(property.getName())) { 10662 keyValidationSet.add(property.getName()); 10663 } else { 10664 throw new CmsVfsException( 10665 Messages.get().container(Messages.ERR_VFS_INVALID_PROPERTY_LIST_1, property.getName())); 10666 } 10667 } 10668 10669 // test if and what state should be updated 10670 // 0: none, 1: structure, 2: resource 10671 int updateStateValue = 0; 10672 if (updateState) { 10673 updateStateValue = getUpdateState(dbc, resource, properties); 10674 } 10675 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10676 for (int i = 0; i < properties.size(); i++) { 10677 // write the property 10678 CmsProperty property = properties.get(i); 10679 vfsDriver.writePropertyObject(dbc, dbc.currentProject(), resource, property); 10680 } 10681 10682 if (updateStateValue > 0) { 10683 // update state 10684 updateState(dbc, resource, (updateStateValue == 2)); 10685 } 10686 10687 if (updateState) { 10688 // log it 10689 log( 10690 dbc, 10691 new CmsLogEntry( 10692 dbc, 10693 resource.getStructureId(), 10694 CmsLogEntryType.RESOURCE_PROPERTIES, 10695 new String[] {resource.getRootPath()}), 10696 false); 10697 } 10698 } finally { 10699 // update the driver manager cache 10700 m_monitor.clearResourceCache(); 10701 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 10702 10703 // fire an event that the properties of a resource have been modified 10704 OpenCms.fireCmsEvent( 10705 new CmsEvent( 10706 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 10707 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource))); 10708 } 10709 } 10710 10711 /** 10712 * Updates a publish job.<p> 10713 * 10714 * @param dbc the current database context 10715 * @param publishJob the publish job to update 10716 * 10717 * @throws CmsException if something goes wrong 10718 */ 10719 public void writePublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 10720 10721 getProjectDriver(dbc).writePublishJob(dbc, publishJob); 10722 } 10723 10724 /** 10725 * Writes the publish report for a publish job.<p> 10726 * 10727 * @param dbc the current database context 10728 * @param publishJob the publish job 10729 * @throws CmsException if something goes wrong 10730 */ 10731 public void writePublishReport(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 10732 10733 CmsPublishReport report = (CmsPublishReport)publishJob.removePublishReport(); 10734 10735 if (report != null) { 10736 getProjectDriver(dbc).writePublishReport(dbc, publishJob.getPublishHistoryId(), report.getContents()); 10737 } 10738 } 10739 10740 /** 10741 * Writes a resource to the OpenCms VFS.<p> 10742 * 10743 * @param dbc the current database context 10744 * @param resource the resource to write 10745 * 10746 * @throws CmsException if something goes wrong 10747 */ 10748 public void writeResource(CmsDbContext dbc, CmsResource resource) throws CmsException { 10749 10750 // access was granted - write the resource 10751 resource.setUserLastModified(dbc.currentUser().getId()); 10752 CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) 10753 ? dbc.currentProject().getUuid() 10754 : dbc.getProjectId(); 10755 10756 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE); 10757 10758 // make sure the written resource has the state correctly set 10759 if (resource.getState().isUnchanged()) { 10760 resource.setState(CmsResource.STATE_CHANGED); 10761 } 10762 10763 // delete in content relations if the new type is not parseable 10764 if (!(OpenCms.getResourceManager().getResourceType(resource.getTypeId()) instanceof I_CmsLinkParseable)) { 10765 deleteRelationsWithSiblings(dbc, resource); 10766 } 10767 10768 // update the cache 10769 m_monitor.clearResourceCache(); 10770 Map<String, Object> data = new HashMap<String, Object>(2); 10771 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10772 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE)); 10773 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10774 } 10775 10776 /** 10777 * Inserts an entry in the published resource table.<p> 10778 * 10779 * This is done during static export.<p> 10780 * 10781 * @param dbc the current database context 10782 * @param resourceName The name of the resource to be added to the static export 10783 * @param linkType the type of resource exported (0= non-parameter, 1=parameter) 10784 * @param linkParameter the parameters added to the resource 10785 * @param timestamp a time stamp for writing the data into the db 10786 * 10787 * @throws CmsException if something goes wrong 10788 */ 10789 public void writeStaticExportPublishedResource( 10790 CmsDbContext dbc, 10791 String resourceName, 10792 int linkType, 10793 String linkParameter, 10794 long timestamp) 10795 throws CmsException { 10796 10797 getProjectDriver(dbc).writeStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter, timestamp); 10798 } 10799 10800 /** 10801 * Adds a new url name mapping for a structure id.<p> 10802 * 10803 * Instead of taking the name directly, this method takes an iterator of strings 10804 * which generates candidate URL names on-the-fly. The first generated name which is 10805 * not already mapped to another structure id will be chosen for the new URL name mapping. 10806 * 10807 * @param dbc the current database context 10808 * @param nameSeq the sequence of URL name candidates 10809 * @param structureId the structure id to which the url name should be mapped 10810 * @param locale the locale for which the mapping should be written 10811 * @param replaceOnPublish name mappings for which this is set will replace all other mappings for the same resource on publishing 10812 * 10813 * @return the actual name which was mapped to the structure id 10814 * 10815 * @throws CmsDataAccessException if something goes wrong 10816 */ 10817 public String writeUrlNameMapping( 10818 CmsDbContext dbc, 10819 Iterator<String> nameSeq, 10820 CmsUUID structureId, 10821 String locale, 10822 boolean replaceOnPublish) 10823 throws CmsDataAccessException { 10824 10825 String bestName = findBestNameForUrlNameMapping(dbc, nameSeq, structureId, locale); 10826 addOrReplaceUrlNameMapping(dbc, bestName, structureId, locale, replaceOnPublish); 10827 return bestName; 10828 } 10829 10830 /** 10831 * Updates the user information. <p> 10832 * 10833 * The user id has to be a valid OpenCms user id.<br> 10834 * 10835 * The user with the given id will be completely overridden 10836 * by the given data.<p> 10837 * 10838 * @param dbc the current database context 10839 * @param user the user to be updated 10840 * 10841 * @throws CmsException if operation was not successful 10842 */ 10843 public void writeUser(CmsDbContext dbc, CmsUser user) throws CmsException { 10844 10845 CmsUser oldUser = readUser(dbc, user.getId()); 10846 m_monitor.clearUserCache(oldUser); 10847 getUserDriver(dbc).writeUser(dbc, user); 10848 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 10849 10850 if (!dbc.getProjectId().isNullUUID()) { 10851 // user modified event is not needed 10852 return; 10853 } 10854 // fire user modified event 10855 Map<String, Object> eventData = new HashMap<String, Object>(); 10856 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 10857 eventData.put(I_CmsEventListener.KEY_USER_NAME, oldUser.getName()); 10858 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 10859 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(user.getChanges(oldUser))); 10860 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 10861 OpenCms.getTwoFactorAuthenticationHandler().trackUserChange(dbc.getRequestContext(), oldUser, user); 10862 } 10863 10864 /** 10865 * Adds or replaces a new url name mapping in the offline project.<p> 10866 * 10867 * @param dbc the current database context 10868 * @param name the URL name of the mapping 10869 * @param structureId the structure id of the mapping 10870 * @param locale the locale of the mapping 10871 * @param replaceOnPublish if the mapping shoudl replace previous URL name mappings when published 10872 * 10873 * @throws CmsDataAccessException if something goes wrong 10874 */ 10875 protected void addOrReplaceUrlNameMapping( 10876 CmsDbContext dbc, 10877 String name, 10878 CmsUUID structureId, 10879 String locale, 10880 boolean replaceOnPublish) 10881 throws CmsDataAccessException { 10882 10883 getVfsDriver(dbc).deleteUrlNameMappingEntries( 10884 dbc, 10885 false, 10886 CmsUrlNameMappingFilter.ALL.filterStructureId(structureId).filterLocale(locale).filterStates( 10887 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 10888 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 10889 CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry( 10890 name, 10891 structureId, 10892 replaceOnPublish 10893 ? CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH 10894 : CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 10895 System.currentTimeMillis(), 10896 locale); 10897 getVfsDriver(dbc).addUrlNameMappingEntry(dbc, false, newEntry); 10898 } 10899 10900 /** 10901 * Converts a resource to a folder (if possible).<p> 10902 * 10903 * @param resource the resource to convert 10904 * @return the converted resource 10905 * 10906 * @throws CmsVfsResourceNotFoundException if the resource is not a folder 10907 */ 10908 protected CmsFolder convertResourceToFolder(CmsResource resource) throws CmsVfsResourceNotFoundException { 10909 10910 if (resource.isFolder()) { 10911 return new CmsFolder(resource); 10912 } 10913 10914 throw new CmsVfsResourceNotFoundException( 10915 Messages.get().container(Messages.ERR_ACCESS_FILE_AS_FOLDER_1, resource.getRootPath())); 10916 } 10917 10918 /** 10919 * Helper method for creating a driver from configuration data.<p> 10920 * 10921 * @param dbc the db context 10922 * @param configManager the configuration manager 10923 * @param config the configuration 10924 * @param driverChainKey the configuration key under which the driver chain is stored 10925 * @param suffix the suffix to append to a driver chain entry to get the key for the driver class 10926 * 10927 * @return the newly created driver 10928 */ 10929 protected Object createDriver( 10930 CmsDbContext dbc, 10931 CmsConfigurationManager configManager, 10932 CmsParameterConfiguration config, 10933 String driverChainKey, 10934 String suffix) { 10935 10936 // read the vfs driver class properties and initialize a new instance 10937 List<String> drivers = config.getList(driverChainKey); 10938 String driverKey = drivers.get(0) + suffix; 10939 String driverName = config.get(driverKey); 10940 drivers = (drivers.size() > 1) ? drivers.subList(1, drivers.size()) : null; 10941 if (driverName == null) { 10942 CmsLog.INIT.error(Messages.get().getBundle().key(Messages.INIT_DRIVER_FAILED_1, driverKey)); 10943 } 10944 Object result = newDriverInstance(dbc, configManager, driverName, drivers); 10945 if ("true".equalsIgnoreCase(System.getProperty("opencms.profile.drivers"))) { 10946 result = wrapDriverInProfilingProxy(result); 10947 } 10948 return result; 10949 } 10950 10951 /** 10952 * Deletes all relations for the given resource and all its siblings.<p> 10953 * 10954 * @param dbc the current database context 10955 * @param resource the resource to delete the resource for 10956 * 10957 * @throws CmsException if something goes wrong 10958 */ 10959 protected void deleteRelationsWithSiblings(CmsDbContext dbc, CmsResource resource) throws CmsException { 10960 10961 // get all siblings 10962 List<CmsResource> siblings; 10963 if (resource.getSiblingCount() > 1) { 10964 siblings = readSiblings(dbc, resource, CmsResourceFilter.ALL); 10965 } else { 10966 siblings = new ArrayList<CmsResource>(); 10967 siblings.add(resource); 10968 } 10969 // clean the relations in content for all siblings 10970 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10971 Iterator<CmsResource> it = siblings.iterator(); 10972 while (it.hasNext()) { 10973 CmsResource sibling = it.next(); 10974 // clean the relation information for this sibling 10975 vfsDriver.deleteRelations( 10976 dbc, 10977 dbc.currentProject().getUuid(), 10978 sibling, 10979 CmsRelationFilter.TARGETS.filterDefinedInContent()); 10980 } 10981 } 10982 10983 /** 10984 * Tries to add sub-resources of moved folders to the publish list and throws an exception if the publish list still does 10985 * not contain some sub-resources of the moved folders.<p> 10986 * 10987 * @param cms the current CMS context 10988 * @param dbc the current database context 10989 * @param pubList the publish list 10990 * @throws CmsException if something goes wrong 10991 */ 10992 protected void ensureSubResourcesOfMovedFoldersPublished(CmsObject cms, CmsDbContext dbc, CmsPublishList pubList) 10993 throws CmsException { 10994 10995 List<CmsResource> topMovedFolders = pubList.getTopMovedFolders(cms); 10996 Iterator<CmsResource> folderIt = topMovedFolders.iterator(); 10997 while (folderIt.hasNext()) { 10998 CmsResource folder = folderIt.next(); 10999 addSubResources(dbc, pubList, folder, resource -> !resource.getState().isNew()); 11000 } 11001 List<CmsResource> missingSubResources = pubList.getMissingSubResources(cms, topMovedFolders); 11002 if (missingSubResources.isEmpty()) { 11003 return; 11004 } 11005 11006 StringBuffer pathBuffer = new StringBuffer(); 11007 11008 for (CmsResource missing : missingSubResources) { 11009 pathBuffer.append(missing.getRootPath()); 11010 pathBuffer.append(" "); 11011 } 11012 throw new CmsVfsException( 11013 Messages.get().container(Messages.RPT_CHILDREN_OF_MOVED_FOLDER_NOT_PUBLISHED_1, pathBuffer.toString())); 11014 11015 } 11016 11017 /** 11018 * Tries to find the best name for an URL name mapping for the given structure id.<p> 11019 * 11020 * @param dbc the database context 11021 * @param nameSeq the sequence of name candidates 11022 * @param structureId the structure id to which an URL name should be mapped 11023 * @param locale the locale for which the URL name should be mapped 11024 * 11025 * @return the selected URL name candidate 11026 * 11027 * @throws CmsDataAccessException if something goes wrong 11028 */ 11029 protected String findBestNameForUrlNameMapping( 11030 CmsDbContext dbc, 11031 Iterator<String> nameSeq, 11032 CmsUUID structureId, 11033 String locale) 11034 throws CmsDataAccessException { 11035 11036 String newName; 11037 boolean alreadyInUse; 11038 do { 11039 newName = nameSeq.next(); 11040 alreadyInUse = false; 11041 CmsUrlNameMappingFilter filter = CmsUrlNameMappingFilter.ALL.filterName(newName); 11042 List<CmsUrlNameMappingEntry> entriesWithSameName = getVfsDriver(dbc).readUrlNameMappingEntries( 11043 dbc, 11044 false, 11045 filter); 11046 for (CmsUrlNameMappingEntry entry : entriesWithSameName) { 11047 boolean sameId = entry.getStructureId().equals(structureId); 11048 if (!sameId) { 11049 // name already used for other resource, or for different locale of the same resource 11050 alreadyInUse = true; 11051 break; 11052 } 11053 } 11054 } while (alreadyInUse); 11055 return newName; 11056 } 11057 11058 /** 11059 * Helper method for finding the 'best' URL name to use for a new URL name mapping.<p> 11060 * 11061 * Since the name given as a parameter may be already used, this method will try to append numeric suffixes 11062 * to the name to find a mapping name which is not used.<p> 11063 * 11064 * @param dbc the current database context 11065 * @param name the name of the mapping 11066 * @param structureId the structure id to which the name is mapped 11067 * 11068 * @return the best name which was found for the new mapping 11069 * 11070 * @throws CmsDataAccessException if something goes wrong 11071 */ 11072 protected String findBestNameForUrlNameMapping(CmsDbContext dbc, String name, CmsUUID structureId) 11073 throws CmsDataAccessException { 11074 11075 List<CmsUrlNameMappingEntry> entriesStartingWithName = getVfsDriver(dbc).readUrlNameMappingEntries( 11076 dbc, 11077 false, 11078 CmsUrlNameMappingFilter.ALL.filterNamePattern(name + "%").filterRejectStructureId(structureId)); 11079 Set<String> usedNames = new HashSet<String>(); 11080 for (CmsUrlNameMappingEntry entry : entriesStartingWithName) { 11081 usedNames.add(entry.getName()); 11082 } 11083 int counter = 0; 11084 String numberedName; 11085 do { 11086 numberedName = getNumberedName(name, counter); 11087 counter += 1; 11088 } while (usedNames.contains(numberedName)); 11089 return numberedName; 11090 } 11091 11092 /** 11093 * Returns the lock manager instance.<p> 11094 * 11095 * @return the lock manager instance 11096 */ 11097 protected CmsLockManager getLockManager() { 11098 11099 return m_lockManager; 11100 } 11101 11102 /** 11103 * Adds a numeric suffix to the end of a string, unless the number passed as a parameter is 0.<p> 11104 * 11105 * @param name the base name 11106 * @param number the number from which to form the suffix 11107 * 11108 * @return the concatenation of the base name and possibly the numeric suffix 11109 */ 11110 protected String getNumberedName(String name, int number) { 11111 11112 if (number == 0) { 11113 return name; 11114 } 11115 PrintfFormat fmt = new PrintfFormat("%0.6d"); 11116 return name + "_" + fmt.sprintf(number); 11117 } 11118 11119 /** 11120 * Resets the resources in a project to their online state.<p> 11121 * 11122 * @param dbc the database context 11123 * @param projectId the project id 11124 * @param modifiedFiles the modified files 11125 * @param modifiedFolders the modified folders 11126 * @throws CmsException if something goes wrong 11127 * @throws CmsSecurityException if we don't have the permissions 11128 * @throws CmsDataAccessException if something goes wrong with the database 11129 */ 11130 protected void resetResourcesInProject( 11131 CmsDbContext dbc, 11132 CmsUUID projectId, 11133 List<CmsResource> modifiedFiles, 11134 List<CmsResource> modifiedFolders) 11135 throws CmsException, CmsSecurityException, CmsDataAccessException { 11136 11137 // all resources inside the project have to be be reset to their online state. 11138 // 1. step: delete all new files 11139 for (int i = 0; i < modifiedFiles.size(); i++) { 11140 CmsResource currentFile = modifiedFiles.get(i); 11141 if (currentFile.getState().isNew()) { 11142 CmsLock lock = getLock(dbc, currentFile); 11143 if (lock.isNullLock()) { 11144 // lock the resource 11145 lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE); 11146 } else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) { 11147 changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE); 11148 } 11149 // delete the properties 11150 getVfsDriver(dbc).deletePropertyObjects( 11151 dbc, 11152 projectId, 11153 currentFile, 11154 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 11155 // delete the file 11156 getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentFile); 11157 // remove the access control entries 11158 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFile.getResourceId()); 11159 // fire the corresponding event 11160 OpenCms.fireCmsEvent( 11161 new CmsEvent( 11162 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11163 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile))); 11164 } 11165 } 11166 11167 // 2. step: delete all new folders 11168 for (int i = 0; i < modifiedFolders.size(); i++) { 11169 CmsResource currentFolder = modifiedFolders.get(i); 11170 if (currentFolder.getState().isNew()) { 11171 // delete the properties 11172 getVfsDriver(dbc).deletePropertyObjects( 11173 dbc, 11174 projectId, 11175 currentFolder, 11176 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 11177 // delete the folder 11178 getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentFolder); 11179 // remove the access control entries 11180 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFolder.getResourceId()); 11181 // fire the corresponding event 11182 OpenCms.fireCmsEvent( 11183 new CmsEvent( 11184 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11185 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder))); 11186 } 11187 } 11188 11189 // 3. step: undo changes on all changed or deleted folders 11190 for (int i = 0; i < modifiedFolders.size(); i++) { 11191 CmsResource currentFolder = modifiedFolders.get(i); 11192 if ((currentFolder.getState().isChanged()) || (currentFolder.getState().isDeleted())) { 11193 CmsLock lock = getLock(dbc, currentFolder); 11194 if (lock.isNullLock()) { 11195 // lock the resource 11196 lockResource(dbc, currentFolder, CmsLockType.EXCLUSIVE); 11197 } else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) { 11198 changeLock(dbc, currentFolder, CmsLockType.EXCLUSIVE); 11199 } 11200 // undo all changes in the folder 11201 undoChanges(dbc, currentFolder, CmsResource.UNDO_CONTENT); 11202 // fire the corresponding event 11203 OpenCms.fireCmsEvent( 11204 new CmsEvent( 11205 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11206 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder))); 11207 } 11208 } 11209 11210 // 4. step: undo changes on all changed or deleted files 11211 for (int i = 0; i < modifiedFiles.size(); i++) { 11212 CmsResource currentFile = modifiedFiles.get(i); 11213 if (currentFile.getState().isChanged() || currentFile.getState().isDeleted()) { 11214 CmsLock lock = getLock(dbc, currentFile); 11215 if (lock.isNullLock()) { 11216 // lock the resource 11217 lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE); 11218 } else if (!lock.isOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) { 11219 if (lock.isLockableBy(dbc.currentUser())) { 11220 changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE); 11221 } 11222 } 11223 // undo all changes in the file 11224 undoChanges(dbc, currentFile, CmsResource.UNDO_CONTENT); 11225 // fire the corresponding event 11226 OpenCms.fireCmsEvent( 11227 new CmsEvent( 11228 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11229 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile))); 11230 } 11231 } 11232 } 11233 11234 /** 11235 * Counts the total number of users which fit the given criteria.<p> 11236 * 11237 * @param dbc the database context 11238 * @param searchParams the user search criteria 11239 * 11240 * @return the total number of users matching the criteria 11241 * 11242 * @throws CmsDataAccessException if something goes wrong 11243 */ 11244 long countUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams) throws CmsDataAccessException { 11245 11246 return getUserDriver(dbc).countUsers(dbc, searchParams); 11247 } 11248 11249 /** 11250 * Adds a pool to the static pool map.<p> 11251 * 11252 * @param pool the pool to add 11253 */ 11254 private void addPool(CmsDbPoolV11 pool) { 11255 11256 m_pools.put(pool.getPoolUrl(), pool); 11257 } 11258 11259 /** 11260 * Adds all sub-resources of the given resource to the publish list.<p> 11261 * 11262 * @param dbc the database context 11263 * @param publishList the publish list 11264 * @param directPublishResource the resource to get the sub-resources for 11265 * @param additionalFilter an additional test for resources to pass before they are added to the publish list 11266 * 11267 * @throws CmsDataAccessException if something goes wrong accessing the database 11268 */ 11269 private void addSubResources( 11270 CmsDbContext dbc, 11271 CmsPublishList publishList, 11272 CmsResource directPublishResource, 11273 Predicate<CmsResource> additionalFilter) 11274 throws CmsDataAccessException { 11275 11276 int flags = CmsDriverManager.READMODE_INCLUDE_TREE | CmsDriverManager.READMODE_EXCLUDE_STATE; 11277 if (!directPublishResource.getState().isDeleted()) { 11278 // fix for org.opencms.file.TestPublishIssues#testPublishFolderWithDeletedFileFromOtherProject 11279 flags = flags | CmsDriverManager.READMODE_INCLUDE_PROJECT; 11280 } 11281 11282 // add all sub resources of the folder 11283 List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree( 11284 dbc, 11285 dbc.currentProject().getUuid(), 11286 directPublishResource.getRootPath(), 11287 CmsDriverManager.READ_IGNORE_TYPE, 11288 CmsResource.STATE_UNCHANGED, 11289 CmsDriverManager.READ_IGNORE_TIME, 11290 CmsDriverManager.READ_IGNORE_TIME, 11291 CmsDriverManager.READ_IGNORE_TIME, 11292 CmsDriverManager.READ_IGNORE_TIME, 11293 CmsDriverManager.READ_IGNORE_TIME, 11294 CmsDriverManager.READ_IGNORE_TIME, 11295 flags | CmsDriverManager.READMODE_ONLY_FOLDERS); 11296 11297 publishList.addAll( 11298 filterResources(dbc, publishList, folderList).stream().filter(additionalFilter).collect( 11299 Collectors.toList()), 11300 true); 11301 11302 List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree( 11303 dbc, 11304 dbc.currentProject().getUuid(), 11305 directPublishResource.getRootPath(), 11306 CmsDriverManager.READ_IGNORE_TYPE, 11307 CmsResource.STATE_UNCHANGED, 11308 CmsDriverManager.READ_IGNORE_TIME, 11309 CmsDriverManager.READ_IGNORE_TIME, 11310 CmsDriverManager.READ_IGNORE_TIME, 11311 CmsDriverManager.READ_IGNORE_TIME, 11312 CmsDriverManager.READ_IGNORE_TIME, 11313 CmsDriverManager.READ_IGNORE_TIME, 11314 flags | CmsDriverManager.READMODE_ONLY_FILES); 11315 11316 publishList.addAll( 11317 filterResources(dbc, publishList, fileList).stream().filter(additionalFilter).collect(Collectors.toList()), 11318 true); 11319 } 11320 11321 /** 11322 * Helper method to check whether we should bother with reading the group for a given role in a given OU.<p> 11323 * 11324 * 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. 11325 * 11326 * @param ou the OU 11327 * @param role the role 11328 * @return true if we should read the role in the OU 11329 */ 11330 private boolean canReadRoleInOu(CmsOrganizationalUnit ou, CmsRole role) { 11331 11332 if (ou.hasFlagWebuser() && !role.getRoleName().equals(CmsRole.ACCOUNT_MANAGER.getRoleName())) { 11333 return false; 11334 } 11335 return true; 11336 } 11337 11338 /** 11339 * Checks the parent of a resource during publishing.<p> 11340 * 11341 * @param dbc the current database context 11342 * @param deletedFolders a list of deleted folders 11343 * @param res a resource to check the parent for 11344 * 11345 * @return <code>true</code> if the parent resource will be deleted during publishing 11346 */ 11347 private boolean checkDeletedParentFolder(CmsDbContext dbc, List<CmsResource> deletedFolders, CmsResource res) { 11348 11349 String parentPath = CmsResource.getParentFolder(res.getRootPath()); 11350 11351 if (parentPath == null) { 11352 // resource has no parent 11353 return false; 11354 } 11355 11356 CmsResource parent; 11357 try { 11358 parent = readResource(dbc, parentPath, CmsResourceFilter.ALL); 11359 } catch (Exception e) { 11360 // failure: if we cannot read the parent, we should not publish the resource 11361 return false; 11362 } 11363 11364 if (!parent.getState().isDeleted()) { 11365 // parent is not deleted 11366 return false; 11367 } 11368 11369 for (int j = 0; j < deletedFolders.size(); j++) { 11370 if ((deletedFolders.get(j)).getStructureId().equals(parent.getStructureId())) { 11371 // parent is deleted, and it will get published 11372 return true; 11373 } 11374 } 11375 11376 // parent is new, but it will not get published 11377 return false; 11378 } 11379 11380 /** 11381 * Checks that no one of the resources to be published has a 'new' parent (that has not been published yet).<p> 11382 * 11383 * @param dbc the db context 11384 * @param publishList the publish list to check 11385 * 11386 * @throws CmsVfsException if there is a resource to be published with a 'new' parent 11387 */ 11388 private void checkParentFolders(CmsDbContext dbc, CmsPublishList publishList) throws CmsVfsException { 11389 11390 boolean directPublish = publishList.isDirectPublish(); 11391 // if we direct publish a file, check if all parent folders are already published 11392 if (directPublish) { 11393 // first get the names of all parent folders 11394 Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator(); 11395 List<String> parentFolderNames = new ArrayList<String>(); 11396 while (it.hasNext()) { 11397 CmsResource res = it.next(); 11398 String parentFolderName = CmsResource.getParentFolder(res.getRootPath()); 11399 if (parentFolderName != null) { 11400 parentFolderNames.add(parentFolderName); 11401 } 11402 } 11403 // remove duplicate parent folder names 11404 parentFolderNames = CmsFileUtil.removeRedundancies(parentFolderNames); 11405 String parentFolderName = null; 11406 try { 11407 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 11408 // now check all folders if they exist in the online project 11409 Iterator<String> parentIt = parentFolderNames.iterator(); 11410 while (parentIt.hasNext()) { 11411 parentFolderName = parentIt.next(); 11412 vfsDriver.readFolder(dbc, CmsProject.ONLINE_PROJECT_ID, parentFolderName); 11413 } 11414 } catch (CmsException e) { 11415 throw new CmsVfsException( 11416 Messages.get().container(Messages.RPT_PARENT_FOLDER_NOT_PUBLISHED_1, parentFolderName)); 11417 } 11418 } 11419 } 11420 11421 /** 11422 * Checks the parent of a resource during publishing.<p> 11423 * 11424 * @param dbc the current database context 11425 * @param folderList a list of folders 11426 * @param res a resource to check the parent for 11427 * 11428 * @return true if the resource should be published 11429 */ 11430 private boolean checkParentResource(CmsDbContext dbc, List<CmsResource> folderList, CmsResource res) { 11431 11432 String parentPath = CmsResource.getParentFolder(res.getRootPath()); 11433 11434 if (parentPath == null) { 11435 // resource has no parent 11436 return true; 11437 } 11438 11439 CmsResource parent; 11440 try { 11441 parent = readResource(dbc, parentPath, CmsResourceFilter.ALL); 11442 } catch (Exception e) { 11443 // failure: if we cannot read the parent, we should not publish the resource 11444 return false; 11445 } 11446 11447 if (!parent.getState().isNew()) { 11448 // parent is already published 11449 return true; 11450 } 11451 11452 for (int j = 0; j < folderList.size(); j++) { 11453 if (folderList.get(j).getStructureId().equals(parent.getStructureId())) { 11454 // parent is new, but it will get published 11455 return true; 11456 } 11457 } 11458 11459 // parent is new, but it will not get published 11460 return false; 11461 } 11462 11463 /** 11464 * Copies all relations from the source resource to the target resource.<p> 11465 * 11466 * @param dbc the database context 11467 * @param source the source 11468 * @param target the target 11469 * 11470 * @throws CmsException if something goes wrong 11471 */ 11472 private void copyRelations(CmsDbContext dbc, CmsResource source, CmsResource target) throws CmsException { 11473 11474 // copy relations all relations 11475 CmsObject cms = new CmsObject(getSecurityManager(), dbc.getRequestContext()); 11476 Iterator<CmsRelation> itRelations = getRelationsForResource( 11477 dbc, 11478 source, 11479 CmsRelationFilter.TARGETS.filterNotDefinedInContent()).iterator(); 11480 while (itRelations.hasNext()) { 11481 CmsRelation relation = itRelations.next(); 11482 if (relation.getType().getCopyBehavior() == CopyBehavior.copy) { 11483 try { 11484 CmsResource relTarget = relation.getTarget(cms, CmsResourceFilter.ALL); 11485 addRelationToResource(dbc, target, relTarget, relation.getType(), true); 11486 } catch (CmsVfsResourceNotFoundException e) { 11487 // ignore this broken relation 11488 if (LOG.isWarnEnabled()) { 11489 LOG.warn(e.getLocalizedMessage(), e); 11490 } 11491 } 11492 } 11493 } 11494 // repair categories 11495 repairCategories(dbc, getProjectIdForContext(dbc), target); 11496 } 11497 11498 /** 11499 * Filters the given list of resources, removes all resources where the current user 11500 * does not have READ permissions, plus the filter is applied.<p> 11501 * 11502 * @param dbc the current database context 11503 * @param resourceList a list of CmsResources 11504 * @param filter the resource filter to use 11505 * 11506 * @return the filtered list of resources 11507 * 11508 * @throws CmsException in case errors testing the permissions 11509 */ 11510 private List<CmsResource> filterPermissions( 11511 CmsDbContext dbc, 11512 List<CmsResource> resourceList, 11513 CmsResourceFilter filter) 11514 throws CmsException { 11515 11516 if (filter.requireTimerange()) { 11517 // never check time range here - this must be done later in #updateContextDates(...) 11518 filter = filter.addExcludeTimerange(); 11519 } 11520 ResourceListWithCacheability result = new ResourceListWithCacheability(); 11521 boolean nocacheWasSet = false; 11522 if (null == dbc.getAttribute(ATTR_PERMISSION_NOCACHE)) { 11523 // The attribute will be used by the permission handler to tell us that lists containing the resource 11524 // should not be cached. 11525 dbc.setAttribute(ATTR_PERMISSION_NOCACHE, new boolean[] {false}); 11526 // insurance against potential indirect recursive calls introduced by future code changes: 11527 // make sure we only remove the attribute later if we were the one who set it 11528 nocacheWasSet = true; 11529 } 11530 try { 11531 for (int i = 0; i < resourceList.size(); i++) { 11532 // check the permission of all resources 11533 CmsResource currentResource = resourceList.get(i); 11534 if (m_securityManager.hasPermissions( 11535 dbc, 11536 currentResource, 11537 CmsPermissionSet.ACCESS_READ, 11538 LockCheck.yes, 11539 filter).isAllowed()) { 11540 // only return resources where permission was granted 11541 result.add(currentResource); 11542 } 11543 } 11544 } finally { 11545 if (nocacheWasSet) { 11546 boolean[] nocache = (boolean[])dbc.getAttribute(ATTR_PERMISSION_NOCACHE); 11547 if (nocache != null) { 11548 dbc.removeAttribute(ATTR_PERMISSION_NOCACHE); 11549 if (nocache[0]) { 11550 result.setCacheable(false); 11551 } 11552 } 11553 } 11554 } 11555 // return the result 11556 return result; 11557 } 11558 11559 /** 11560 * Returns a filtered list of resources for publishing.<p> 11561 * Contains all resources, which are not locked 11562 * and which have a parent folder that is already published or will be published, too.<p> 11563 * 11564 * @param dbc the current database context 11565 * @param publishList the filling publish list 11566 * @param resourceList the list of resources to filter 11567 * 11568 * @return a filtered list of resources 11569 */ 11570 private List<CmsResource> filterResources( 11571 CmsDbContext dbc, 11572 CmsPublishList publishList, 11573 List<CmsResource> resourceList) { 11574 11575 List<CmsResource> result = new ArrayList<CmsResource>(); 11576 11577 // local folder list for adding new publishing subfolders 11578 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioD} problem. 11579 List<CmsResource> newFolderList = new ArrayList<CmsResource>( 11580 publishList == null ? resourceList : publishList.getFolderList()); 11581 11582 for (int i = 0; i < resourceList.size(); i++) { 11583 CmsResource res = resourceList.get(i); 11584 try { 11585 CmsLock lock = getLock(dbc, res); 11586 if (lock.isPublish()) { 11587 // if already enqueued 11588 continue; 11589 } 11590 if (!lock.isLockableBy(dbc.currentUser())) { 11591 // checks if there is a shared lock and if the resource is deleted 11592 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem. 11593 if (lock.isShared() && (publishList != null)) { 11594 if (!res.getState().isDeleted() 11595 || !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) { 11596 continue; 11597 } 11598 } else { 11599 // don't add locked resources 11600 continue; 11601 } 11602 } 11603 if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, newFolderList, res)) { 11604 continue; 11605 } 11606 // check permissions 11607 try { 11608 m_securityManager.checkPermissions( 11609 dbc, 11610 res, 11611 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 11612 false, 11613 CmsResourceFilter.ALL); 11614 } catch (CmsException e) { 11615 // skip if not enough permissions 11616 continue; 11617 } 11618 if (res.isFolder()) { 11619 newFolderList.add(res); 11620 } 11621 result.add(res); 11622 } catch (Exception e) { 11623 // should never happen 11624 LOG.error(e.getLocalizedMessage(), e); 11625 } 11626 } 11627 return result; 11628 } 11629 11630 /** 11631 * Returns a filtered list of sibling resources for publishing.<p> 11632 * 11633 * Contains all siblings of the given resources, which are not locked 11634 * and which have a parent folder that is already published or will be published, too.<p> 11635 * 11636 * @param dbc the current database context 11637 * @param publishList the unfinished publish list 11638 * @param resourceList the list of siblings to filter 11639 * 11640 * @return a filtered list of sibling resources for publishing 11641 */ 11642 private List<CmsResource> filterSiblings( 11643 CmsDbContext dbc, 11644 CmsPublishList publishList, 11645 Collection<CmsResource> resourceList) { 11646 11647 List<CmsResource> result = new ArrayList<CmsResource>(); 11648 11649 // removed internal extendible folder list, since iterated (sibling) resources are files in any case, never folders 11650 11651 for (CmsResource res : resourceList) { 11652 try { 11653 CmsLock lock = getLock(dbc, res); 11654 if (lock.isPublish()) { 11655 // if already enqueued 11656 continue; 11657 } 11658 if (!lock.isLockableBy(dbc.currentUser())) { 11659 // checks if there is a shared lock and if the resource is deleted 11660 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem. 11661 if (lock.isShared() && (publishList != null)) { 11662 if (!res.getState().isDeleted() 11663 || !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) { 11664 continue; 11665 } 11666 } else { 11667 // don't add locked resources 11668 continue; 11669 } 11670 } 11671 if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, publishList.getFolderList(), res)) { 11672 // don't add resources that have no parent in the online project 11673 continue; 11674 } 11675 // check permissions 11676 try { 11677 m_securityManager.checkPermissions( 11678 dbc, 11679 res, 11680 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 11681 false, 11682 CmsResourceFilter.ALL); 11683 } catch (CmsException e) { 11684 // skip if not enough permissions 11685 continue; 11686 } 11687 result.add(res); 11688 } catch (Exception e) { 11689 // should never happen 11690 LOG.error(e.getLocalizedMessage(), e); 11691 } 11692 } 11693 return result; 11694 } 11695 11696 /** 11697 * Returns the access control list of a given resource.<p> 11698 * 11699 * @param dbc the current database context 11700 * @param resource the resource 11701 * @param forFolder should be true if resource is a folder 11702 * @param depth the depth to include non-inherited access entries, also 11703 * @param inheritedOnly flag indicates to collect inherited permissions only 11704 * 11705 * @return the access control list of the resource 11706 * 11707 * @throws CmsException if something goes wrong 11708 */ 11709 private CmsAccessControlList getAccessControlList( 11710 CmsDbContext dbc, 11711 CmsResource resource, 11712 boolean inheritedOnly, 11713 boolean forFolder, 11714 int depth) 11715 throws CmsException { 11716 11717 String cacheKey = getCacheKey( 11718 new String[] { 11719 inheritedOnly ? "+" : "-", 11720 forFolder ? "+" : "-", 11721 Integer.toString(depth), 11722 resource.getStructureId().toString()}, 11723 dbc); 11724 11725 CmsAccessControlList acl = m_monitor.getCachedACL(cacheKey); 11726 11727 // return the cached acl if already available 11728 if ((acl != null) && dbc.getProjectId().isNullUUID()) { 11729 return acl; 11730 } 11731 11732 List<CmsAccessControlEntry> aces = getUserDriver(dbc).readAccessControlEntries( 11733 dbc, 11734 dbc.currentProject(), 11735 resource.getResourceId(), 11736 (depth > 1) || ((depth > 0) && forFolder)); 11737 11738 // sort the list of aces 11739 boolean overwriteAll = sortAceList(aces); 11740 11741 // if no 'overwrite all' ace was found 11742 if (!overwriteAll) { 11743 // get the acl of the parent 11744 CmsResource parentResource = null; 11745 try { 11746 // try to recurse over the id 11747 parentResource = getVfsDriver(dbc).readParentFolder( 11748 dbc, 11749 dbc.currentProject().getUuid(), 11750 resource.getStructureId()); 11751 } catch (CmsVfsResourceNotFoundException e) { 11752 // should never happen, but try with the path 11753 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 11754 if (parentPath != null) { 11755 parentResource = getVfsDriver(dbc).readFolder(dbc, dbc.currentProject().getUuid(), parentPath); 11756 } 11757 } 11758 if (parentResource != null) { 11759 acl = (CmsAccessControlList)getAccessControlList( 11760 dbc, 11761 parentResource, 11762 inheritedOnly, 11763 forFolder, 11764 depth + 1).clone(); 11765 } 11766 } 11767 if (acl == null) { 11768 acl = new CmsAccessControlList(); 11769 } 11770 11771 Set<CmsUUID> exclusiveAccessPrincipals = new HashSet<>(); 11772 if (!((depth == 0) && inheritedOnly)) { 11773 Iterator<CmsAccessControlEntry> itAces = aces.iterator(); 11774 while (itAces.hasNext()) { 11775 CmsAccessControlEntry acEntry = itAces.next(); 11776 if (depth > 0) { 11777 acEntry.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED); 11778 } 11779 if ((depth == 0) 11780 && resource.isFile() 11781 && (0 != (acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE))) { 11782 11783 // 'responsible' flag is only interpreted as exclusive access if it's not inherited and set directly on a file 11784 exclusiveAccessPrincipals.add(acEntry.getPrincipal()); 11785 } 11786 11787 acl.add(acEntry); 11788 11789 // if the overwrite flag is set, reset the allowed permissions to the permissions of this entry 11790 // denied permissions are kept or extended 11791 if ((acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE) > 0) { 11792 acl.setAllowedPermissions(acEntry); 11793 } 11794 } 11795 } 11796 if (exclusiveAccessPrincipals.size() > 0) { 11797 acl.setExclusiveAccessPrincipals(exclusiveAccessPrincipals); 11798 } 11799 11800 if (dbc.getProjectId().isNullUUID()) { 11801 m_monitor.cacheACL(cacheKey, acl); 11802 } 11803 return acl; 11804 } 11805 11806 /** 11807 * Return a cache key build from the provided information.<p> 11808 * 11809 * @param prefix a prefix for the key 11810 * @param flag a boolean flag for the key (only used if prefix is not null) 11811 * @param projectId the project for which to generate the key 11812 * @param resource the resource for which to generate the key 11813 * 11814 * @return String a cache key build from the provided information 11815 */ 11816 private String getCacheKey(String prefix, boolean flag, CmsUUID projectId, String resource) { 11817 11818 StringBuffer b = new StringBuffer(64); 11819 if (prefix != null) { 11820 b.append(prefix); 11821 b.append(flag ? '+' : '-'); 11822 } 11823 b.append(CmsProject.isOnlineProject(projectId) ? '+' : '-'); 11824 return b.append(resource).toString(); 11825 } 11826 11827 /** 11828 * Return a cache key build from the provided information.<p> 11829 * 11830 * @param keys an array of keys to generate the cache key from 11831 * @param dbc the database context for which to generate the key 11832 * 11833 * @return String a cache key build from the provided information 11834 */ 11835 private String getCacheKey(String[] keys, CmsDbContext dbc) { 11836 11837 if (!dbc.getProjectId().isNullUUID()) { 11838 return ""; 11839 } 11840 StringBuffer b = new StringBuffer(64); 11841 int len = keys.length; 11842 if (len > 0) { 11843 for (int i = 0; i < len; i++) { 11844 b.append(keys[i]); 11845 b.append('_'); 11846 } 11847 } 11848 if (dbc.currentProject().isOnlineProject()) { 11849 b.append("+"); 11850 } else { 11851 b.append("-"); 11852 } 11853 return b.toString(); 11854 } 11855 11856 /** 11857 * Gets the correct driver interface to use for proxying a specific driver instance.<p> 11858 * 11859 * @param obj the driver instance 11860 * @return the interface to use for proxying 11861 */ 11862 private Class<?> getDriverInterfaceForProxy(Object obj) { 11863 11864 for (Class<?> interfaceClass : new Class[] { 11865 I_CmsUserDriver.class, 11866 I_CmsVfsDriver.class, 11867 I_CmsProjectDriver.class, 11868 I_CmsHistoryDriver.class, 11869 I_CmsSubscriptionDriver.class}) { 11870 if (interfaceClass.isAssignableFrom(obj.getClass())) { 11871 return interfaceClass; 11872 } 11873 } 11874 return null; 11875 } 11876 11877 /** 11878 * Returns the correct project id.<p> 11879 * 11880 * @param dbc the database context 11881 * 11882 * @return the correct project id 11883 */ 11884 private CmsUUID getProjectIdForContext(CmsDbContext dbc) { 11885 11886 CmsUUID projectId = dbc.getProjectId(); 11887 if (projectId.isNullUUID()) { 11888 projectId = dbc.currentProject().getUuid(); 11889 } 11890 return projectId; 11891 } 11892 11893 /** 11894 * Returns if and what state needs to be updated.<p> 11895 * 11896 * @param dbc the db context 11897 * @param resource the resource 11898 * @param properties the properties to check 11899 * 11900 * @return 0: none, 1: structure, 2: resource 11901 * 11902 * @throws CmsDataAccessException if something goes wrong 11903 */ 11904 private int getUpdateState(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties) 11905 throws CmsDataAccessException { 11906 11907 int updateState = 0; 11908 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 11909 Iterator<CmsProperty> it = properties.iterator(); 11910 while (it.hasNext() && (updateState < 2)) { 11911 CmsProperty property = it.next(); 11912 11913 // read existing property 11914 CmsProperty existingProperty = vfsDriver.readPropertyObject( 11915 dbc, 11916 property.getName(), 11917 dbc.currentProject(), 11918 resource); 11919 11920 // check the shared property 11921 if (property.getResourceValue() != null) { 11922 if (property.isDeleteResourceValue()) { 11923 if (existingProperty.getResourceValue() != null) { 11924 updateState = 2; // deleted 11925 } 11926 } else { 11927 if (existingProperty.getResourceValue() == null) { 11928 updateState = 2; // created 11929 } else { 11930 if (!property.getResourceValue().equals(existingProperty.getResourceValue())) { 11931 updateState = 2; // updated 11932 } 11933 } 11934 } 11935 } 11936 if (updateState == 0) { 11937 // check the individual property only if needed 11938 if (property.getStructureValue() != null) { 11939 if (property.isDeleteStructureValue()) { 11940 if (existingProperty.getStructureValue() != null) { 11941 updateState = 1; // deleted 11942 } 11943 } else { 11944 if (existingProperty.getStructureValue() == null) { 11945 updateState = 1; // created 11946 } else { 11947 if (!property.getStructureValue().equals(existingProperty.getStructureValue())) { 11948 updateState = 1; // updated 11949 } 11950 } 11951 } 11952 } 11953 } 11954 } 11955 return updateState; 11956 } 11957 11958 /** 11959 * Returns all groups that are virtualizing the given role in the given ou.<p> 11960 * 11961 * @param dbc the database context 11962 * @param role the role 11963 * 11964 * @return all groups that are virtualizing the given role (or a child of it) 11965 * 11966 * @throws CmsException if something goes wrong 11967 */ 11968 private List<CmsGroup> getVirtualGroupsForRole(CmsDbContext dbc, CmsRole role) throws CmsException { 11969 11970 Set<Integer> roleFlags = new HashSet<Integer>(); 11971 // add role flag 11972 Integer flags = Integer.valueOf(role.getVirtualGroupFlags()); 11973 roleFlags.add(flags); 11974 // collect all child role flags 11975 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 11976 while (itChildRoles.hasNext()) { 11977 CmsRole child = itChildRoles.next(); 11978 flags = Integer.valueOf(child.getVirtualGroupFlags()); 11979 roleFlags.add(flags); 11980 } 11981 // iterate all groups matching the flags 11982 List<CmsGroup> groups = new ArrayList<CmsGroup>(); 11983 Iterator<CmsGroup> it = getGroups(dbc, readOrganizationalUnit(dbc, role.getOuFqn()), false, false).iterator(); 11984 while (it.hasNext()) { 11985 CmsGroup group = it.next(); 11986 if (group.isVirtual()) { 11987 CmsRole r = CmsRole.valueOf(group); 11988 if (roleFlags.contains(Integer.valueOf(r.getVirtualGroupFlags()))) { 11989 groups.add(group); 11990 } 11991 } 11992 } 11993 return groups; 11994 } 11995 11996 /** 11997 * Returns a list of users in a group.<p> 11998 * 11999 * @param dbc the current database context 12000 * @param ouFqn the organizational unit to get the users from 12001 * @param groupname the name of the group to list users from 12002 * @param includeOtherOuUsers include users of other organizational units 12003 * @param directUsersOnly if set only the direct assigned users will be returned, 12004 * if not also indirect users, ie. members of parent roles, 12005 * this parameter only works with roles 12006 * @param readRoles if to read roles or groups 12007 * 12008 * @return all <code>{@link CmsUser}</code> objects in the group 12009 * 12010 * @throws CmsException if operation was not successful 12011 */ 12012 private List<CmsUser> internalUsersOfGroup( 12013 CmsDbContext dbc, 12014 String ouFqn, 12015 String groupname, 12016 boolean includeOtherOuUsers, 12017 boolean directUsersOnly, 12018 boolean readRoles) 12019 throws CmsException { 12020 12021 CmsGroup group = readGroup(dbc, groupname); // check that the group really exists 12022 if ((group == null) || (!((!readRoles && !group.isRole()) || (readRoles && group.isRole())))) { 12023 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 12024 } 12025 12026 String prefix = "_" + includeOtherOuUsers + "_" + directUsersOnly + "_" + ouFqn; 12027 String cacheKey = m_keyGenerator.getCacheKeyForGroupUsers(prefix, dbc, group); 12028 List<CmsUser> allUsers = m_monitor.getCachedUserList(cacheKey); 12029 if (allUsers == null) { 12030 Set<CmsUser> users = new HashSet<CmsUser>( 12031 getUserDriver(dbc).readUsersOfGroup(dbc, groupname, includeOtherOuUsers)); 12032 if (readRoles && !directUsersOnly) { 12033 CmsRole role = CmsRole.valueOf(group); 12034 if (role.getParentRole() != null) { 12035 try { 12036 String parentGroup = role.getParentRole().getGroupName(); 12037 readGroup(dbc, parentGroup); 12038 // iterate the parent roles 12039 users.addAll( 12040 internalUsersOfGroup( 12041 dbc, 12042 ouFqn, 12043 parentGroup, 12044 includeOtherOuUsers, 12045 directUsersOnly, 12046 readRoles)); 12047 } catch (CmsDbEntryNotFoundException e) { 12048 // ignore, this may happen while deleting an orgunit 12049 if (LOG.isDebugEnabled()) { 12050 LOG.debug(e.getLocalizedMessage(), e); 12051 } 12052 } 12053 } 12054 String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn()); 12055 if (parentOu != null) { 12056 // iterate the parent ou's 12057 users.addAll( 12058 internalUsersOfGroup( 12059 dbc, 12060 ouFqn, 12061 parentOu + group.getSimpleName(), 12062 includeOtherOuUsers, 12063 directUsersOnly, 12064 readRoles)); 12065 } 12066 } else if (!readRoles && !directUsersOnly) { 12067 List<CmsGroup> groups = getChildren(dbc, group, false); 12068 for (CmsGroup parentGroup : groups) { 12069 try { 12070 // iterate the parent groups 12071 users.addAll( 12072 internalUsersOfGroup( 12073 dbc, 12074 ouFqn, 12075 parentGroup.getName(), 12076 includeOtherOuUsers, 12077 directUsersOnly, 12078 readRoles)); 12079 } catch (CmsDbEntryNotFoundException e) { 12080 // ignore, this may happen while deleting an orgunit 12081 if (LOG.isDebugEnabled()) { 12082 LOG.debug(e.getLocalizedMessage(), e); 12083 } 12084 } 12085 } 12086 } 12087 // filter users from other ous 12088 if (!includeOtherOuUsers) { 12089 Iterator<CmsUser> itUsers = users.iterator(); 12090 while (itUsers.hasNext()) { 12091 CmsUser user = itUsers.next(); 12092 if (!user.getOuFqn().equals(ouFqn)) { 12093 itUsers.remove(); 12094 } 12095 } 12096 } 12097 12098 // make user list unmodifiable for caching 12099 allUsers = Collections.unmodifiableList(new ArrayList<CmsUser>(users)); 12100 if (dbc.getProjectId().isNullUUID()) { 12101 m_monitor.cacheUserList(cacheKey, allUsers); 12102 } 12103 } 12104 return allUsers; 12105 } 12106 12107 /** 12108 * Reads all resources that are inside and changed in a specified project.<p> 12109 * 12110 * @param dbc the current database context 12111 * @param projectId the ID of the project 12112 * @param mode one of the {@link CmsReadChangedProjectResourceMode} constants 12113 * 12114 * @return a List with all resources inside the specified project 12115 * 12116 * @throws CmsException if something goes wrong 12117 */ 12118 private List<CmsResource> readChangedResourcesInsideProject( 12119 CmsDbContext dbc, 12120 CmsUUID projectId, 12121 CmsReadChangedProjectResourceMode mode) 12122 throws CmsException { 12123 12124 String cacheKey = projectId + "_" + mode.toString(); 12125 List<CmsResource> result = m_monitor.getCachedProjectResources(cacheKey); 12126 if (result != null) { 12127 return result; 12128 } 12129 List<String> projectResources = readProjectResources(dbc, readProject(dbc, projectId)); 12130 result = new ArrayList<CmsResource>(); 12131 String currentProjectResource = null; 12132 List<CmsResource> resources = new ArrayList<CmsResource>(); 12133 CmsResource currentResource = null; 12134 CmsLock currentLock = null; 12135 12136 for (int i = 0; i < projectResources.size(); i++) { 12137 // read all resources that are inside the project by visiting each project resource 12138 currentProjectResource = projectResources.get(i); 12139 12140 try { 12141 currentResource = readResource(dbc, currentProjectResource, CmsResourceFilter.ALL); 12142 12143 if (currentResource.isFolder()) { 12144 resources.addAll(readResources(dbc, currentResource, CmsResourceFilter.ALL, true)); 12145 } else { 12146 resources.add(currentResource); 12147 } 12148 } catch (CmsException e) { 12149 // the project resource probably doesn't exist (anymore)... 12150 if (!(e instanceof CmsVfsResourceNotFoundException)) { 12151 throw e; 12152 } 12153 } 12154 } 12155 12156 for (int j = 0; j < resources.size(); j++) { 12157 currentResource = resources.get(j); 12158 currentLock = getLock(dbc, currentResource).getEditionLock(); 12159 12160 if (!currentResource.getState().isUnchanged()) { 12161 if ((currentLock.isNullLock() && (currentResource.getProjectLastModified().equals(projectId))) 12162 || (currentLock.isOwnedBy(dbc.currentUser()) && (currentLock.getProjectId().equals(projectId)))) { 12163 // add only resources that are 12164 // - inside the project, 12165 // - changed in the project, 12166 // - either unlocked, or locked for the current user in the project 12167 if ((mode == RCPRM_FILES_AND_FOLDERS_MODE) 12168 || (currentResource.isFolder() && (mode == RCPRM_FOLDERS_ONLY_MODE)) 12169 || (currentResource.isFile() && (mode == RCPRM_FILES_ONLY_MODE))) { 12170 result.add(currentResource); 12171 } 12172 } 12173 } 12174 } 12175 12176 resources.clear(); 12177 resources = null; 12178 12179 m_monitor.cacheProjectResources(cacheKey, result); 12180 return result; 12181 } 12182 12183 /** 12184 * Sorts the given list of {@link CmsAccessControlEntry} objects.<p> 12185 * 12186 * The the 'all others' ace in first place, the 'overwrite all' ace in second.<p> 12187 * 12188 * @param aces the list of ACEs to sort 12189 * 12190 * @return <code>true</code> if the list contains the 'overwrite all' ace 12191 */ 12192 private boolean sortAceList(List<CmsAccessControlEntry> aces) { 12193 12194 // sort the list of entries 12195 Collections.sort(aces, CmsAccessControlEntry.COMPARATOR_ACE); 12196 // after sorting just the first 2 positions come in question 12197 for (int i = 0; i < Math.min(aces.size(), 2); i++) { 12198 CmsAccessControlEntry acEntry = aces.get(i); 12199 if (acEntry.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_ID)) { 12200 return true; 12201 } 12202 } 12203 return false; 12204 } 12205 12206 /** 12207 * All permissions and resources attributes of the principal 12208 * are transfered to a replacement principal.<p> 12209 * 12210 * @param dbc the current database context 12211 * @param project the current project 12212 * @param principalId the id of the principal to be replaced 12213 * @param replacementId the user to be transfered 12214 * @param withACEs flag to signal if the ACEs should also be transfered or just deleted 12215 * 12216 * @throws CmsException if operation was not successful 12217 */ 12218 private void transferPrincipalResources( 12219 CmsDbContext dbc, 12220 CmsProject project, 12221 CmsUUID principalId, 12222 CmsUUID replacementId, 12223 boolean withACEs) 12224 throws CmsException { 12225 12226 // get all resources for the given user including resources associated by ACEs or attributes 12227 I_CmsUserDriver userDriver = getUserDriver(dbc); 12228 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12229 Set<CmsResource> resources = getResourcesForPrincipal(dbc, project, principalId, null, true); 12230 Iterator<CmsResource> it = resources.iterator(); 12231 while (it.hasNext()) { 12232 CmsResource resource = it.next(); 12233 // check resource attributes 12234 boolean attrModified = false; 12235 CmsUUID createdUser = null; 12236 if (resource.getUserCreated().equals(principalId)) { 12237 createdUser = replacementId; 12238 attrModified = true; 12239 } 12240 CmsUUID lastModUser = null; 12241 if (resource.getUserLastModified().equals(principalId)) { 12242 lastModUser = replacementId; 12243 attrModified = true; 12244 } 12245 if (attrModified) { 12246 vfsDriver.transferResource(dbc, project, resource, createdUser, lastModUser); 12247 // clear the cache 12248 m_monitor.clearResourceCache(); 12249 } 12250 boolean aceModified = false; 12251 // check aces 12252 if (withACEs) { 12253 Iterator<CmsAccessControlEntry> itAces = userDriver.readAccessControlEntries( 12254 dbc, 12255 project, 12256 resource.getResourceId(), 12257 false).iterator(); 12258 while (itAces.hasNext()) { 12259 CmsAccessControlEntry ace = itAces.next(); 12260 if (ace.getPrincipal().equals(principalId)) { 12261 CmsAccessControlEntry newAce = new CmsAccessControlEntry( 12262 ace.getResource(), 12263 replacementId, 12264 ace.getAllowedPermissions(), 12265 ace.getDeniedPermissions(), 12266 ace.getFlags()); 12267 // write the new ace 12268 userDriver.writeAccessControlEntry(dbc, project, newAce); 12269 aceModified = true; 12270 } 12271 } 12272 if (aceModified) { 12273 // clear the cache 12274 m_monitor.clearAccessControlListCache(); 12275 } 12276 } 12277 if (attrModified || aceModified) { 12278 // fire the event 12279 Map<String, Object> data = new HashMap<String, Object>(2); 12280 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 12281 data.put( 12282 I_CmsEventListener.KEY_CHANGE, 12283 Integer.valueOf( 12284 ((attrModified) ? CHANGED_RESOURCE : 0) | ((aceModified) ? CHANGED_ACCESSCONTROL : 0))); 12285 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 12286 } 12287 } 12288 } 12289 12290 /** 12291 * Undoes all content changes of a resource.<p> 12292 * 12293 * @param dbc the database context 12294 * @param onlineProject the online project 12295 * @param offlineResource the offline resource, or <code>null</code> if deleted 12296 * @param onlineResource the online resource 12297 * @param newState the new resource state 12298 * @param moveUndone is a move operation on the same resource has been made 12299 * 12300 * @throws CmsException if something goes wrong 12301 */ 12302 private void undoContentChanges( 12303 CmsDbContext dbc, 12304 CmsProject onlineProject, 12305 CmsResource offlineResource, 12306 CmsResource onlineResource, 12307 CmsResourceState newState, 12308 boolean moveUndone) 12309 throws CmsException { 12310 12311 String path = ((moveUndone || (offlineResource == null)) 12312 ? onlineResource.getRootPath() 12313 : offlineResource.getRootPath()); 12314 12315 // change folder or file? 12316 I_CmsUserDriver userDriver = getUserDriver(dbc); 12317 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12318 if (onlineResource.isFolder()) { 12319 CmsFolder restoredFolder = new CmsFolder( 12320 onlineResource.getStructureId(), 12321 onlineResource.getResourceId(), 12322 path, 12323 onlineResource.getTypeId(), 12324 onlineResource.getFlags(), 12325 dbc.currentProject().getUuid(), 12326 newState, 12327 onlineResource.getDateCreated(), 12328 onlineResource.getUserCreated(), 12329 onlineResource.getDateLastModified(), 12330 onlineResource.getUserLastModified(), 12331 onlineResource.getDateReleased(), 12332 onlineResource.getDateExpired(), 12333 onlineResource.getVersion()); // version number does not matter since it will be computed later 12334 12335 // write the folder in the offline project 12336 // this sets a flag so that the folder date is not set to the current time 12337 restoredFolder.setDateLastModified(onlineResource.getDateLastModified()); 12338 12339 // write the folder 12340 vfsDriver.writeResource(dbc, dbc.currentProject().getUuid(), restoredFolder, NOTHING_CHANGED); 12341 12342 // restore the properties from the online project 12343 vfsDriver.deletePropertyObjects( 12344 dbc, 12345 dbc.currentProject().getUuid(), 12346 restoredFolder, 12347 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 12348 12349 List<CmsProperty> propertyInfos = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource); 12350 vfsDriver.writePropertyObjects(dbc, dbc.currentProject(), restoredFolder, propertyInfos); 12351 12352 // restore the access control entries from the online project 12353 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId()); 12354 ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries( 12355 dbc, 12356 onlineProject, 12357 onlineResource.getResourceId(), 12358 false).listIterator(); 12359 12360 while (aceList.hasNext()) { 12361 CmsAccessControlEntry ace = aceList.next(); 12362 userDriver.createAccessControlEntry( 12363 dbc, 12364 dbc.currentProject(), 12365 onlineResource.getResourceId(), 12366 ace.getPrincipal(), 12367 ace.getPermissions().getAllowedPermissions(), 12368 ace.getPermissions().getDeniedPermissions(), 12369 ace.getFlags()); 12370 } 12371 } else { 12372 byte[] onlineContent = vfsDriver.readContent( 12373 dbc, 12374 CmsProject.ONLINE_PROJECT_ID, 12375 onlineResource.getResourceId()); 12376 12377 CmsFile restoredFile = new CmsFile( 12378 onlineResource.getStructureId(), 12379 onlineResource.getResourceId(), 12380 path, 12381 onlineResource.getTypeId(), 12382 onlineResource.getFlags(), 12383 dbc.currentProject().getUuid(), 12384 newState, 12385 onlineResource.getDateCreated(), 12386 onlineResource.getUserCreated(), 12387 onlineResource.getDateLastModified(), 12388 onlineResource.getUserLastModified(), 12389 onlineResource.getDateReleased(), 12390 onlineResource.getDateExpired(), 12391 0, 12392 onlineResource.getLength(), 12393 onlineResource.getDateContent(), 12394 onlineResource.getVersion(), // version number does not matter since it will be computed later 12395 onlineContent); 12396 12397 // write the file in the offline project 12398 // this sets a flag so that the file date is not set to the current time 12399 restoredFile.setDateLastModified(onlineResource.getDateLastModified()); 12400 12401 // collect the old properties 12402 List<CmsProperty> properties = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource); 12403 12404 if (offlineResource != null) { 12405 // bug fix 1020: delete all properties (inclum_rejectStructureIdded shared), 12406 // shared properties will be recreated by the next call of #createResource(...) 12407 vfsDriver.deletePropertyObjects( 12408 dbc, 12409 dbc.currentProject().getUuid(), 12410 onlineResource, 12411 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 12412 12413 // implementation notes: 12414 // undo changes can become complex e.g. if a resource was deleted, and then 12415 // another resource was copied over the deleted file as a sibling 12416 // therefore we must "clean" delete the offline resource, and then create 12417 // an new resource with the create method 12418 // note that this does NOT apply to folders, since a folder cannot be replaced 12419 // like a resource anyway 12420 deleteResource(dbc, offlineResource, CmsResource.DELETE_PRESERVE_SIBLINGS); 12421 } 12422 CmsResource res = createResource( 12423 dbc, 12424 restoredFile.getRootPath(), 12425 restoredFile, 12426 restoredFile.getContents(), 12427 properties, 12428 false); 12429 12430 // copy the access control entries from the online project 12431 if (offlineResource != null) { 12432 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId()); 12433 } 12434 ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries( 12435 dbc, 12436 onlineProject, 12437 onlineResource.getResourceId(), 12438 false).listIterator(); 12439 12440 while (aceList.hasNext()) { 12441 CmsAccessControlEntry ace = aceList.next(); 12442 userDriver.createAccessControlEntry( 12443 dbc, 12444 dbc.currentProject(), 12445 res.getResourceId(), 12446 ace.getPrincipal(), 12447 ace.getPermissions().getAllowedPermissions(), 12448 ace.getPermissions().getDeniedPermissions(), 12449 ace.getFlags()); 12450 } 12451 12452 vfsDriver.deleteUrlNameMappingEntries( 12453 dbc, 12454 false, 12455 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates( 12456 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 12457 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 12458 // restore the state to unchanged 12459 res.setState(newState); 12460 m_vfsDriver.writeResourceState(dbc, dbc.currentProject(), res, UPDATE_ALL, false); 12461 } 12462 12463 // delete all offline relations 12464 if (offlineResource != null) { 12465 vfsDriver.deleteRelations(dbc, dbc.currentProject().getUuid(), offlineResource, CmsRelationFilter.TARGETS); 12466 } 12467 // get online relations 12468 List<CmsRelation> relations = vfsDriver.readRelations( 12469 dbc, 12470 CmsProject.ONLINE_PROJECT_ID, 12471 onlineResource, 12472 CmsRelationFilter.TARGETS); 12473 // write offline relations 12474 Iterator<CmsRelation> itRelations = relations.iterator(); 12475 while (itRelations.hasNext()) { 12476 CmsRelation relation = itRelations.next(); 12477 vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation); 12478 } 12479 12480 // update the cache 12481 m_monitor.clearResourceCache(); 12482 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 12483 12484 if ((offlineResource == null) || offlineResource.getRootPath().equals(onlineResource.getRootPath())) { 12485 log( 12486 dbc, 12487 new CmsLogEntry( 12488 dbc, 12489 onlineResource.getStructureId(), 12490 CmsLogEntryType.RESOURCE_RESTORED, 12491 new String[] {onlineResource.getRootPath()}), 12492 false); 12493 } else { 12494 log( 12495 dbc, 12496 new CmsLogEntry( 12497 dbc, 12498 offlineResource.getStructureId(), 12499 CmsLogEntryType.RESOURCE_MOVE_RESTORED, 12500 new String[] {offlineResource.getRootPath(), onlineResource.getRootPath()}), 12501 false); 12502 } 12503 if (offlineResource != null) { 12504 OpenCms.fireCmsEvent( 12505 new CmsEvent( 12506 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 12507 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, offlineResource))); 12508 } else { 12509 OpenCms.fireCmsEvent( 12510 new CmsEvent( 12511 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 12512 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, onlineResource))); 12513 } 12514 } 12515 12516 /** 12517 * Updates the current users context dates with the given resource.<p> 12518 * 12519 * This checks the date information of the resource based on 12520 * {@link CmsResource#getDateLastModified()} as well as 12521 * {@link CmsResource#getDateReleased()} and {@link CmsResource#getDateExpired()}. 12522 * The current users request context is updated with the the "latest" dates found.<p> 12523 * 12524 * This is required in order to ensure proper setting of <code>"last-modified"</code> http headers 12525 * and also for expiration of cached elements in the Flex cache. 12526 * Consider the following use case: Page A is generated from resources x, y and z. 12527 * If either x, y or z has an expiration / release date set, then page A must expire at a certain point 12528 * in time. This is ensured by the context date check here.<p> 12529 * 12530 * @param dbc the current database context 12531 * @param resource the resource to get the date information from 12532 */ 12533 private void updateContextDates(CmsDbContext dbc, CmsResource resource) { 12534 12535 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12536 if (info != null) { 12537 info.updateFromResource(resource); 12538 } 12539 } 12540 12541 /** 12542 * Updates the current users context dates with each {@link CmsResource} object in the given list.<p> 12543 * 12544 * The given input list is returned unmodified.<p> 12545 * 12546 * Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p> 12547 * 12548 * @param dbc the current database context 12549 * @param resourceList a list of {@link CmsResource} objects 12550 * 12551 * @return the original list of CmsResources with the full resource name set 12552 */ 12553 private List<CmsResource> updateContextDates(CmsDbContext dbc, List<CmsResource> resourceList) { 12554 12555 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12556 if (info != null) { 12557 for (int i = 0; i < resourceList.size(); i++) { 12558 CmsResource resource = resourceList.get(i); 12559 info.updateFromResource(resource); 12560 } 12561 } 12562 return resourceList; 12563 } 12564 12565 /** 12566 * Returns a List of {@link CmsResource} objects generated when applying the given filter to the given list, 12567 * also updates the current users context dates with each {@link CmsResource} object in the given list, 12568 * also applies the selected resource filter to all resources in the list and returns the remaining resources.<p> 12569 * 12570 * Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p> 12571 * 12572 * @param dbc the current database context 12573 * @param resourceList a list of {@link CmsResource} objects 12574 * @param filter the resource filter to use 12575 * 12576 * @return a List of {@link CmsResource} objects generated when applying the given filter to the given list 12577 */ 12578 private List<CmsResource> updateContextDates( 12579 CmsDbContext dbc, 12580 List<CmsResource> resourceList, 12581 CmsResourceFilter filter) { 12582 12583 if (CmsResourceFilter.ALL == filter) { 12584 // if there is no filter required, then use the simpler method that does not apply the filter 12585 return new ArrayList<CmsResource>(updateContextDates(dbc, resourceList)); 12586 } 12587 12588 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12589 List<CmsResource> result = new ArrayList<CmsResource>(resourceList.size()); 12590 for (int i = 0; i < resourceList.size(); i++) { 12591 CmsResource resource = resourceList.get(i); 12592 if (filter.isValid(dbc.getRequestContext(), resource)) { 12593 result.add(resource); 12594 } 12595 // must also include "invalid" resources for the update of context dates 12596 // since a resource may be invalid because of release / expiration date 12597 if (info != null) { 12598 info.updateFromResource(resource); 12599 } 12600 } 12601 return result; 12602 } 12603 12604 /** 12605 * Updates the state of a resource, depending on the <code>resourceState</code> parameter.<p> 12606 * 12607 * @param dbc the db context 12608 * @param resource the resource 12609 * @param resourceState if <code>true</code> the resource state will be updated, if not just the structure state. 12610 * 12611 * @throws CmsDataAccessException if something goes wrong 12612 */ 12613 private void updateState(CmsDbContext dbc, CmsResource resource, boolean resourceState) 12614 throws CmsDataAccessException { 12615 12616 CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) 12617 ? dbc.currentProject().getUuid() 12618 : dbc.getProjectId(); 12619 resource.setUserLastModified(dbc.currentUser().getId()); 12620 if (resourceState) { 12621 // update the whole resource state 12622 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE); 12623 } else { 12624 // update the structure state 12625 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_STRUCTURE_STATE); 12626 } 12627 } 12628 12629 /** 12630 * Wraps a driver object with a dynamic proxy that counts method calls and their durations.<p> 12631 * 12632 * @param newDriverInstance the driver instance to wrap 12633 * @return the proxy 12634 */ 12635 private Object wrapDriverInProfilingProxy(Object newDriverInstance) { 12636 12637 Class<?> cls = getDriverInterfaceForProxy(newDriverInstance); 12638 if (cls == null) { 12639 return newDriverInstance; 12640 } 12641 return Proxy.newProxyInstance( 12642 Thread.currentThread().getContextClassLoader(), 12643 new Class[] {cls}, 12644 new CmsProfilingInvocationHandler(newDriverInstance, CmsDefaultProfilingHandler.INSTANCE)); 12645 } 12646 12647}