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.function.Predicate; 155import java.util.function.Supplier; 156import java.util.regex.Pattern; 157import java.util.regex.PatternSyntaxException; 158import java.util.stream.Collectors; 159 160import org.apache.commons.logging.Log; 161 162import com.google.common.collect.ArrayListMultimap; 163import com.google.common.collect.Maps; 164import com.google.common.collect.Multimap; 165 166/** 167 * The OpenCms driver manager.<p> 168 * 169 * @since 6.0.0 170 */ 171public final class CmsDriverManager implements I_CmsEventListener { 172 173 /** 174 * Enum for distinguishing between login modes. 175 */ 176 public static enum LoginUserMode { 177 /** 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). */ 178 checkOnly, 179 180 /** Normal login process. */ 181 standard 182 } 183 184 /** 185 * Resource list which additionally knows whether it should be cacheable in the resource list cache or not. 186 */ 187 public static class ResourceListWithCacheability extends ArrayList<CmsResource> { 188 189 /** Serial version id. */ 190 private static final long serialVersionUID = 1L; 191 192 /** True if the list should be cacheable. */ 193 private boolean m_cacheable = true; 194 195 /** 196 * Creates a new instance. 197 */ 198 public ResourceListWithCacheability() { 199 200 super(); 201 } 202 203 /** 204 * Creates a new instance. 205 * @param initialCapacity the initial capacity 206 */ 207 public ResourceListWithCacheability(int initialCapacity) { 208 209 super(initialCapacity); 210 } 211 212 /** 213 * Returns true if the resource list is cacheable. 214 * 215 * @return true if the list is cacheable 216 */ 217 public boolean isCacheable() { 218 219 return m_cacheable; 220 } 221 222 /** 223 * Enables/disables cacheability for the resource list. 224 * @param cacheable true if the list should be cacheable 225 */ 226 public void setCacheable(boolean cacheable) { 227 228 m_cacheable = cacheable; 229 } 230 231 } 232 233 /** 234 * Special key class for caching the resource OU data with a Guava LoadingCache.<p> 235 * 236 * In principle, the actual cache key is just the current project, but because of how cache loaders work, 237 * the key must contain everything that varies between calls and is required to load the value. So we also store the DB context 238 * for use by the cache loader. The project (offline/online) must still be stored, because the DB context gets invalidated 239 * eventually, i.e. its project id gets nulled. 240 */ 241 public static class ResourceOUCacheKey { 242 243 /** The actual cache key. */ 244 private String m_actualKey; 245 246 /** The DB context. */ 247 private CmsDbContext m_dbc; 248 249 /** The driver manager to use. */ 250 private CmsDriverManager m_driverManager; 251 252 /** 253 * Creates a new instance. 254 * 255 * @param driverManager the driver manager to use 256 * @param dbc the current DB context 257 */ 258 public ResourceOUCacheKey(CmsDriverManager driverManager, CmsDbContext dbc) { 259 260 m_dbc = dbc; 261 m_driverManager = driverManager; 262 m_actualKey = CmsProject.ONLINE_PROJECT_ID.equals(dbc.currentProject().getId()) ? "ONLINE" : "OFFLINE"; 263 } 264 265 /** 266 * @see java.lang.Object#equals(java.lang.Object) 267 */ 268 @Override 269 public boolean equals(Object obj) { 270 271 return (obj instanceof ResourceOUCacheKey) 272 && ((ResourceOUCacheKey)obj).getActualKey().equals(getActualKey()); 273 } 274 275 /** 276 * Gets the stored DB context.<p> 277 * 278 * Note that the DB contex returned by this may have been invalidated! 279 * 280 * @return the stored DB context 281 */ 282 public CmsDbContext getDbContext() { 283 284 return m_dbc; 285 } 286 287 /** 288 * Gets the current driver manager. 289 * 290 * @return the driver manager to use 291 **/ 292 public CmsDriverManager getDriverManager() { 293 294 return m_driverManager; 295 } 296 297 /** 298 * @see java.lang.Object#hashCode() 299 */ 300 @Override 301 public int hashCode() { 302 303 return getActualKey().hashCode(); 304 } 305 306 /** 307 * Gets the actual key data. 308 * 309 * @return the actual key data 310 */ 311 private String getActualKey() { 312 313 return m_actualKey; 314 } 315 316 } 317 318 /** 319 * Helper class used to store information about resources assigned to OUs in a cache. 320 */ 321 public static class ResourceOUMap { 322 323 /** Multimap from the paths of resources to the OUs to which they are assigned as OU resources. */ 324 private Multimap<CmsPath, CmsOrganizationalUnit> m_ousByAssignedResourcePaths = ArrayListMultimap.create(); 325 326 /** The organizational units, with their UUIDs as keys. */ 327 private Map<CmsUUID, CmsOrganizationalUnit> m_ousById = new HashMap<>(); 328 329 /** 330 * Gets the list of organizational units to which a given root path belongs, according to the cached 331 * OU resource assignments. 332 * 333 * @param rootPath the root path 334 * @return the organizational units to which the path belongs 335 */ 336 public List<CmsOrganizationalUnit> getResourceOrgUnits(String rootPath) { 337 338 Set<CmsOrganizationalUnit> result = new HashSet<>(); 339 String currentPath = rootPath; 340 while (currentPath != null) { 341 result.addAll(m_ousByAssignedResourcePaths.get(new CmsPath(currentPath))); 342 currentPath = CmsResource.getParentFolder(currentPath); 343 } 344 return new ArrayList<>(result); 345 } 346 347 /** 348 * Reads the OU resource data from the VFS and initializes this instance with it. 349 * 350 * @param driverManager the driver manager to use 351 * @param dbc the current DB context 352 * @throws CmsException if something goes wrong 353 */ 354 public void init(CmsDriverManager driverManager, CmsDbContext dbc) throws CmsException { 355 356 List<CmsRelation> relations = driverManager.getRelationsForResource( 357 dbc, 358 null, 359 CmsRelationFilter.ALL.filterType(CmsRelationType.OU_RESOURCE)); 360 CmsOrganizationalUnit root = driverManager.readOrganizationalUnit(dbc, ""); 361 List<CmsOrganizationalUnit> children = driverManager.getOrganizationalUnits(dbc, root, true); 362 363 Set<CmsOrganizationalUnit> ous = new HashSet<>(); 364 ous.add(root); 365 ous.addAll(children); 366 init(relations, ous); 367 368 } 369 370 /** 371 * Initializes the OU resource data. 372 * 373 * @param ouRelations the current list of OU relations 374 * @param ous the current list of OUs 375 */ 376 public void init(Collection<CmsRelation> ouRelations, Collection<CmsOrganizationalUnit> ous) { 377 378 m_ousById.clear(); 379 m_ousByAssignedResourcePaths.clear(); 380 for (CmsOrganizationalUnit ou : ous) { 381 m_ousById.put(ou.getId(), ou); 382 } 383 for (CmsRelation rel : ouRelations) { 384 CmsOrganizationalUnit ou = m_ousById.get(rel.getSourceId()); 385 if (ou != null) { 386 m_ousByAssignedResourcePaths.put(new CmsPath(rel.getTargetPath()), ou); 387 } 388 } 389 } 390 } 391 392 /** 393 * The comparator used for comparing url name mapping entries by date.<p> 394 */ 395 class UrlNameMappingComparator implements Comparator<CmsUrlNameMappingEntry> { 396 397 /** 398 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 399 */ 400 public int compare(CmsUrlNameMappingEntry o1, CmsUrlNameMappingEntry o2) { 401 402 long date1 = o1.getDateChanged(); 403 long date2 = o2.getDateChanged(); 404 if (date1 < date2) { 405 return -1; 406 } 407 if (date1 > date2) { 408 return +1; 409 } 410 return 0; 411 } 412 } 413 414 /** 415 * Enumeration class for the mode parameter in the 416 * {@link CmsDriverManager#readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)} 417 * method.<p> 418 */ 419 private static class CmsReadChangedProjectResourceMode { 420 421 /** 422 * Default constructor.<p> 423 */ 424 protected CmsReadChangedProjectResourceMode() { 425 426 // noop 427 } 428 } 429 430 /** Request context attribute used to override the time used for time-based exclusive access checks. */ 431 public static final String ATTR_EXCLUSIVE_ACCESS_CLOCK = "ATTR_EXCLUSIVE_ACCESS_CLOCK"; 432 433 /** Attribute for signaling to the user driver that a specific OU should be initialized by fillDefaults. */ 434 public static final String ATTR_INIT_OU = "INIT_OU"; 435 436 /** DB context attribute used to communicate information about resource cacheability between various methods. */ 437 public static final String ATTR_PERMISSION_NOCACHE = "ATTR_PERMISSION_NOCACHE"; 438 439 /** Attribute login. */ 440 public static final String ATTRIBUTE_LOGIN = "A_LOGIN"; 441 442 /** Cache key for all properties. */ 443 public static final String CACHE_ALL_PROPERTIES = "_CAP_"; 444 445 /** 446 * Values indicating changes of a resource, 447 * ordered according to the scope of the change. 448 */ 449 /** Value to indicate a change in access control entries of a resource. */ 450 public static final int CHANGED_ACCESSCONTROL = 1; 451 452 /** Value to indicate a content change. */ 453 public static final int CHANGED_CONTENT = 16; 454 455 /** Value to indicate a change in the lastmodified settings of a resource. */ 456 public static final int CHANGED_LASTMODIFIED = 4; 457 458 /** Value to indicate a project change. */ 459 public static final int CHANGED_PROJECT = 32; 460 461 /** Value to indicate a change in the resource data. */ 462 public static final int CHANGED_RESOURCE = 8; 463 464 /** Value to indicate a change in the availability timeframe. */ 465 public static final int CHANGED_TIMEFRAME = 2; 466 467 /** "cache" string in the configuration-file. */ 468 public static final String CONFIGURATION_CACHE = "cache"; 469 470 /** "db" string in the configuration-file. */ 471 public static final String CONFIGURATION_DB = "db"; 472 473 /** "driver.history" string in the configuration-file. */ 474 public static final String CONFIGURATION_HISTORY = "driver.history"; 475 476 /** "driver.project" string in the configuration-file. */ 477 public static final String CONFIGURATION_PROJECT = "driver.project"; 478 479 /** "subscription.vfs" string in the configuration file. */ 480 public static final String CONFIGURATION_SUBSCRIPTION = "driver.subscription"; 481 482 /** "driver.user" string in the configuration-file. */ 483 public static final String CONFIGURATION_USER = "driver.user"; 484 485 /** "driver.vfs" string in the configuration-file. */ 486 public static final String CONFIGURATION_VFS = "driver.vfs"; 487 488 /** DBC attribute key needed to fix publishing behavior involving siblings. */ 489 public static final String KEY_CHANGED_AND_DELETED = "changedAndDeleted"; 490 491 /** The vfs path of the loast and found folder. */ 492 public static final String LOST_AND_FOUND_FOLDER = "/system/lost-found"; 493 494 /** The maximum length of a VFS resource path. */ 495 public static final int MAX_VFS_RESOURCE_PATH_LENGTH = 512; 496 497 /** Key for indicating no changes. */ 498 public static final int NOTHING_CHANGED = 0; 499 500 /** Name of the configuration parameter to enable/disable logging to the CMS_LOG table. */ 501 public static final String PARAM_LOG_TABLE_ENABLED = "log.table.enabled"; 502 503 /** Indicates to ignore the resource path when matching resources. */ 504 public static final String READ_IGNORE_PARENT = null; 505 506 /** Indicates to ignore the time value. */ 507 public static final long READ_IGNORE_TIME = 0L; 508 509 /** Indicates to ignore the resource type when matching resources. */ 510 public static final int READ_IGNORE_TYPE = -1; 511 512 /** Indicates to match resources NOT having the given state. */ 513 public static final int READMODE_EXCLUDE_STATE = 8; 514 515 /** Indicates to match immediate children only. */ 516 public static final int READMODE_EXCLUDE_TREE = 1; 517 518 /** Indicates to match resources NOT having the given type. */ 519 public static final int READMODE_EXCLUDE_TYPE = 4; 520 521 /** Mode for reading project resources from the db. */ 522 public static final int READMODE_IGNORESTATE = 0; 523 524 /** Indicates to match resources in given project only. */ 525 public static final int READMODE_INCLUDE_PROJECT = 2; 526 527 /** Indicates to match all successors. */ 528 public static final int READMODE_INCLUDE_TREE = 0; 529 530 /** Mode for reading project resources from the db. */ 531 public static final int READMODE_MATCHSTATE = 1; 532 533 /** Indicates if only file resources should be read. */ 534 public static final int READMODE_ONLY_FILES = 128; 535 536 /** Indicates if only folder resources should be read. */ 537 public static final int READMODE_ONLY_FOLDERS = 64; 538 539 /** Mode for reading project resources from the db. */ 540 public static final int READMODE_UNMATCHSTATE = 2; 541 542 /** Flag that can be used to disable the resource OU caching if necessary. */ 543 public static boolean resourceOrgUnitCachingEnabled = true; 544 545 /** Prefix char for temporary files in the VFS. */ 546 public static final String TEMP_FILE_PREFIX = "~"; 547 548 /** Key to indicate complete update. */ 549 public static final int UPDATE_ALL = 3; 550 551 /** Key to indicate update of resource record. */ 552 public static final int UPDATE_RESOURCE = 4; 553 554 /** Key to indicate update of last modified project reference. */ 555 public static final int UPDATE_RESOURCE_PROJECT = 6; 556 557 /** Key to indicate update of resource state. */ 558 public static final int UPDATE_RESOURCE_STATE = 1; 559 560 /** Key to indicate update of resource state including the content date. */ 561 public static final int UPDATE_RESOURCE_STATE_CONTENT = 7; 562 563 /** Key to indicate update of structure record. */ 564 public static final int UPDATE_STRUCTURE = 5; 565 566 /** Key to indicate update of structure state. */ 567 public static final int UPDATE_STRUCTURE_STATE = 2; 568 569 /** Map of pools defined in opencms.properties. */ 570 protected static ConcurrentMap<String, CmsDbPoolV11> m_pools = Maps.newConcurrentMap(); 571 572 /** The log object for this class. */ 573 private static final Log LOG = CmsLog.getLog(CmsDriverManager.class); 574 575 /** Constant mode parameter to read all files and folders in the {@link #readChangedResourcesInsideProject(CmsDbContext, CmsUUID, CmsReadChangedProjectResourceMode)}} method. */ 576 private static final CmsReadChangedProjectResourceMode RCPRM_FILES_AND_FOLDERS_MODE = new CmsReadChangedProjectResourceMode(); 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_ONLY_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_FOLDERS_ONLY_MODE = new CmsReadChangedProjectResourceMode(); 583 584 private final Object m_publishTagLock = new Object(); 585 586 /** The history driver. */ 587 private I_CmsHistoryDriver m_historyDriver; 588 589 /** The HTML link validator. */ 590 private CmsRelationSystemValidator m_htmlLinkValidator; 591 592 /** The class used for cache key generation. */ 593 private I_CmsCacheKey m_keyGenerator; 594 595 /** The lock manager. */ 596 private CmsLockManager m_lockManager; 597 598 /** The log entry cache. */ 599 private List<CmsLogEntry> m_log = new ArrayList<CmsLogEntry>(); 600 601 /** Local reference to the memory monitor to avoid multiple lookups through the OpenCms singleton. */ 602 private CmsMemoryMonitor m_monitor; 603 604 /** The project driver. */ 605 private I_CmsProjectDriver m_projectDriver; 606 607 /** The the configuration read from the <code>opencms.properties</code> file. */ 608 private CmsParameterConfiguration m_propertyConfiguration; 609 610 /** the publish engine. */ 611 private CmsPublishEngine m_publishEngine; 612 613 /** Object used for synchronizing updates to the user publish list. */ 614 private Object m_publishListUpdateLock = new Object(); 615 616 /** The security manager (for access checks). */ 617 private CmsSecurityManager m_securityManager; 618 619 /** The sql manager. */ 620 private CmsSqlManager m_sqlManager; 621 622 /** The subscription driver. */ 623 private I_CmsSubscriptionDriver m_subscriptionDriver; 624 625 /** The user driver. */ 626 private I_CmsUserDriver m_userDriver; 627 628 /** The VFS driver. */ 629 private I_CmsVfsDriver m_vfsDriver; 630 631 private volatile Integer m_lastPublishTag = null; 632 633 /** 634 * Private constructor, initializes some required member variables.<p> 635 */ 636 private CmsDriverManager() { 637 638 // intentionally left blank 639 } 640 641 /** 642 * Reads the required configurations from the opencms.properties file and creates 643 * the various drivers to access the cms resources.<p> 644 * 645 * The initialization process of the driver manager and its drivers is split into 646 * the following phases: 647 * <ul> 648 * <li>the database pool configuration is read</li> 649 * <li>a plain and empty driver manager instance is created</li> 650 * <li>an instance of each driver is created</li> 651 * <li>the driver manager is passed to each driver during initialization</li> 652 * <li>finally, the driver instances are passed to the driver manager during initialization</li> 653 * </ul> 654 * 655 * @param configurationManager the configuration manager 656 * @param securityManager the security manager 657 * @param runtimeInfoFactory the initialized OpenCms runtime info factory 658 * @param publishEngine the publish engine 659 * 660 * @return CmsDriverManager the instantiated driver manager 661 * @throws CmsInitException if the driver manager couldn't be instantiated 662 */ 663 public static CmsDriverManager newInstance( 664 CmsConfigurationManager configurationManager, 665 CmsSecurityManager securityManager, 666 I_CmsDbContextFactory runtimeInfoFactory, 667 CmsPublishEngine publishEngine) 668 throws CmsInitException { 669 670 // read the opencms.properties from the configuration 671 CmsParameterConfiguration config = configurationManager.getConfiguration(); 672 673 CmsDriverManager driverManager = null; 674 try { 675 // create a driver manager instance 676 driverManager = new CmsDriverManager(); 677 if (CmsLog.INIT.isInfoEnabled()) { 678 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE1_0)); 679 } 680 if (runtimeInfoFactory == null) { 681 throw new CmsInitException( 682 org.opencms.main.Messages.get().container(org.opencms.main.Messages.ERR_CRITICAL_NO_DB_CONTEXT_0)); 683 } 684 } catch (Exception exc) { 685 CmsMessageContainer message = Messages.get().container(Messages.LOG_ERR_DRIVER_MANAGER_START_0); 686 if (LOG.isFatalEnabled()) { 687 LOG.fatal(message.key(), exc); 688 } 689 throw new CmsInitException(message, exc); 690 } 691 692 // store the configuration 693 driverManager.m_propertyConfiguration = config; 694 695 // set the security manager 696 driverManager.m_securityManager = securityManager; 697 698 // set the lock manager 699 driverManager.m_lockManager = new CmsLockManager(driverManager); 700 701 // create and set the sql manager 702 driverManager.m_sqlManager = new CmsSqlManager(driverManager); 703 704 // set the publish engine 705 driverManager.m_publishEngine = publishEngine; 706 707 if (CmsLog.INIT.isInfoEnabled()) { 708 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE2_0)); 709 } 710 711 // read the pool names to initialize 712 List<String> driverPoolNames = config.getList(CmsDriverManager.CONFIGURATION_DB + ".pools"); 713 if (CmsLog.INIT.isInfoEnabled()) { 714 String names = ""; 715 for (String name : driverPoolNames) { 716 names += name + " "; 717 } 718 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_POOLS_1, names)); 719 } 720 721 // initialize each pool 722 for (String name : driverPoolNames) { 723 driverManager.newPoolInstance(config, name); 724 } 725 726 // initialize the runtime info factory with the generated driver manager 727 runtimeInfoFactory.initialize(driverManager); 728 729 if (CmsLog.INIT.isInfoEnabled()) { 730 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE3_0)); 731 } 732 733 // store the access objects 734 CmsDbContext dbc = runtimeInfoFactory.getDbContext(); 735 driverManager.m_vfsDriver = (I_CmsVfsDriver)driverManager.createDriver( 736 dbc, 737 configurationManager, 738 config, 739 CONFIGURATION_VFS, 740 ".vfs.driver"); 741 dbc.clear(); 742 743 dbc = runtimeInfoFactory.getDbContext(); 744 driverManager.m_userDriver = (I_CmsUserDriver)driverManager.createDriver( 745 dbc, 746 configurationManager, 747 config, 748 CONFIGURATION_USER, 749 ".user.driver"); 750 dbc.clear(); 751 752 dbc = runtimeInfoFactory.getDbContext(); 753 driverManager.m_projectDriver = (I_CmsProjectDriver)driverManager.createDriver( 754 dbc, 755 configurationManager, 756 config, 757 CONFIGURATION_PROJECT, 758 ".project.driver"); 759 dbc.clear(); 760 761 dbc = runtimeInfoFactory.getDbContext(); 762 driverManager.m_historyDriver = (I_CmsHistoryDriver)driverManager.createDriver( 763 dbc, 764 configurationManager, 765 config, 766 CONFIGURATION_HISTORY, 767 ".history.driver"); 768 dbc.clear(); 769 770 dbc = runtimeInfoFactory.getDbContext(); 771 try { 772 // we wrap this in a try-catch because otherwise it would fail during the update 773 // process, since the subscription driver configuration does not exist at that point. 774 driverManager.m_subscriptionDriver = (I_CmsSubscriptionDriver)driverManager.createDriver( 775 dbc, 776 configurationManager, 777 config, 778 CONFIGURATION_SUBSCRIPTION, 779 ".subscription.driver"); 780 } catch (IndexOutOfBoundsException npe) { 781 LOG.warn("Could not instantiate subscription driver!"); 782 LOG.warn(npe.getLocalizedMessage(), npe); 783 } 784 dbc.clear(); 785 786 // register the driver manager for required events 787 org.opencms.main.OpenCms.addCmsEventListener( 788 driverManager, 789 new int[] { 790 I_CmsEventListener.EVENT_UPDATE_EXPORTS, 791 I_CmsEventListener.EVENT_CLEAR_CACHES, 792 I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES, 793 I_CmsEventListener.EVENT_USER_MODIFIED, 794 I_CmsEventListener.EVENT_PUBLISH_PROJECT}); 795 796 // return the configured driver manager 797 return driverManager; 798 } 799 800 /** 801 * Adds an alias entry.<p> 802 * 803 * @param dbc the database context 804 * @param project the current project 805 * @param alias the alias to add 806 * 807 * @throws CmsException if something goes wrong 808 */ 809 public void addAlias(CmsDbContext dbc, CmsProject project, CmsAlias alias) throws CmsException { 810 811 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 812 vfsDriver.insertAlias(dbc, project, alias); 813 } 814 815 /** 816 * Adds a new relation to the given resource.<p> 817 * 818 * @param dbc the database context 819 * @param resource the resource to add the relation to 820 * @param target the target of the relation 821 * @param type the type of the relation 822 * @param importCase if importing relations 823 * 824 * @throws CmsException if something goes wrong 825 */ 826 public void addRelationToResource( 827 CmsDbContext dbc, 828 CmsResource resource, 829 CmsResource target, 830 CmsRelationType type, 831 boolean importCase) 832 throws CmsException { 833 834 if (type.isDefinedInContent()) { 835 throw new CmsIllegalArgumentException( 836 Messages.get().container( 837 Messages.ERR_ADD_RELATION_IN_CONTENT_3, 838 dbc.removeSiteRoot(resource.getRootPath()), 839 dbc.removeSiteRoot(target.getRootPath()), 840 type.getLocalizedName(dbc.getRequestContext().getLocale()))); 841 } 842 CmsRelation relation = new CmsRelation(resource, target, type); 843 getVfsDriver(dbc).createRelation(dbc, dbc.currentProject().getUuid(), relation); 844 // IMPORTANT: In the import case, the importer has to ensure that the 845 // resource is reindexed offline after the relation has been added. 846 // We do not trigger reindexing here for each added relation due to performance issues. 847 // in the the non-import case reindexing is triggered by the update of the last modification date. 848 if (!importCase) { 849 // log it 850 log( 851 dbc, 852 new CmsLogEntry( 853 dbc, 854 resource.getStructureId(), 855 CmsLogEntryType.RESOURCE_ADD_RELATION, 856 new String[] {relation.getSourcePath(), relation.getTargetPath()}), 857 false); 858 // touch the resource 859 setDateLastModified(dbc, resource, System.currentTimeMillis()); 860 } 861 } 862 863 /** 864 * Adds a resource to the given organizational unit.<p> 865 * 866 * @param dbc the current db context 867 * @param orgUnit the organizational unit to add the resource to 868 * @param resource the resource that is to be added to the organizational unit 869 * 870 * @throws CmsException if something goes wrong 871 * 872 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 873 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 874 */ 875 public void addResourceToOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource) 876 throws CmsException { 877 878 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 879 getUserDriver(dbc).addResourceToOrganizationalUnit(dbc, orgUnit, resource); 880 } 881 882 /** 883 * Adds a user to a group.<p> 884 * 885 * @param dbc the current database context 886 * @param username the name of the user that is to be added to the group 887 * @param groupname the name of the group 888 * @param readRoles if reading roles or groups 889 * 890 * @throws CmsException if operation was not successful 891 * @throws CmsDbEntryNotFoundException if the given user or the given group was not found 892 * 893 * @see #removeUserFromGroup(CmsDbContext, String, String, boolean) 894 */ 895 public void addUserToGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 896 throws CmsException, CmsDbEntryNotFoundException { 897 898 //check if group exists 899 CmsGroup group = readGroup(dbc, groupname); 900 if (group == null) { 901 // the group does not exists 902 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 903 } 904 if (group.isVirtual() && !readRoles) { 905 String roleName = CmsRole.valueOf(group).getGroupName(); 906 if (!userInGroup(dbc, username, roleName, true)) { 907 addUserToGroup(dbc, username, roleName, true); 908 return; 909 } 910 } 911 if (group.isVirtual()) { 912 // this is an hack to prevent unlimited recursive calls 913 readRoles = false; 914 } 915 if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) { 916 // we want a role but we got a group, or the other way 917 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 918 } 919 if (userInGroup(dbc, username, groupname, readRoles)) { 920 // the user is already member of the group 921 return; 922 } 923 //check if the user exists 924 CmsUser user = readUser(dbc, username); 925 if (user == null) { 926 // the user does not exists 927 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, username)); 928 } 929 930 // if adding an user to a role 931 if (readRoles) { 932 CmsRole role = CmsRole.valueOf(group); 933 // a role can only be set if the user has the given role 934 m_securityManager.checkRole(dbc, role); 935 // now we check if we already have the role 936 if (m_securityManager.hasRole(dbc, user, role)) { 937 // do nothing 938 return; 939 } 940 // and now we need to remove all possible child-roles 941 List<CmsRole> children = role.getChildren(true); 942 Iterator<CmsGroup> itUserGroups = getGroupsOfUser( 943 dbc, 944 username, 945 group.getOuFqn(), 946 true, 947 true, 948 true, 949 dbc.getRequestContext().getRemoteAddress()).iterator(); 950 while (itUserGroups.hasNext()) { 951 CmsGroup roleGroup = itUserGroups.next(); 952 if (children.contains(CmsRole.valueOf(roleGroup))) { 953 // remove only child roles 954 removeUserFromGroup(dbc, username, roleGroup.getName(), true); 955 } 956 } 957 // update virtual groups 958 Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator(); 959 while (it.hasNext()) { 960 CmsGroup virtualGroup = it.next(); 961 // here we say readroles = true, to prevent an unlimited recursive calls 962 addUserToGroup(dbc, username, virtualGroup.getName(), true); 963 } 964 } 965 966 //add this user to the group 967 getUserDriver(dbc).createUserInGroup(dbc, user.getId(), group.getId()); 968 969 // flush the cache 970 if (readRoles) { 971 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 972 } 973 m_monitor.flushUserGroups(user.getId()); 974 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 975 976 if (!dbc.getProjectId().isNullUUID() && !CmsProject.ONLINE_PROJECT_ID.equals(dbc.getProjectId())) { 977 // user modified event is not needed 978 return; 979 } 980 // fire user modified event 981 Map<String, Object> eventData = new HashMap<String, Object>(); 982 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 983 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 984 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 985 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 986 eventData.put( 987 I_CmsEventListener.KEY_USER_ACTION, 988 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP); 989 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 990 } 991 992 /** 993 * Changes the lock of a resource to the current user, 994 * that is "steals" the lock from another user.<p> 995 * 996 * @param dbc the current database context 997 * @param resource the resource to change the lock for 998 * @param lockType the new lock type to set 999 * 1000 * @throws CmsException if something goes wrong 1001 * @throws CmsSecurityException if something goes wrong 1002 * 1003 * 1004 * @see CmsObject#changeLock(String) 1005 * @see I_CmsResourceType#changeLock(CmsObject, CmsSecurityManager, CmsResource) 1006 * 1007 * @see CmsSecurityManager#hasPermissions(CmsRequestContext, CmsResource, CmsPermissionSet, boolean, CmsResourceFilter) 1008 */ 1009 public void changeLock(CmsDbContext dbc, CmsResource resource, CmsLockType lockType) 1010 throws CmsException, CmsSecurityException { 1011 1012 // get the current lock 1013 CmsLock currentLock = getLock(dbc, resource); 1014 // check if the resource is locked at all 1015 if (currentLock.getEditionLock().isUnlocked() && currentLock.getSystemLock().isUnlocked()) { 1016 throw new CmsLockException( 1017 Messages.get().container( 1018 Messages.ERR_CHANGE_LOCK_UNLOCKED_RESOURCE_1, 1019 dbc.getRequestContext().getSitePath(resource))); 1020 } else if ((lockType == CmsLockType.EXCLUSIVE) 1021 && currentLock.isExclusiveOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) { 1022 // the current lock requires no change 1023 return; 1024 } 1025 1026 // duplicate logic from CmsSecurityManager#hasPermissions() because lock state can't be ignored 1027 // if another user has locked the file, the current user can never get WRITE permissions with the default check 1028 int denied = 0; 1029 1030 // check if the current user is vfs manager 1031 boolean canIgnorePermissions = m_securityManager.hasRoleForResource( 1032 dbc, 1033 dbc.currentUser(), 1034 CmsRole.VFS_MANAGER, 1035 resource); 1036 // if the resource type is jsp 1037 // write is only allowed for developers 1038 if (!canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) { 1039 if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource)) { 1040 denied |= CmsPermissionSet.PERMISSION_WRITE; 1041 } 1042 } 1043 CmsPermissionSetCustom permissions; 1044 if (canIgnorePermissions) { 1045 // if the current user is administrator, anything is allowed 1046 permissions = new CmsPermissionSetCustom(~0); 1047 } else { 1048 // otherwise, get the permissions from the access control list 1049 permissions = getPermissions(dbc, resource, dbc.currentUser()); 1050 } 1051 // revoke the denied permissions 1052 permissions.denyPermissions(denied); 1053 // now check if write permission is granted 1054 if ((CmsPermissionSet.ACCESS_WRITE.getPermissions() 1055 & permissions.getPermissions()) != CmsPermissionSet.ACCESS_WRITE.getPermissions()) { 1056 // check failed, throw exception 1057 m_securityManager.checkPermissions( 1058 dbc.getRequestContext(), 1059 resource, 1060 CmsPermissionSet.ACCESS_WRITE, 1061 I_CmsPermissionHandler.PERM_DENIED); 1062 } 1063 // if we got here write permission is granted on the target 1064 1065 // remove the old lock 1066 m_lockManager.removeResource(dbc, resource, true, lockType.isSystem()); 1067 // apply the new lock 1068 lockResource(dbc, resource, lockType); 1069 } 1070 1071 /** 1072 * Returns a list with all sub resources of a given folder that have set the given property, 1073 * matching the current property's value with the given old value and replacing it by a given new value.<p> 1074 * 1075 * @param dbc the current database context 1076 * @param resource the resource on which property definition values are changed 1077 * @param propertyDefinition the name of the propertydefinition to change the value 1078 * @param oldValue the old value of the propertydefinition 1079 * @param newValue the new value of the propertydefinition 1080 * @param recursive if true, change the property value on the resource and recursively all property values on 1081 * sub-resources (only for folders) 1082 * @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed 1083 * 1084 * @throws CmsVfsException for now only when the search for the oldvalue failed. 1085 * @throws CmsException if operation was not successful 1086 */ 1087 public List<CmsResource> changeResourcesInFolderWithProperty( 1088 CmsDbContext dbc, 1089 CmsResource resource, 1090 String propertyDefinition, 1091 String oldValue, 1092 String newValue, 1093 boolean recursive) 1094 throws CmsVfsException, CmsException { 1095 1096 CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION; 1097 // collect the resources to look up 1098 List<CmsResource> resources = new ArrayList<CmsResource>(); 1099 if (recursive) { 1100 // read the files in the folder 1101 resources = readResourcesWithProperty(dbc, resource, propertyDefinition, null, filter); 1102 // add the folder itself 1103 resources.add(resource); 1104 } else { 1105 resources.add(resource); 1106 } 1107 1108 Pattern oldPattern; 1109 try { 1110 // remove the place holder if available 1111 String tmpOldValue = oldValue; 1112 if (tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_START) 1113 && tmpOldValue.contains(CmsStringUtil.PLACEHOLDER_END)) { 1114 tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_START, ""); 1115 tmpOldValue = tmpOldValue.replace(CmsStringUtil.PLACEHOLDER_END, ""); 1116 } 1117 // compile regular expression pattern 1118 oldPattern = Pattern.compile(tmpOldValue); 1119 } catch (PatternSyntaxException e) { 1120 throw new CmsVfsException( 1121 Messages.get().container( 1122 Messages.ERR_CHANGE_RESOURCES_IN_FOLDER_WITH_PROP_4, 1123 new Object[] {propertyDefinition, oldValue, newValue, resource.getRootPath()}), 1124 e); 1125 } 1126 1127 List<CmsResource> changedResources = new ArrayList<CmsResource>(resources.size()); 1128 // create permission set and filter to check each resource 1129 CmsPermissionSet perm = CmsPermissionSet.ACCESS_WRITE; 1130 for (int i = 0; i < resources.size(); i++) { 1131 // loop through found resources and check property values 1132 CmsResource res = resources.get(i); 1133 // check resource state and permissions 1134 try { 1135 m_securityManager.checkPermissions(dbc, res, perm, true, filter); 1136 } catch (Exception e) { 1137 // resource is deleted or not writable for current user 1138 continue; 1139 } 1140 CmsProperty property = readPropertyObject(dbc, res, propertyDefinition, false); 1141 String propertyValue = property.getValue(); 1142 boolean changed = false; 1143 if ((propertyValue != null) && oldPattern.matcher(propertyValue).matches()) { 1144 // apply the place holder content 1145 String tmpNewValue = CmsStringUtil.transformValues(oldValue, newValue, propertyValue); 1146 // change structure value 1147 property.setStructureValue(tmpNewValue); 1148 changed = true; 1149 } 1150 if (changed) { 1151 // write property object if something has changed 1152 writePropertyObject(dbc, res, property); 1153 changedResources.add(res); 1154 } 1155 } 1156 return changedResources; 1157 } 1158 1159 /** 1160 * Changes the resource flags of a resource.<p> 1161 * 1162 * The resource flags are used to indicate various "special" conditions 1163 * for a resource. Most notably, the "internal only" setting which signals 1164 * that a resource can not be directly requested with it's URL.<p> 1165 * 1166 * @param dbc the current database context 1167 * @param resource the resource to change the flags for 1168 * @param flags the new resource flags for this resource 1169 * 1170 * @throws CmsException if something goes wrong 1171 * 1172 * @see CmsObject#chflags(String, int) 1173 * @see I_CmsResourceType#chflags(CmsObject, CmsSecurityManager, CmsResource, int) 1174 */ 1175 public void chflags(CmsDbContext dbc, CmsResource resource, int flags) throws CmsException { 1176 1177 // must operate on a clone to ensure resource is not modified in case permissions are not granted 1178 CmsResource clone = (CmsResource)resource.clone(); 1179 clone.setFlags(flags); 1180 // log it 1181 log( 1182 dbc, 1183 new CmsLogEntry( 1184 dbc, 1185 resource.getStructureId(), 1186 CmsLogEntryType.RESOURCE_FLAGS, 1187 new String[] {resource.getRootPath()}), 1188 false); 1189 // write it 1190 writeResource(dbc, clone); 1191 } 1192 1193 /** 1194 * Changes the resource type of a resource.<p> 1195 * 1196 * OpenCms handles resources according to the resource type, 1197 * not the file suffix. This is e.g. why a JSP in OpenCms can have the 1198 * suffix ".html" instead of ".jsp" only. Changing the resource type 1199 * makes sense e.g. if you want to make a plain text file a JSP resource, 1200 * or a binary file an image, etc.<p> 1201 * 1202 * @param dbc the current database context 1203 * @param resource the resource to change the type for 1204 * @param type the new resource type for this resource 1205 * 1206 * @throws CmsException if something goes wrong 1207 * 1208 * @see CmsObject#chtype(String, int) 1209 * @see I_CmsResourceType#chtype(CmsObject, CmsSecurityManager, CmsResource, int) 1210 */ 1211 @SuppressWarnings({"javadoc", "deprecation"}) 1212 public void chtype(CmsDbContext dbc, CmsResource resource, int type) throws CmsException { 1213 1214 // must operate on a clone to ensure resource is not modified in case permissions are not granted 1215 CmsResource clone = (CmsResource)resource.clone(); 1216 I_CmsResourceType newType = OpenCms.getResourceManager().getResourceType(type); 1217 clone.setType(newType.getTypeId()); 1218 // log it 1219 log( 1220 dbc, 1221 new CmsLogEntry( 1222 dbc, 1223 resource.getStructureId(), 1224 CmsLogEntryType.RESOURCE_TYPE, 1225 new String[] {resource.getRootPath()}), 1226 false); 1227 // write it 1228 writeResource(dbc, clone); 1229 } 1230 1231 /** 1232 * Cleans up the publish history entries according to the given filter. 1233 * 1234 * @param dbc the database context 1235 * @param filter the filter 1236 * @return the number of cleaned up rows 1237 * @throws CmsDataAccessException if something goes wrong 1238 */ 1239 public int cleanupPublishHistory(CmsDbContext dbc, CmsPublishHistoryCleanupFilter filter) 1240 throws CmsDataAccessException { 1241 1242 int result = m_projectDriver.cleanupPublishHistory(dbc, filter); 1243 if (filter.getMode() == CmsPublishHistoryCleanupFilter.Mode.single) { 1244 OpenCms.getMemoryMonitor().cachePublishedResources(filter.getHistoryId().toString(), null); 1245 } else { 1246 OpenCms.getMemoryMonitor().flushCache(CmsMemoryMonitor.CacheType.PUBLISHED_RESOURCES); 1247 } 1248 return result; 1249 } 1250 1251 /** 1252 * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) 1253 */ 1254 public void cmsEvent(CmsEvent event) { 1255 1256 if (LOG.isDebugEnabled()) { 1257 LOG.debug(Messages.get().getBundle().key(Messages.LOG_CMS_EVENT_1, Integer.valueOf(event.getType()))); 1258 } 1259 1260 I_CmsReport report; 1261 CmsDbContext dbc; 1262 1263 switch (event.getType()) { 1264 1265 case I_CmsEventListener.EVENT_UPDATE_EXPORTS: 1266 dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT); 1267 updateExportPoints(dbc); 1268 break; 1269 1270 case I_CmsEventListener.EVENT_PUBLISH_PROJECT: 1271 CmsUUID publishHistoryId = new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID)); 1272 report = (I_CmsReport)event.getData().get(I_CmsEventListener.KEY_REPORT); 1273 dbc = (CmsDbContext)event.getData().get(I_CmsEventListener.KEY_DBCONTEXT); 1274 m_monitor.clearCacheForPublishing(); 1275 writeExportPoints(dbc, report, publishHistoryId); 1276 break; 1277 1278 case I_CmsEventListener.EVENT_CLEAR_CACHES: 1279 m_monitor.clearCache(); 1280 break; 1281 case I_CmsEventListener.EVENT_CLEAR_PRINCIPAL_CACHES: 1282 m_monitor.clearPrincipalsCache(); 1283 break; 1284 case I_CmsEventListener.EVENT_USER_MODIFIED: 1285 String action = (String)event.getData().get(I_CmsEventListener.KEY_USER_ACTION); 1286 m_monitor.flushCache( 1287 CacheType.USER, 1288 CacheType.GROUP, 1289 CacheType.ORG_UNIT, 1290 CacheType.ACL, 1291 CacheType.PERMISSION, 1292 CacheType.USER_LIST); 1293 if (I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_ADD_USER_TO_GROUP.equals(action) 1294 || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP.equals(action) 1295 || I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU.equals(action)) { 1296 1297 Object userIdObj = event.getData().get(I_CmsEventListener.KEY_USER_ID); 1298 if (userIdObj != null) { 1299 CmsUUID userId = null; 1300 if (userIdObj instanceof CmsUUID) { 1301 userId = (CmsUUID)userIdObj; 1302 } else if (userIdObj instanceof String) { 1303 try { 1304 userId = new CmsUUID(userIdObj.toString()); 1305 } catch (Exception e) { 1306 LOG.error(e.getLocalizedMessage(), e); 1307 } 1308 } 1309 if (userId != null) { 1310 m_monitor.flushUserGroups(userId); 1311 } 1312 } else { 1313 m_monitor.flushCache(CacheType.USERGROUPS); 1314 } 1315 m_monitor.flushCache(CacheType.HAS_ROLE, CacheType.ROLE_LIST); 1316 } 1317 break; 1318 default: 1319 // noop 1320 } 1321 } 1322 1323 /** 1324 * Copies the access control entries of a given resource to a destination resource.<p> 1325 * 1326 * Already existing access control entries of the destination resource are removed.<p> 1327 * 1328 * @param dbc the current database context 1329 * @param source the resource to copy the access control entries from 1330 * @param destination the resource to which the access control entries are copied 1331 * @param updateLastModifiedInfo if true, user and date "last modified" information on the target resource will be updated 1332 * 1333 * @throws CmsException if something goes wrong 1334 */ 1335 public void copyAccessControlEntries( 1336 CmsDbContext dbc, 1337 CmsResource source, 1338 CmsResource destination, 1339 boolean updateLastModifiedInfo) 1340 throws CmsException { 1341 1342 // get the entries to copy 1343 ListIterator<CmsAccessControlEntry> aceList = getUserDriver( 1344 dbc).readAccessControlEntries(dbc, dbc.currentProject(), source.getResourceId(), false).listIterator(); 1345 1346 // remove the current entries from the destination 1347 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), destination.getResourceId()); 1348 1349 // now write the new entries 1350 while (aceList.hasNext()) { 1351 CmsAccessControlEntry ace = aceList.next(); 1352 getUserDriver(dbc).createAccessControlEntry( 1353 dbc, 1354 dbc.currentProject(), 1355 destination.getResourceId(), 1356 ace.getPrincipal(), 1357 ace.getPermissions().getAllowedPermissions(), 1358 ace.getPermissions().getDeniedPermissions(), 1359 ace.getFlags()); 1360 } 1361 1362 // log it 1363 log( 1364 dbc, 1365 new CmsLogEntry( 1366 dbc, 1367 destination.getStructureId(), 1368 CmsLogEntryType.RESOURCE_PERMISSIONS, 1369 new String[] {destination.getRootPath()}), 1370 false); 1371 1372 // update the "last modified" information 1373 if (updateLastModifiedInfo) { 1374 setDateLastModified(dbc, destination, destination.getDateLastModified()); 1375 } 1376 1377 // clear the cache 1378 m_monitor.clearAccessControlListCache(); 1379 1380 // fire a resource modification event 1381 Map<String, Object> data = new HashMap<String, Object>(2); 1382 data.put(I_CmsEventListener.KEY_RESOURCE, destination); 1383 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 1384 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 1385 } 1386 1387 /** 1388 * Copies a resource.<p> 1389 * 1390 * You must ensure that the destination path is an absolute, valid and 1391 * existing VFS path. Relative paths from the source are currently not supported.<p> 1392 * 1393 * In case the target resource already exists, it is overwritten with the 1394 * source resource.<p> 1395 * 1396 * The <code>siblingMode</code> parameter controls how to handle siblings 1397 * during the copy operation. 1398 * Possible values for this parameter are: 1399 * <ul> 1400 * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_NEW}</code></li> 1401 * <li><code>{@link org.opencms.file.CmsResource#COPY_AS_SIBLING}</code></li> 1402 * <li><code>{@link org.opencms.file.CmsResource#COPY_PRESERVE_SIBLING}</code></li> 1403 * </ul><p> 1404 * 1405 * @param dbc the current database context 1406 * @param source the resource to copy 1407 * @param destination the name of the copy destination with complete path 1408 * @param siblingMode indicates how to handle siblings during copy 1409 * 1410 * @throws CmsException if something goes wrong 1411 * @throws CmsIllegalArgumentException if the <code>source</code> argument is <code>null</code> 1412 * 1413 * @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode) 1414 * @see I_CmsResourceType#copyResource(CmsObject, CmsSecurityManager, CmsResource, String, CmsResource.CmsResourceCopyMode) 1415 */ 1416 public CmsResource copyResource( 1417 CmsDbContext dbc, 1418 CmsResource source, 1419 String destination, 1420 CmsResource.CmsResourceCopyMode siblingMode) 1421 throws CmsException, CmsIllegalArgumentException { 1422 1423 // check the sibling mode to see if this resource has to be copied as a sibling 1424 boolean copyAsSibling = false; 1425 1426 // siblings of folders are not supported 1427 if (!source.isFolder()) { 1428 // if the "copy as sibling" mode is used, set the flag to true 1429 if (siblingMode == CmsResource.COPY_AS_SIBLING) { 1430 copyAsSibling = true; 1431 } 1432 // if the mode is "preserve siblings", we have to check the sibling counter 1433 if (siblingMode == CmsResource.COPY_PRESERVE_SIBLING) { 1434 if (source.getSiblingCount() > 1) { 1435 copyAsSibling = true; 1436 } 1437 } 1438 } 1439 1440 // read the source properties 1441 List<CmsProperty> properties = readPropertyObjects(dbc, source, false); 1442 1443 if (copyAsSibling) { 1444 // create a sibling of the source file at the destination 1445 return createSibling(dbc, source, destination, properties); 1446 } 1447 1448 // prepare the content if required 1449 byte[] content = null; 1450 if (source.isFile()) { 1451 if (source instanceof CmsFile) { 1452 // resource already is a file 1453 content = ((CmsFile)source).getContents(); 1454 } 1455 if ((content == null) || (content.length < 1)) { 1456 // no known content yet - read from database 1457 content = getVfsDriver(dbc).readContent(dbc, dbc.currentProject().getUuid(), source.getResourceId()); 1458 } 1459 } 1460 1461 // determine destination folder 1462 String destinationFoldername = CmsResource.getParentFolder(destination); 1463 1464 // read the destination folder (will also check read permissions) 1465 CmsFolder destinationFolder = m_securityManager.readFolder( 1466 dbc, 1467 destinationFoldername, 1468 CmsResourceFilter.IGNORE_EXPIRATION); 1469 1470 // no further permission check required here, will be done in createResource() 1471 1472 // set user and creation time stamps 1473 long currentTime = System.currentTimeMillis(); 1474 long dateLastModified; 1475 CmsUUID userLastModified; 1476 if (source.isFolder()) { 1477 // folders always get a new date and user when they are copied 1478 dateLastModified = currentTime; 1479 userLastModified = dbc.currentUser().getId(); 1480 } else { 1481 // files keep the date and user last modified from the source 1482 dateLastModified = source.getDateLastModified(); 1483 userLastModified = source.getUserLastModified(); 1484 } 1485 1486 // check the resource flags 1487 int flags = source.getFlags(); 1488 if (source.isLabeled()) { 1489 // reset "labeled" link flag for new resource 1490 flags &= ~CmsResource.FLAG_LABELED; 1491 } 1492 1493 // create the new resource 1494 CmsResource newResource = new CmsResource( 1495 new CmsUUID(), 1496 new CmsUUID(), 1497 destination, 1498 source.getTypeId(), 1499 source.isFolder(), 1500 flags, 1501 dbc.currentProject().getUuid(), 1502 CmsResource.STATE_NEW, 1503 currentTime, 1504 dbc.currentUser().getId(), 1505 dateLastModified, 1506 userLastModified, 1507 source.getDateReleased(), 1508 source.getDateExpired(), 1509 1, 1510 source.getLength(), 1511 source.getDateContent(), 1512 source.getVersion()); // version number does not matter since it will be computed later 1513 1514 // trigger "is touched" state on resource (will ensure modification date is kept unchanged) 1515 newResource.setDateLastModified(dateLastModified); 1516 1517 // log it 1518 log( 1519 dbc, 1520 new CmsLogEntry( 1521 dbc, 1522 newResource.getStructureId(), 1523 CmsLogEntryType.RESOURCE_COPIED, 1524 new String[] {newResource.getRootPath()}), 1525 false); 1526 1527 // create the resource 1528 newResource = createResource(dbc, destination, newResource, content, properties, false); 1529 // copy relations 1530 copyRelations(dbc, source, newResource); 1531 1532 // copy the access control entries to the created resource 1533 copyAccessControlEntries(dbc, source, newResource, false); 1534 1535 // clear the cache 1536 m_monitor.clearAccessControlListCache(); 1537 1538 List<CmsResource> modifiedResources = new ArrayList<CmsResource>(); 1539 modifiedResources.add(source); 1540 modifiedResources.add(newResource); 1541 modifiedResources.add(destinationFolder); 1542 OpenCms.fireCmsEvent( 1543 new CmsEvent( 1544 I_CmsEventListener.EVENT_RESOURCE_COPIED, 1545 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, modifiedResources))); 1546 return newResource; 1547 } 1548 1549 /** 1550 * Copies a resource to the current project of the user.<p> 1551 * 1552 * @param dbc the current database context 1553 * @param resource the resource to apply this operation to 1554 * 1555 * @throws CmsException if something goes wrong 1556 * 1557 * @see CmsObject#copyResourceToProject(String) 1558 * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource) 1559 */ 1560 public void copyResourceToProject(CmsDbContext dbc, CmsResource resource) throws CmsException { 1561 1562 // copy the resource to the project only if the resource is not already in the project 1563 if (!isInsideCurrentProject(dbc, resource.getRootPath())) { 1564 // check if there are already any subfolders of this resource 1565 I_CmsProjectDriver projectDriver = getProjectDriver(dbc); 1566 if (resource.isFolder()) { 1567 List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject()); 1568 for (int i = 0; i < projectResources.size(); i++) { 1569 String resname = projectResources.get(i); 1570 if (resname.startsWith(resource.getRootPath())) { 1571 // delete the existing project resource first 1572 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname); 1573 } 1574 } 1575 } 1576 try { 1577 projectDriver.createProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath()); 1578 } catch (CmsException exc) { 1579 // if the subfolder exists already - all is ok 1580 } finally { 1581 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 1582 1583 OpenCms.fireCmsEvent( 1584 new CmsEvent( 1585 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 1586 Collections.<String, Object> singletonMap("project", dbc.currentProject()))); 1587 } 1588 } 1589 } 1590 1591 /** 1592 * Counts the locked resources in this project.<p> 1593 * 1594 * @param project the project to count the locked resources in 1595 * 1596 * @return the amount of locked resources in this project 1597 */ 1598 public int countLockedResources(CmsProject project) { 1599 1600 // count locks 1601 return m_lockManager.countExclusiveLocksInProject(project); 1602 } 1603 1604 /** 1605 * Add a new group to the Cms.<p> 1606 * 1607 * Only the admin can do this. 1608 * Only users, which are in the group "administrators" are granted.<p> 1609 * 1610 * @param dbc the current database context 1611 * @param id the id of the new group 1612 * @param name the name of the new group 1613 * @param description the description for the new group 1614 * @param flags the flags for the new group 1615 * @param parent the name of the parent group (or <code>null</code>) 1616 * 1617 * @return new created group 1618 * 1619 * @throws CmsException if the creation of the group failed 1620 * @throws CmsIllegalArgumentException if the length of the given name was below 1 1621 */ 1622 public CmsGroup createGroup(CmsDbContext dbc, CmsUUID id, String name, String description, int flags, String parent) 1623 throws CmsIllegalArgumentException, CmsException { 1624 1625 // check the group name 1626 OpenCms.getValidationHandler().checkGroupName(CmsOrganizationalUnit.getSimpleName(name)); 1627 // trim the name 1628 name = name.trim(); 1629 1630 // check the OU 1631 readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 1632 1633 // get the id of the parent group if necessary 1634 if (CmsStringUtil.isNotEmpty(parent)) { 1635 CmsGroup parentGroup = readGroup(dbc, parent); 1636 if (!parentGroup.isRole() 1637 && !CmsOrganizationalUnit.getParentFqn(parent).equals(CmsOrganizationalUnit.getParentFqn(name))) { 1638 throw new CmsDataAccessException( 1639 Messages.get().container( 1640 Messages.ERR_PARENT_GROUP_MUST_BE_IN_SAME_OU_3, 1641 CmsOrganizationalUnit.getSimpleName(name), 1642 CmsOrganizationalUnit.getParentFqn(name), 1643 parent)); 1644 } 1645 } 1646 1647 // create the group 1648 CmsGroup group = getUserDriver(dbc).createGroup(dbc, id, name, description, flags, parent); 1649 1650 // if the group is in fact a role, initialize it 1651 if (group.isVirtual()) { 1652 // get all users that have the given role 1653 String groupname = CmsRole.valueOf(group).getGroupName(); 1654 Iterator<CmsUser> it = getUsersOfGroup(dbc, groupname, true, false, true).iterator(); 1655 while (it.hasNext()) { 1656 CmsUser user = it.next(); 1657 // put them in the new group 1658 addUserToGroup(dbc, user.getName(), group.getName(), true); 1659 } 1660 } 1661 1662 // put it into the cache 1663 m_monitor.cacheGroup(group); 1664 1665 if (!dbc.getProjectId().isNullUUID()) { 1666 // group modified event is not needed 1667 return group; 1668 } 1669 // fire group modified event 1670 Map<String, Object> eventData = new HashMap<String, Object>(); 1671 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 1672 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 1673 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_CREATE); 1674 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 1675 1676 // return it 1677 return group; 1678 } 1679 1680 /** 1681 * Creates a new organizational unit.<p> 1682 * 1683 * @param dbc the current db context 1684 * @param ouFqn the fully qualified name of the new organizational unit 1685 * @param description the description of the new organizational unit 1686 * @param flags the flags for the new organizational unit 1687 * @param resource the first associated resource 1688 * 1689 * @return a <code>{@link CmsOrganizationalUnit}</code> object representing 1690 * the newly created organizational unit 1691 * 1692 * @throws CmsException if operation was not successful 1693 * 1694 * @see org.opencms.security.CmsOrgUnitManager#createOrganizationalUnit(CmsObject, String, String, int, String) 1695 */ 1696 public CmsOrganizationalUnit createOrganizationalUnit( 1697 CmsDbContext dbc, 1698 String ouFqn, 1699 String description, 1700 int flags, 1701 CmsResource resource) 1702 throws CmsException { 1703 1704 // normal case 1705 CmsOrganizationalUnit parent = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(ouFqn)); 1706 String name = CmsOrganizationalUnit.getSimpleName(ouFqn); 1707 if (name.endsWith(CmsOrganizationalUnit.SEPARATOR)) { 1708 name = name.substring(0, name.length() - 1); 1709 } 1710 1711 // check the name 1712 CmsResource.checkResourceName(name); 1713 1714 // trim the name 1715 name = name.trim(); 1716 1717 // check the description 1718 if (CmsStringUtil.isEmptyOrWhitespaceOnly(description)) { 1719 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_OU_DESCRIPTION_EMPTY_0)); 1720 } 1721 1722 // create the organizational unit 1723 CmsOrganizationalUnit orgUnit = getUserDriver(dbc).createOrganizationalUnit( 1724 dbc, 1725 name, 1726 description, 1727 flags, 1728 parent, 1729 resource != null ? resource.getRootPath() : null); 1730 // put the new created org unit into the cache 1731 m_monitor.cacheOrgUnit(orgUnit); 1732 1733 // flush relevant caches 1734 m_monitor.clearPrincipalsCache(); 1735 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 1736 1737 // create a publish list for the 'virtual' publish event 1738 CmsResource ouRes = readResource( 1739 dbc, 1740 CmsUserDriver.ORGUNIT_BASE_FOLDER + orgUnit.getName(), 1741 CmsResourceFilter.DEFAULT); 1742 CmsPublishList pl = new CmsPublishList(ouRes, false); 1743 pl.add(ouRes, false); 1744 1745 getProjectDriver(dbc).writePublishHistory( 1746 dbc, 1747 pl.getPublishHistoryId(), 1748 new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW)); 1749 1750 // fire the 'virtual' publish event 1751 Map<String, Object> eventData = new HashMap<String, Object>(); 1752 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 1753 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 1754 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 1755 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 1756 OpenCms.fireCmsEvent(afterPublishEvent); 1757 1758 if (!dbc.getProjectId().isNullUUID()) { 1759 // OU modified event is not needed 1760 return orgUnit; 1761 } 1762 1763 // fire OU modified event 1764 Map<String, Object> event2Data = new HashMap<String, Object>(); 1765 event2Data.put(I_CmsEventListener.KEY_OU_NAME, orgUnit.getName()); 1766 event2Data.put(I_CmsEventListener.KEY_OU_ID, orgUnit.getId().toString()); 1767 event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_CREATE); 1768 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data)); 1769 1770 // return it 1771 return orgUnit; 1772 } 1773 1774 /** 1775 * Creates a project.<p> 1776 * 1777 * @param dbc the current database context 1778 * @param name the name of the project to create 1779 * @param description the description of the project 1780 * @param groupname the project user group to be set 1781 * @param managergroupname the project manager group to be set 1782 * @param projecttype the type of the project 1783 * 1784 * @return the created project 1785 * 1786 * @throws CmsIllegalArgumentException if the chosen <code>name</code> is already used 1787 * by the online project, or if the name is not valid 1788 * @throws CmsException if something goes wrong 1789 */ 1790 public CmsProject createProject( 1791 CmsDbContext dbc, 1792 String name, 1793 String description, 1794 String groupname, 1795 String managergroupname, 1796 CmsProject.CmsProjectType projecttype) 1797 throws CmsIllegalArgumentException, CmsException { 1798 1799 if (CmsProject.ONLINE_PROJECT_NAME.equals(name)) { 1800 throw new CmsIllegalArgumentException( 1801 Messages.get().container( 1802 Messages.ERR_CREATE_PROJECT_ONLINE_PROJECT_NAME_1, 1803 CmsProject.ONLINE_PROJECT_NAME)); 1804 } 1805 // check the name 1806 CmsProject.checkProjectName(CmsOrganizationalUnit.getSimpleName(name)); 1807 // check the ou 1808 readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 1809 // read the needed groups from the cms 1810 CmsGroup group = readGroup(dbc, groupname); 1811 CmsGroup managergroup = readGroup(dbc, managergroupname); 1812 1813 return getProjectDriver(dbc).createProject( 1814 dbc, 1815 new CmsUUID(), 1816 dbc.currentUser(), 1817 group, 1818 managergroup, 1819 name, 1820 description, 1821 projecttype.getDefaultFlags(), 1822 projecttype); 1823 } 1824 1825 /** 1826 * Creates a property definition.<p> 1827 * 1828 * Property definitions are valid for all resource types.<p> 1829 * 1830 * @param dbc the current database context 1831 * @param name the name of the property definition to create 1832 * 1833 * @return the created property definition 1834 * 1835 * @throws CmsException if something goes wrong 1836 */ 1837 public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 1838 1839 CmsPropertyDefinition propertyDefinition = null; 1840 1841 name = name.trim(); 1842 // validate the property name 1843 CmsPropertyDefinition.checkPropertyName(name); 1844 // TODO: make the type a parameter 1845 try { 1846 try { 1847 propertyDefinition = getVfsDriver(dbc).readPropertyDefinition( 1848 dbc, 1849 name, 1850 dbc.currentProject().getUuid()); 1851 } catch (CmsException e) { 1852 propertyDefinition = getVfsDriver(dbc).createPropertyDefinition( 1853 dbc, 1854 dbc.currentProject().getUuid(), 1855 name, 1856 CmsPropertyDefinition.TYPE_NORMAL); 1857 } 1858 1859 try { 1860 getVfsDriver(dbc).readPropertyDefinition(dbc, name, CmsProject.ONLINE_PROJECT_ID); 1861 } catch (CmsException e) { 1862 getVfsDriver(dbc).createPropertyDefinition( 1863 dbc, 1864 CmsProject.ONLINE_PROJECT_ID, 1865 name, 1866 CmsPropertyDefinition.TYPE_NORMAL); 1867 } 1868 1869 try { 1870 getHistoryDriver(dbc).readPropertyDefinition(dbc, name); 1871 } catch (CmsException e) { 1872 getHistoryDriver(dbc).createPropertyDefinition(dbc, name, CmsPropertyDefinition.TYPE_NORMAL); 1873 } 1874 } finally { 1875 1876 // fire an event that a property of a resource has been deleted 1877 OpenCms.fireCmsEvent( 1878 new CmsEvent( 1879 I_CmsEventListener.EVENT_PROPERTY_DEFINITION_CREATED, 1880 Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition))); 1881 1882 } 1883 1884 return propertyDefinition; 1885 } 1886 1887 /** 1888 * Creates a new publish job.<p> 1889 * 1890 * @param dbc the current database context 1891 * @param publishJob the publish job to create 1892 * 1893 * @throws CmsException if something goes wrong 1894 */ 1895 public void createPublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 1896 1897 getProjectDriver(dbc).createPublishJob(dbc, publishJob); 1898 } 1899 1900 /** 1901 * Creates a new resource with the provided content and properties.<p> 1902 * 1903 * The <code>content</code> parameter may be <code>null</code> if the resource id 1904 * already exists. If so, the created resource will be a sibling of the existing 1905 * resource, the existing content will remain unchanged.<p> 1906 * 1907 * This is used during file import for import of siblings as the 1908 * <code>manifest.xml</code> only contains one binary copy per file.<p> 1909 * 1910 * If the resource id exists but the <code>content</code> is not <code>null</code>, 1911 * the created resource will be made a sibling of the existing resource, 1912 * and both will share the new content.<p> 1913 * 1914 * @param dbc the current database context 1915 * @param resourcePath the name of the resource to create (full path) 1916 * @param resource the new resource to create 1917 * @param content the content for the new resource 1918 * @param properties the properties for the new resource 1919 * @param importCase if <code>true</code>, signals that this operation is done while 1920 * importing resource, causing different lock behavior and 1921 * potential "lost and found" usage 1922 * 1923 * @return the created resource 1924 * 1925 * @throws CmsException if something goes wrong 1926 */ 1927 public CmsResource createResource( 1928 CmsDbContext dbc, 1929 String resourcePath, 1930 CmsResource resource, 1931 byte[] content, 1932 List<CmsProperty> properties, 1933 boolean importCase) 1934 throws CmsException { 1935 1936 CmsResource newResource = null; 1937 if (resource.isFolder()) { 1938 resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath); 1939 } 1940 1941 try { 1942 synchronized (this) { 1943 // need to provide the parent folder id for resource creation 1944 String parentFolderName = CmsResource.getParentFolder(resourcePath); 1945 CmsResource parentFolder = readFolder(dbc, parentFolderName, CmsResourceFilter.IGNORE_EXPIRATION); 1946 1947 CmsLock parentLock = getLock(dbc, parentFolder); 1948 // it is not allowed to create a resource in a folder locked by other user 1949 if (!parentLock.isUnlocked() && !parentLock.isOwnedBy(dbc.currentUser())) { 1950 // one exception is if the admin user tries to create a temporary resource 1951 if (!CmsResource.getName(resourcePath).startsWith(TEMP_FILE_PREFIX) 1952 || !m_securityManager.hasRole(dbc, dbc.currentUser(), CmsRole.ROOT_ADMIN)) { 1953 throw new CmsLockException( 1954 Messages.get().container( 1955 Messages.ERR_CREATE_RESOURCE_PARENT_LOCK_1, 1956 dbc.removeSiteRoot(resourcePath))); 1957 } 1958 } 1959 if (CmsResourceTypeJsp.isJsp(resource)) { 1960 // security check when trying to create a new jsp file 1961 m_securityManager.checkRoleForResource(dbc, CmsRole.VFS_MANAGER, parentFolder); 1962 } 1963 1964 // check import configuration of "lost and found" folder 1965 boolean useLostAndFound = importCase && !OpenCms.getImportExportManager().overwriteCollidingResources(); 1966 1967 // check if the resource already exists by name 1968 CmsResource currentResourceByName = null; 1969 try { 1970 currentResourceByName = readResource(dbc, resourcePath, CmsResourceFilter.ALL); 1971 } catch (CmsVfsResourceNotFoundException e) { 1972 // if the resource does exist, we have to check the id later to decide what to do 1973 } 1974 1975 // check if the resource already exists by id 1976 try { 1977 CmsResource currentResourceById = readResource( 1978 dbc, 1979 resource.getStructureId(), 1980 CmsResourceFilter.ALL); 1981 // it is not allowed to import resources when there is already a resource with the same id but different path 1982 if (!currentResourceById.getRootPath().equals(resourcePath)) { 1983 throw new CmsVfsResourceAlreadyExistsException( 1984 Messages.get().container( 1985 Messages.ERR_RESOURCE_WITH_ID_ALREADY_EXISTS_3, 1986 dbc.removeSiteRoot(resourcePath), 1987 dbc.removeSiteRoot(currentResourceById.getRootPath()), 1988 currentResourceById.getStructureId())); 1989 } 1990 } catch (CmsVfsResourceNotFoundException e) { 1991 // if the resource does exist, we have to check the id later to decide what to do 1992 } 1993 1994 // check the permissions 1995 if (currentResourceByName == null) { 1996 // resource does not exist - check parent folder 1997 m_securityManager.checkPermissions( 1998 dbc, 1999 parentFolder, 2000 CmsPermissionSet.ACCESS_WRITE, 2001 false, 2002 CmsResourceFilter.IGNORE_EXPIRATION); 2003 } else { 2004 // resource already exists - check existing resource 2005 m_securityManager.checkPermissions( 2006 dbc, 2007 currentResourceByName, 2008 CmsPermissionSet.ACCESS_WRITE, 2009 !importCase, 2010 CmsResourceFilter.ALL); 2011 } 2012 2013 // now look for the resource by name 2014 if (currentResourceByName != null) { 2015 boolean overwrite = true; 2016 if (currentResourceByName.getState().isDeleted()) { 2017 if (!currentResourceByName.isFolder()) { 2018 // if a non-folder resource was deleted it's treated like a new resource 2019 overwrite = false; 2020 } 2021 } else { 2022 if (!importCase) { 2023 // direct "overwrite" of a resource is possible only during import, 2024 // or if the resource has been deleted 2025 throw new CmsVfsResourceAlreadyExistsException( 2026 org.opencms.db.generic.Messages.get().container( 2027 org.opencms.db.generic.Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1, 2028 dbc.removeSiteRoot(resource.getRootPath()))); 2029 } 2030 // the resource already exists 2031 if (!resource.isFolder() 2032 && useLostAndFound 2033 && (!currentResourceByName.getResourceId().equals(resource.getResourceId()))) { 2034 // semantic change: the current resource is moved to L&F and the imported resource will overwrite the old one 2035 // will leave the resource with state deleted, 2036 // but it does not matter, since the state will be set later again 2037 moveToLostAndFound(dbc, currentResourceByName, false); 2038 } 2039 } 2040 if (!overwrite) { 2041 // lock the resource, will throw an exception if not lockable 2042 lockResource(dbc, currentResourceByName, CmsLockType.EXCLUSIVE); 2043 2044 // trigger createResource instead of writeResource 2045 currentResourceByName = null; 2046 } 2047 } 2048 // if null, create new resource, if not null write resource 2049 CmsResource overwrittenResource = currentResourceByName; 2050 2051 // extract the name (without path) 2052 String targetName = CmsResource.getName(resourcePath); 2053 2054 int contentLength; 2055 2056 // modify target name and content length in case of folder creation 2057 if (resource.isFolder()) { 2058 // folders never have any content 2059 contentLength = -1; 2060 // must cut of trailing '/' for folder creation (or name check fails) 2061 if (CmsResource.isFolder(targetName)) { 2062 targetName = targetName.substring(0, targetName.length() - 1); 2063 } 2064 } else { 2065 // otherwise ensure content and content length are set correctly 2066 if (content != null) { 2067 // if a content is provided, in each case the length is the length of this content 2068 contentLength = content.length; 2069 } else if (overwrittenResource != null) { 2070 // we have no content, but an already existing resource - length remains unchanged 2071 contentLength = overwrittenResource.getLength(); 2072 } else { 2073 // we have no content - length is used as set in the resource 2074 contentLength = resource.getLength(); 2075 } 2076 } 2077 2078 // check if the target name is valid (forbidden chars etc.), 2079 // if not throw an exception 2080 // must do this here since targetName is modified in folder case (see above) 2081 CmsResource.checkResourceName(targetName); 2082 2083 // set structure and resource ids as given 2084 CmsUUID structureId = resource.getStructureId(); 2085 CmsUUID resourceId = resource.getResourceId(); 2086 2087 // decide which structure id to use 2088 if (overwrittenResource != null) { 2089 // resource exists, re-use existing ids 2090 structureId = overwrittenResource.getStructureId(); 2091 } 2092 if (structureId.isNullUUID()) { 2093 // need a new structure id 2094 structureId = new CmsUUID(); 2095 } 2096 2097 // decide which resource id to use 2098 if (overwrittenResource != null) { 2099 // if we are overwriting we have to assure the resource id is the same 2100 resourceId = overwrittenResource.getResourceId(); 2101 } 2102 if (resourceId.isNullUUID()) { 2103 // need a new resource id 2104 resourceId = new CmsUUID(); 2105 } 2106 2107 try { 2108 // check online resource 2109 CmsResource onlineResource = getVfsDriver( 2110 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resourcePath, true); 2111 // only allow to overwrite with different id if importing (createResource will set the right id) 2112 try { 2113 CmsResource offlineResource = getVfsDriver(dbc).readResource( 2114 dbc, 2115 dbc.currentProject().getUuid(), 2116 onlineResource.getStructureId(), 2117 true); 2118 if (!offlineResource.getRootPath().equals(onlineResource.getRootPath())) { 2119 throw new CmsVfsOnlineResourceAlreadyExistsException( 2120 Messages.get().container( 2121 Messages.ERR_ONLINE_RESOURCE_EXISTS_2, 2122 dbc.removeSiteRoot(resourcePath), 2123 dbc.removeSiteRoot(offlineResource.getRootPath()))); 2124 } 2125 } catch (CmsVfsResourceNotFoundException e) { 2126 // there is no problem for now 2127 // but should never happen 2128 if (LOG.isErrorEnabled()) { 2129 LOG.error(e.getLocalizedMessage(), e); 2130 } 2131 } 2132 } catch (CmsVfsResourceNotFoundException e) { 2133 // ok, there is no online entry to worry about 2134 } 2135 2136 // now create a resource object with all informations 2137 newResource = new CmsResource( 2138 structureId, 2139 resourceId, 2140 resourcePath, 2141 resource.getTypeId(), 2142 resource.isFolder(), 2143 resource.getFlags(), 2144 dbc.currentProject().getUuid(), 2145 resource.getState(), 2146 resource.getDateCreated(), 2147 resource.getUserCreated(), 2148 resource.getDateLastModified(), 2149 resource.getUserLastModified(), 2150 resource.getDateReleased(), 2151 resource.getDateExpired(), 2152 1, 2153 contentLength, 2154 resource.getDateContent(), 2155 resource.getVersion()); // version number does not matter since it will be computed later 2156 2157 // ensure date is updated only if required 2158 if (resource.isTouched()) { 2159 // this will trigger the internal "is touched" state on the new resource 2160 newResource.setDateLastModified(resource.getDateLastModified()); 2161 } 2162 2163 if (resource.isFile()) { 2164 // check if a sibling to the imported resource lies in a marked site 2165 if (labelResource(dbc, resource, resourcePath, 2)) { 2166 int flags = resource.getFlags(); 2167 flags |= CmsResource.FLAG_LABELED; 2168 resource.setFlags(flags); 2169 } 2170 // ensure siblings don't overwrite existing resource records 2171 if (content == null) { 2172 newResource.setState(CmsResource.STATE_KEEP); 2173 } 2174 } 2175 2176 // delete all relations for the resource, before writing the content 2177 getVfsDriver( 2178 dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), newResource, CmsRelationFilter.TARGETS); 2179 if (overwrittenResource == null) { 2180 CmsLock lock = getLock(dbc, newResource); 2181 if (lock.getEditionLock().isExclusive()) { 2182 unlockResource(dbc, newResource, true, false); 2183 } 2184 // resource does not exist. 2185 newResource = getVfsDriver( 2186 dbc).createResource(dbc, dbc.currentProject().getUuid(), newResource, content); 2187 } else { 2188 // resource already exists. 2189 // probably the resource is a merged page file that gets overwritten during import, or it gets 2190 // overwritten by a copy operation. if so, the structure & resource state are not modified to changed. 2191 int updateStates = (overwrittenResource.getState().isNew() 2192 ? CmsDriverManager.NOTHING_CHANGED 2193 : CmsDriverManager.UPDATE_ALL); 2194 getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), newResource, updateStates); 2195 2196 if ((content != null) && resource.isFile()) { 2197 // also update file content if required 2198 getVfsDriver(dbc).writeContent(dbc, newResource.getResourceId(), content); 2199 } 2200 } 2201 2202 // write the properties (internal operation, no events or duplicate permission checks) 2203 writePropertyObjects(dbc, newResource, properties, false); 2204 2205 // lock the created resource 2206 try { 2207 // if it is locked by another user (copied or moved resource) this lock should be preserved and 2208 // the exception is OK: locks on created resources are a slave feature to original locks 2209 lockResource(dbc, newResource, CmsLockType.EXCLUSIVE); 2210 } catch (CmsLockException cle) { 2211 if (LOG.isDebugEnabled()) { 2212 LOG.debug( 2213 Messages.get().getBundle().key( 2214 Messages.ERR_CREATE_RESOURCE_LOCK_1, 2215 new Object[] {dbc.removeSiteRoot(newResource.getRootPath())})); 2216 } 2217 } 2218 2219 if (!importCase) { 2220 log( 2221 dbc, 2222 new CmsLogEntry( 2223 dbc, 2224 newResource.getStructureId(), 2225 CmsLogEntryType.RESOURCE_CREATED, 2226 new String[] {resource.getRootPath()}), 2227 false); 2228 } else { 2229 log( 2230 dbc, 2231 new CmsLogEntry( 2232 dbc, 2233 newResource.getStructureId(), 2234 CmsLogEntryType.RESOURCE_IMPORTED, 2235 new String[] {resource.getRootPath()}), 2236 false); 2237 } 2238 } 2239 } finally { 2240 // clear the internal caches 2241 m_monitor.clearAccessControlListCache(); 2242 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2243 2244 if (newResource != null) { 2245 // fire an event that a new resource has been created 2246 OpenCms.fireCmsEvent( 2247 new CmsEvent( 2248 I_CmsEventListener.EVENT_RESOURCE_CREATED, 2249 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, newResource))); 2250 } 2251 } 2252 return newResource; 2253 } 2254 2255 /** 2256 * Creates a new resource of the given resource type 2257 * with the provided content and properties.<p> 2258 * 2259 * If the provided content is null and the resource is not a folder, 2260 * the content will be set to an empty byte array.<p> 2261 * 2262 * @param dbc the current database context 2263 * @param resourcename the name of the resource to create (full path) 2264 * @param type the type of the resource to create 2265 * @param content the content for the new resource 2266 * @param properties the properties for the new resource 2267 * 2268 * @return the created resource 2269 * 2270 * @throws CmsException if something goes wrong 2271 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 2272 * 2273 * @see CmsObject#createResource(String, int, byte[], List) 2274 * @see CmsObject#createResource(String, int) 2275 * @see I_CmsResourceType#createResource(CmsObject, CmsSecurityManager, String, byte[], List) 2276 */ 2277 @SuppressWarnings("javadoc") 2278 public CmsResource createResource( 2279 CmsDbContext dbc, 2280 String resourcename, 2281 int type, 2282 byte[] content, 2283 List<CmsProperty> properties) 2284 throws CmsException, CmsIllegalArgumentException { 2285 2286 String targetName = resourcename; 2287 2288 if (content == null) { 2289 // name based resource creation MUST have a content 2290 content = new byte[0]; 2291 } 2292 int size; 2293 2294 if (CmsFolder.isFolderType(type)) { 2295 // must cut of trailing '/' for folder creation 2296 if (CmsResource.isFolder(targetName)) { 2297 targetName = targetName.substring(0, targetName.length() - 1); 2298 } 2299 size = -1; 2300 } else { 2301 size = content.length; 2302 } 2303 2304 // create a new resource 2305 CmsResource newResource = new CmsResource( 2306 CmsUUID.getNullUUID(), // uuids will be "corrected" later 2307 CmsUUID.getNullUUID(), 2308 targetName, 2309 type, 2310 CmsFolder.isFolderType(type), 2311 0, 2312 dbc.currentProject().getUuid(), 2313 CmsResource.STATE_NEW, 2314 0, 2315 dbc.currentUser().getId(), 2316 0, 2317 dbc.currentUser().getId(), 2318 CmsResource.DATE_RELEASED_DEFAULT, 2319 CmsResource.DATE_EXPIRED_DEFAULT, 2320 1, 2321 size, 2322 0, // version number does not matter since it will be computed later 2323 0); // content time will be corrected later 2324 2325 return createResource(dbc, targetName, newResource, content, properties, false); 2326 } 2327 2328 /** 2329 * Creates a new sibling of the source resource.<p> 2330 * 2331 * @param dbc the current database context 2332 * @param source the resource to create a sibling for 2333 * @param destination the name of the sibling to create with complete path 2334 * @param properties the individual properties for the new sibling 2335 * 2336 * @return the new created sibling 2337 * 2338 * @throws CmsException if something goes wrong 2339 * 2340 * @see CmsObject#createSibling(String, String, List) 2341 * @see I_CmsResourceType#createSibling(CmsObject, CmsSecurityManager, CmsResource, String, List) 2342 */ 2343 public CmsResource createSibling( 2344 CmsDbContext dbc, 2345 CmsResource source, 2346 String destination, 2347 List<CmsProperty> properties) 2348 throws CmsException { 2349 2350 if (source.isFolder()) { 2351 throw new CmsVfsException(Messages.get().container(Messages.ERR_VFS_FOLDERS_DONT_SUPPORT_SIBLINGS_0)); 2352 } 2353 2354 // determine destination folder and resource name 2355 String destinationFoldername = CmsResource.getParentFolder(destination); 2356 2357 // read the destination folder (will also check read permissions) 2358 CmsFolder destinationFolder = readFolder(dbc, destinationFoldername, CmsResourceFilter.IGNORE_EXPIRATION); 2359 2360 // no further permission check required here, will be done in createResource() 2361 2362 // check the resource flags 2363 int flags = source.getFlags(); 2364 if (labelResource(dbc, source, destination, 1)) { 2365 // set "labeled" link flag for new resource 2366 flags |= CmsResource.FLAG_LABELED; 2367 } 2368 2369 // create the new resource 2370 CmsResource newResource = new CmsResource( 2371 new CmsUUID(), 2372 source.getResourceId(), 2373 destination, 2374 source.getTypeId(), 2375 source.isFolder(), 2376 flags, 2377 dbc.currentProject().getUuid(), 2378 CmsResource.STATE_KEEP, 2379 source.getDateCreated(), // ensures current resource record remains untouched 2380 source.getUserCreated(), 2381 source.getDateLastModified(), 2382 source.getUserLastModified(), 2383 source.getDateReleased(), 2384 source.getDateExpired(), 2385 source.getSiblingCount() + 1, 2386 source.getLength(), 2387 source.getDateContent(), 2388 source.getVersion()); // version number does not matter since it will be computed later 2389 2390 // trigger "is touched" state on resource (will ensure modification date is kept unchanged) 2391 newResource.setDateLastModified(newResource.getDateLastModified()); 2392 2393 log( 2394 dbc, 2395 new CmsLogEntry( 2396 dbc, 2397 newResource.getStructureId(), 2398 CmsLogEntryType.RESOURCE_CLONED, 2399 new String[] {newResource.getRootPath()}), 2400 false); 2401 // create the resource (null content signals creation of sibling) 2402 newResource = createResource(dbc, destination, newResource, null, properties, false); 2403 2404 // copy relations 2405 copyRelations(dbc, source, newResource); 2406 2407 // clear the caches 2408 m_monitor.clearAccessControlListCache(); 2409 2410 List<CmsResource> modifiedResources = new ArrayList<CmsResource>(); 2411 modifiedResources.add(source); 2412 modifiedResources.add(newResource); 2413 modifiedResources.add(destinationFolder); 2414 Map<String, Object> eventData = new HashMap<>(); 2415 eventData.put(I_CmsEventListener.KEY_RESOURCES, modifiedResources); 2416 eventData.put(I_CmsEventListener.KEY_CHANGE, I_CmsEventListener.VALUE_CREATE_SIBLING); 2417 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, eventData)); 2418 2419 return newResource; 2420 } 2421 2422 /** 2423 * Creates the project for the temporary workplace files.<p> 2424 * 2425 * @param dbc the current database context 2426 * 2427 * @return the created project for the temporary workplace files 2428 * 2429 * @throws CmsException if something goes wrong 2430 */ 2431 public CmsProject createTempfileProject(CmsDbContext dbc) throws CmsException { 2432 2433 // read the needed groups from the cms 2434 CmsGroup projectUserGroup = readGroup(dbc, dbc.currentProject().getGroupId()); 2435 CmsGroup projectManagerGroup = readGroup(dbc, dbc.currentProject().getManagerGroupId()); 2436 2437 CmsProject tempProject = getProjectDriver(dbc).createProject( 2438 dbc, 2439 new CmsUUID(), 2440 dbc.currentUser(), 2441 projectUserGroup, 2442 projectManagerGroup, 2443 I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME, 2444 Messages.get().getBundle(dbc.getRequestContext().getLocale()).key( 2445 Messages.GUI_WORKPLACE_TEMPFILE_PROJECT_DESC_0), 2446 CmsProject.PROJECT_FLAG_HIDDEN, 2447 CmsProject.PROJECT_TYPE_NORMAL); 2448 getProjectDriver(dbc).createProjectResource(dbc, tempProject.getUuid(), "/"); 2449 2450 OpenCms.fireCmsEvent( 2451 new CmsEvent( 2452 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 2453 Collections.<String, Object> singletonMap("project", tempProject))); 2454 2455 return tempProject; 2456 } 2457 2458 /** 2459 * Creates a new user.<p> 2460 * 2461 * @param dbc the current database context 2462 * @param name the name for the new user 2463 * @param password the password for the new user 2464 * @param description the description for the new user 2465 * @param additionalInfos the additional infos for the user 2466 * 2467 * @return the created user 2468 * 2469 * @see CmsObject#createUser(String, String, String, Map) 2470 * 2471 * @throws CmsException if something goes wrong 2472 * @throws CmsIllegalArgumentException if the name for the user is not valid 2473 */ 2474 public CmsUser createUser( 2475 CmsDbContext dbc, 2476 String name, 2477 String password, 2478 String description, 2479 Map<String, Object> additionalInfos) 2480 throws CmsException, CmsIllegalArgumentException { 2481 2482 // no space before or after the name 2483 name = name.trim(); 2484 // check the user name 2485 String userName = CmsOrganizationalUnit.getSimpleName(name); 2486 OpenCms.getValidationHandler().checkUserName(userName); 2487 if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) { 2488 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName)); 2489 } 2490 // check the ou 2491 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 2492 // check the password 2493 validatePassword(password); 2494 2495 Map<String, Object> info = new HashMap<String, Object>(); 2496 if (additionalInfos != null) { 2497 info.putAll(additionalInfos); 2498 } 2499 if (description != null) { 2500 info.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description); 2501 } 2502 int flags = 0; 2503 if (ou.hasFlagWebuser()) { 2504 flags += I_CmsPrincipal.FLAG_USER_WEBUSER; 2505 } 2506 CmsUser user = getUserDriver(dbc).createUser( 2507 dbc, 2508 new CmsUUID(), 2509 name, 2510 OpenCms.getPasswordHandler().digest(password), 2511 " ", 2512 " ", 2513 " ", 2514 0, 2515 I_CmsPrincipal.FLAG_ENABLED + flags, 2516 0, 2517 info); 2518 2519 if (!dbc.getProjectId().isNullUUID()) { 2520 // user modified event is not needed 2521 return user; 2522 } 2523 // fire user modified event 2524 Map<String, Object> eventData = new HashMap<String, Object>(); 2525 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 2526 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_CREATE_USER); 2527 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 2528 return user; 2529 } 2530 2531 /** 2532 * Deletes aliases indicated by a filter.<p> 2533 * 2534 * @param dbc the current database context 2535 * @param project the current project 2536 * @param filter the filter which describes which aliases to delete 2537 * 2538 * @throws CmsException if something goes wrong 2539 */ 2540 public void deleteAliases(CmsDbContext dbc, CmsProject project, CmsAliasFilter filter) throws CmsException { 2541 2542 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 2543 vfsDriver.deleteAliases(dbc, project, filter); 2544 } 2545 2546 /** 2547 * Deletes all property values of a file or folder.<p> 2548 * 2549 * If there are no other siblings than the specified resource, 2550 * both the structure and resource property values get deleted. 2551 * If the specified resource has siblings, only the structure 2552 * property values get deleted.<p> 2553 * 2554 * @param dbc the current database context 2555 * @param resourcename the name of the resource for which all properties should be deleted 2556 * 2557 * @throws CmsException if operation was not successful 2558 */ 2559 public void deleteAllProperties(CmsDbContext dbc, String resourcename) throws CmsException { 2560 2561 CmsResource resource = null; 2562 List<CmsResource> resources = new ArrayList<CmsResource>(); 2563 2564 try { 2565 // read the resource 2566 resource = readResource(dbc, resourcename, CmsResourceFilter.IGNORE_EXPIRATION); 2567 2568 // check the security 2569 m_securityManager.checkPermissions( 2570 dbc, 2571 resource, 2572 CmsPermissionSet.ACCESS_WRITE, 2573 false, 2574 CmsResourceFilter.ALL); 2575 2576 // delete the property values 2577 if (resource.getSiblingCount() > 1) { 2578 // the resource has siblings- delete only the (structure) properties of this sibling 2579 getVfsDriver(dbc).deletePropertyObjects( 2580 dbc, 2581 dbc.currentProject().getUuid(), 2582 resource, 2583 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES); 2584 resources.addAll(readSiblings(dbc, resource, CmsResourceFilter.ALL)); 2585 2586 } else { 2587 // the resource has no other siblings- delete all (structure+resource) properties 2588 getVfsDriver(dbc).deletePropertyObjects( 2589 dbc, 2590 dbc.currentProject().getUuid(), 2591 resource, 2592 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 2593 resources.add(resource); 2594 } 2595 } finally { 2596 // clear the driver manager cache 2597 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2598 2599 // fire an event that all properties of a resource have been deleted 2600 OpenCms.fireCmsEvent( 2601 new CmsEvent( 2602 I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED, 2603 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCES, resources))); 2604 } 2605 } 2606 2607 /** 2608 * Deletes all entries in the published resource table.<p> 2609 * 2610 * @param dbc the current database context 2611 * @param linkType the type of resource deleted (0= non-paramter, 1=parameter) 2612 * 2613 * @throws CmsException if something goes wrong 2614 */ 2615 public void deleteAllStaticExportPublishedResources(CmsDbContext dbc, int linkType) throws CmsException { 2616 2617 getProjectDriver(dbc).deleteAllStaticExportPublishedResources(dbc, linkType); 2618 } 2619 2620 /** 2621 * Deletes a group, where all permissions, users and children of the group 2622 * are transfered to a replacement group.<p> 2623 * 2624 * @param dbc the current request context 2625 * @param group the id of the group to be deleted 2626 * @param replacementId the id of the group to be transfered, can be <code>null</code> 2627 * 2628 * @throws CmsException if operation was not successful 2629 * @throws CmsDataAccessException if group to be deleted contains user 2630 */ 2631 public void deleteGroup(CmsDbContext dbc, CmsGroup group, CmsUUID replacementId) 2632 throws CmsDataAccessException, CmsException { 2633 2634 CmsGroup replacementGroup = null; 2635 if (replacementId != null) { 2636 replacementGroup = readGroup(dbc, replacementId); 2637 } 2638 // get all child groups of the group 2639 List<CmsGroup> children = getChildren(dbc, group, false); 2640 // get all users in this group 2641 List<CmsUser> users = getUsersOfGroup(dbc, group.getName(), true, true, group.isRole()); 2642 // get online project 2643 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 2644 if (replacementGroup == null) { 2645 // remove users 2646 Iterator<CmsUser> itUsers = users.iterator(); 2647 while (itUsers.hasNext()) { 2648 CmsUser user = itUsers.next(); 2649 if (userInGroup(dbc, user.getName(), group.getName(), group.isRole())) { 2650 removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole()); 2651 } 2652 } 2653 // transfer children to grandfather if possible 2654 CmsUUID parentId = group.getParentId(); 2655 if (parentId == null) { 2656 parentId = CmsUUID.getNullUUID(); 2657 } 2658 Iterator<CmsGroup> itChildren = children.iterator(); 2659 while (itChildren.hasNext()) { 2660 CmsGroup child = itChildren.next(); 2661 child.setParentId(parentId); 2662 writeGroup(dbc, child); 2663 } 2664 } else { 2665 // move children 2666 Iterator<CmsGroup> itChildren = children.iterator(); 2667 while (itChildren.hasNext()) { 2668 CmsGroup child = itChildren.next(); 2669 child.setParentId(replacementId); 2670 writeGroup(dbc, child); 2671 } 2672 // move users 2673 Iterator<CmsUser> itUsers = users.iterator(); 2674 while (itUsers.hasNext()) { 2675 CmsUser user = itUsers.next(); 2676 addUserToGroup(dbc, user.getName(), replacementGroup.getName(), group.isRole()); 2677 removeUserFromGroup(dbc, user.getName(), group.getName(), group.isRole()); 2678 } 2679 // transfer for offline 2680 transferPrincipalResources(dbc, dbc.currentProject(), group.getId(), replacementId, true); 2681 // transfer for online 2682 transferPrincipalResources(dbc, onlineProject, group.getId(), replacementId, true); 2683 } 2684 // remove the group 2685 getUserDriver( 2686 dbc).removeAccessControlEntriesForPrincipal(dbc, dbc.currentProject(), onlineProject, group.getId()); 2687 getUserDriver(dbc).deleteGroup(dbc, group.getName()); 2688 // backup the group 2689 getHistoryDriver(dbc).writePrincipal(dbc, group); 2690 if (OpenCms.getSubscriptionManager().isEnabled()) { 2691 // delete all subscribed resources for group 2692 unsubscribeAllResourcesFor(dbc, OpenCms.getSubscriptionManager().getPoolName(), group); 2693 } 2694 2695 // clear the relevant caches 2696 m_monitor.uncacheGroup(group); 2697 m_monitor.flushCache( 2698 CmsMemoryMonitor.CacheType.USERGROUPS, 2699 CmsMemoryMonitor.CacheType.USER_LIST, 2700 CmsMemoryMonitor.CacheType.ACL); 2701 2702 if (!dbc.getProjectId().isNullUUID()) { 2703 // group modified event is not needed 2704 return; 2705 } 2706 // fire group modified event 2707 Map<String, Object> eventData = new HashMap<String, Object>(); 2708 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 2709 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 2710 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_DELETE); 2711 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 2712 } 2713 2714 /** 2715 * Deletes the versions from the history tables, keeping the given number of versions per resource.<p> 2716 * 2717 * if the <code>cleanUp</code> option is set, additionally versions of deleted resources will be removed.<p> 2718 * 2719 * @param dbc the current database context 2720 * @param versionsToKeep number of versions to keep, is ignored if negative 2721 * @param versionsDeleted number of versions to keep for deleted resources, is ignored if negative 2722 * @param timeDeleted deleted resources older than this will also be deleted, is ignored if negative 2723 * @param clearDeletedFilter filter to evaluate whether the deleted resources should be cleared 2724 * @param report the report for output logging 2725 * 2726 * @throws CmsException if operation was not successful 2727 */ 2728 public void deleteHistoricalVersions( 2729 CmsDbContext dbc, 2730 int versionsToKeep, 2731 int versionsDeleted, 2732 long timeDeleted, 2733 Predicate<I_CmsHistoryResource> clearDeletedFilter, 2734 I_CmsReport report) 2735 throws CmsException { 2736 2737 if (clearDeletedFilter == null) { 2738 clearDeletedFilter = res -> true; 2739 } 2740 2741 report.println(Messages.get().container(Messages.RPT_START_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE); 2742 if (versionsToKeep >= 0) { 2743 report.println( 2744 Messages.get().container(Messages.RPT_START_DELETE_ACT_VERSIONS_1, Integer.valueOf(versionsToKeep)), 2745 I_CmsReport.FORMAT_HEADLINE); 2746 2747 List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllNotDeletedEntries(dbc); 2748 if (resources.isEmpty()) { 2749 report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK); 2750 } 2751 int n = resources.size(); 2752 int m = 1; 2753 Iterator<I_CmsHistoryResource> itResources = resources.iterator(); 2754 while (itResources.hasNext()) { 2755 I_CmsHistoryResource histResource = itResources.next(); 2756 2757 report.print( 2758 org.opencms.report.Messages.get().container( 2759 org.opencms.report.Messages.RPT_SUCCESSION_2, 2760 String.valueOf(m), 2761 String.valueOf(n)), 2762 I_CmsReport.FORMAT_NOTE); 2763 report.print( 2764 org.opencms.report.Messages.get().container( 2765 org.opencms.report.Messages.RPT_ARGUMENT_1, 2766 dbc.removeSiteRoot(histResource.getRootPath()))); 2767 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2768 2769 try { 2770 int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsToKeep, -1); 2771 2772 report.print( 2773 Messages.get().container(Messages.RPT_VERSION_DELETING_1, Integer.valueOf(deleted)), 2774 I_CmsReport.FORMAT_NOTE); 2775 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2776 report.println( 2777 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 2778 I_CmsReport.FORMAT_OK); 2779 } catch (CmsDataAccessException e) { 2780 report.println( 2781 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0), 2782 I_CmsReport.FORMAT_ERROR); 2783 2784 if (LOG.isDebugEnabled()) { 2785 LOG.debug(e.getLocalizedMessage(), e); 2786 } 2787 } 2788 2789 m++; 2790 } 2791 2792 report.println( 2793 Messages.get().container(Messages.RPT_END_DELETE_ACT_VERSIONS_0), 2794 I_CmsReport.FORMAT_HEADLINE); 2795 } 2796 if ((versionsDeleted >= 0) || (timeDeleted >= 0)) { 2797 if (timeDeleted >= 0) { 2798 report.println( 2799 Messages.get().container( 2800 Messages.RPT_START_DELETE_DEL_VERSIONS_2, 2801 Integer.valueOf(versionsDeleted), 2802 new Date(timeDeleted)), 2803 I_CmsReport.FORMAT_HEADLINE); 2804 } else { 2805 report.println( 2806 Messages.get().container( 2807 Messages.RPT_START_DELETE_DEL_VERSIONS_1, 2808 Integer.valueOf(versionsDeleted)), 2809 I_CmsReport.FORMAT_HEADLINE); 2810 } 2811 List<I_CmsHistoryResource> resources = getHistoryDriver(dbc).getAllDeletedEntries(dbc); 2812 resources = resources.stream().filter(clearDeletedFilter).collect(Collectors.toList()); 2813 if (resources.isEmpty()) { 2814 report.println(Messages.get().container(Messages.RPT_DELETE_NOTHING_0), I_CmsReport.FORMAT_OK); 2815 } 2816 int n = resources.size(); 2817 int m = 1; 2818 Iterator<I_CmsHistoryResource> itResources = resources.iterator(); 2819 while (itResources.hasNext()) { 2820 I_CmsHistoryResource histResource = itResources.next(); 2821 2822 report.print( 2823 org.opencms.report.Messages.get().container( 2824 org.opencms.report.Messages.RPT_SUCCESSION_2, 2825 String.valueOf(m), 2826 String.valueOf(n)), 2827 I_CmsReport.FORMAT_NOTE); 2828 report.print( 2829 org.opencms.report.Messages.get().container( 2830 org.opencms.report.Messages.RPT_ARGUMENT_1, 2831 dbc.removeSiteRoot(histResource.getRootPath()))); 2832 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2833 2834 try { 2835 int deleted = getHistoryDriver(dbc).deleteEntries(dbc, histResource, versionsDeleted, timeDeleted); 2836 2837 report.print( 2838 Messages.get().container(Messages.RPT_VERSION_DELETING_1, Integer.valueOf(deleted)), 2839 I_CmsReport.FORMAT_NOTE); 2840 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 2841 report.println( 2842 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 2843 I_CmsReport.FORMAT_OK); 2844 } catch (CmsDataAccessException e) { 2845 report.println( 2846 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ERROR_0), 2847 I_CmsReport.FORMAT_ERROR); 2848 2849 if (LOG.isDebugEnabled()) { 2850 LOG.debug(e.getLocalizedMessage(), e); 2851 } 2852 } 2853 2854 m++; 2855 } 2856 report.println( 2857 Messages.get().container(Messages.RPT_END_DELETE_DEL_VERSIONS_0), 2858 I_CmsReport.FORMAT_HEADLINE); 2859 } 2860 report.println(Messages.get().container(Messages.RPT_END_DELETE_VERSIONS_0), I_CmsReport.FORMAT_HEADLINE); 2861 } 2862 2863 /** 2864 * Deletes all log entries matching the given filter.<p> 2865 * 2866 * @param dbc the current db context 2867 * @param filter the filter to use for deletion 2868 * 2869 * @throws CmsException if something goes wrong 2870 * 2871 * @see CmsSecurityManager#deleteLogEntries(CmsRequestContext, CmsLogFilter) 2872 */ 2873 public void deleteLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException { 2874 2875 updateLog(dbc); 2876 m_projectDriver.deleteLog(dbc, filter); 2877 } 2878 2879 /** 2880 * Deletes an organizational unit.<p> 2881 * 2882 * Only organizational units that contain no suborganizational unit can be deleted.<p> 2883 * 2884 * The organizational unit can not be delete if it is used in the request context, 2885 * or if the current user belongs to it.<p> 2886 * 2887 * All users and groups in the given organizational unit will be deleted.<p> 2888 * 2889 * @param dbc the current db context 2890 * @param organizationalUnit the organizational unit to delete 2891 * 2892 * @throws CmsException if operation was not successful 2893 * 2894 * @see org.opencms.security.CmsOrgUnitManager#deleteOrganizationalUnit(CmsObject, String) 2895 */ 2896 public void deleteOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) 2897 throws CmsException { 2898 2899 // check organizational unit in context 2900 if (dbc.getRequestContext().getOuFqn().equals(organizationalUnit.getName())) { 2901 throw new CmsDbConsistencyException( 2902 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_IN_CONTEXT_1, organizationalUnit.getName())); 2903 } 2904 // check organizational unit for user 2905 if (dbc.currentUser().getOuFqn().equals(organizationalUnit.getName())) { 2906 throw new CmsDbConsistencyException( 2907 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_CURRENT_USER_1, organizationalUnit.getName())); 2908 } 2909 // check sub organizational units 2910 if (!getOrganizationalUnits(dbc, organizationalUnit, true).isEmpty()) { 2911 throw new CmsDbConsistencyException( 2912 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_SUB_ORGUNITS_1, organizationalUnit.getName())); 2913 } 2914 // check groups 2915 List<CmsGroup> groups = getGroups(dbc, organizationalUnit, true, false); 2916 Iterator<CmsGroup> itGroups = groups.iterator(); 2917 while (itGroups.hasNext()) { 2918 CmsGroup group = itGroups.next(); 2919 if (!OpenCms.getDefaultUsers().isDefaultGroup(group.getName())) { 2920 throw new CmsDbConsistencyException( 2921 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_GROUPS_1, organizationalUnit.getName())); 2922 } 2923 } 2924 // check users 2925 if (!getUsers(dbc, organizationalUnit, true).isEmpty()) { 2926 throw new CmsDbConsistencyException( 2927 Messages.get().container(Messages.ERR_ORGUNIT_DELETE_USERS_1, organizationalUnit.getName())); 2928 } 2929 2930 // delete default groups if needed 2931 itGroups = groups.iterator(); 2932 while (itGroups.hasNext()) { 2933 CmsGroup group = itGroups.next(); 2934 deleteGroup(dbc, group, null); 2935 } 2936 2937 // delete projects 2938 Iterator<CmsProject> itProjects = getProjectDriver(dbc).readProjects( 2939 dbc, 2940 organizationalUnit.getName()).iterator(); 2941 while (itProjects.hasNext()) { 2942 CmsProject project = itProjects.next(); 2943 deleteProject(dbc, project, false); 2944 } 2945 2946 // delete roles 2947 Iterator<CmsGroup> itRoles = getGroups(dbc, organizationalUnit, true, true).iterator(); 2948 while (itRoles.hasNext()) { 2949 CmsGroup role = itRoles.next(); 2950 deleteGroup(dbc, role, null); 2951 } 2952 2953 // create a publish list for the 'virtual' publish event 2954 CmsResource resource = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT); 2955 CmsPublishList pl = new CmsPublishList(resource, false); 2956 pl.add(resource, false); 2957 2958 // remove the organizational unit itself 2959 getUserDriver(dbc).deleteOrganizationalUnit(dbc, organizationalUnit); 2960 2961 // write the publish history entry 2962 getProjectDriver(dbc).writePublishHistory( 2963 dbc, 2964 pl.getPublishHistoryId(), 2965 new CmsPublishedResource(resource, -1, CmsResourceState.STATE_DELETED)); 2966 2967 // flush relevant caches 2968 m_monitor.clearPrincipalsCache(); 2969 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 2970 2971 // fire the 'virtual' publish event 2972 Map<String, Object> eventData = new HashMap<String, Object>(); 2973 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 2974 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 2975 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 2976 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 2977 OpenCms.fireCmsEvent(afterPublishEvent); 2978 2979 m_lockManager.removeDeletedResource(dbc, resource.getRootPath()); 2980 2981 if (!dbc.getProjectId().isNullUUID()) { 2982 // OU modified event is not needed 2983 return; 2984 } 2985 // fire OU modified event 2986 Map<String, Object> event2Data = new HashMap<String, Object>(); 2987 event2Data.put(I_CmsEventListener.KEY_OU_NAME, organizationalUnit.getName()); 2988 event2Data.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_OU_MODIFIED_ACTION_DELETE); 2989 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_OU_MODIFIED, event2Data)); 2990 2991 } 2992 2993 /** 2994 * Deletes a project.<p> 2995 * 2996 * Only the admin or the owner of the project can do this. 2997 * 2998 * @param dbc the current database context 2999 * @param deleteProject the project to be deleted 3000 * 3001 * @throws CmsException if something goes wrong 3002 */ 3003 public void deleteProject(CmsDbContext dbc, CmsProject deleteProject) throws CmsException { 3004 3005 deleteProject(dbc, deleteProject, true); 3006 } 3007 3008 /** 3009 * Deletes a project.<p> 3010 * 3011 * Only the admin or the owner of the project can do this. 3012 * 3013 * @param dbc the current database context 3014 * @param deleteProject the project to be deleted 3015 * @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 3016 * 3017 * @throws CmsException if something goes wrong 3018 */ 3019 public void deleteProject(CmsDbContext dbc, CmsProject deleteProject, boolean resetResources) throws CmsException { 3020 3021 CmsUUID projectId = deleteProject.getUuid(); 3022 3023 if (resetResources) { 3024 // changed/new/deleted files in the specified project 3025 List<CmsResource> modifiedFiles = readChangedResourcesInsideProject(dbc, projectId, RCPRM_FILES_ONLY_MODE); 3026 // changed/new/deleted folders in the specified project 3027 List<CmsResource> modifiedFolders = readChangedResourcesInsideProject( 3028 dbc, 3029 projectId, 3030 RCPRM_FOLDERS_ONLY_MODE); 3031 resetResourcesInProject(dbc, projectId, modifiedFiles, modifiedFolders); 3032 } 3033 3034 // unlock all resources in the project 3035 m_lockManager.removeResourcesInProject(deleteProject.getUuid(), true); 3036 m_monitor.clearAccessControlListCache(); 3037 m_monitor.clearResourceCache(); 3038 3039 // set project to online project if current project is the one which will be deleted 3040 if (projectId.equals(dbc.currentProject().getUuid())) { 3041 dbc.getRequestContext().setCurrentProject(readProject(dbc, CmsProject.ONLINE_PROJECT_ID)); 3042 } 3043 3044 // delete the project itself 3045 getProjectDriver(dbc).deleteProject(dbc, deleteProject); 3046 m_monitor.uncacheProject(deleteProject); 3047 3048 // fire the corresponding event 3049 OpenCms.fireCmsEvent( 3050 new CmsEvent( 3051 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 3052 Collections.<String, Object> singletonMap("project", deleteProject))); 3053 3054 } 3055 3056 /** 3057 * Deletes a property definition.<p> 3058 * 3059 * @param dbc the current database context 3060 * @param name the name of the property definition to delete 3061 * 3062 * @throws CmsException if something goes wrong 3063 */ 3064 public void deletePropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 3065 3066 CmsPropertyDefinition propertyDefinition = null; 3067 3068 try { 3069 // first read and then delete the metadefinition. 3070 propertyDefinition = readPropertyDefinition(dbc, name); 3071 getVfsDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition); 3072 getHistoryDriver(dbc).deletePropertyDefinition(dbc, propertyDefinition); 3073 } finally { 3074 3075 // fire an event that a property of a resource has been deleted 3076 OpenCms.fireCmsEvent( 3077 new CmsEvent( 3078 I_CmsEventListener.EVENT_PROPERTY_DEFINITION_MODIFIED, 3079 Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition))); 3080 } 3081 } 3082 3083 /** 3084 * Deletes a publish job identified by its history id.<p> 3085 * 3086 * @param dbc the current database context 3087 * @param publishHistoryId the history id identifying the publish job 3088 * 3089 * @throws CmsException if something goes wrong 3090 */ 3091 public void deletePublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 3092 3093 getProjectDriver(dbc).deletePublishJob(dbc, publishHistoryId); 3094 } 3095 3096 /** 3097 * Deletes the publish list assigned to a publish job.<p> 3098 * 3099 * @param dbc the current database context 3100 * @param publishHistoryId the history id identifying the publish job 3101 * @throws CmsException if something goes wrong 3102 */ 3103 public void deletePublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 3104 3105 getProjectDriver(dbc).deletePublishList(dbc, publishHistoryId); 3106 } 3107 3108 /** 3109 * Deletes all relations for the given resource matching the given filter.<p> 3110 * 3111 * @param dbc the current db context 3112 * @param resource the resource to delete the relations for 3113 * @param filter the filter to use for deletion 3114 * 3115 * @throws CmsException if something goes wrong 3116 * 3117 * @see CmsSecurityManager#deleteRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter) 3118 */ 3119 public void deleteRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter) 3120 throws CmsException { 3121 3122 if (filter.includesDefinedInContent()) { 3123 throw new CmsIllegalArgumentException( 3124 Messages.get().container( 3125 Messages.ERR_DELETE_RELATION_IN_CONTENT_2, 3126 dbc.removeSiteRoot(resource.getRootPath()), 3127 filter.getTypes())); 3128 } 3129 getVfsDriver(dbc).deleteRelations(dbc, dbc.currentProject().getUuid(), resource, filter); 3130 setDateLastModified(dbc, resource, System.currentTimeMillis()); 3131 log( 3132 dbc, 3133 new CmsLogEntry( 3134 dbc, 3135 resource.getStructureId(), 3136 CmsLogEntryType.RESOURCE_REMOVE_RELATION, 3137 new String[] {resource.getRootPath(), filter.toString()}), 3138 false); 3139 } 3140 3141 /** 3142 * Deletes a resource.<p> 3143 * 3144 * The <code>siblingMode</code> parameter controls how to handle siblings 3145 * during the delete operation. 3146 * Possible values for this parameter are: 3147 * <ul> 3148 * <li><code>{@link CmsResource#DELETE_REMOVE_SIBLINGS}</code></li> 3149 * <li><code>{@link CmsResource#DELETE_PRESERVE_SIBLINGS}</code></li> 3150 * </ul><p> 3151 * 3152 * @param dbc the current database context 3153 * @param resource the name of the resource to delete (full path) 3154 * @param siblingMode indicates how to handle siblings of the deleted resource 3155 * 3156 * @throws CmsException if something goes wrong 3157 * 3158 * @see CmsObject#deleteResource(String, CmsResource.CmsResourceDeleteMode) 3159 * @see I_CmsResourceType#deleteResource(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode) 3160 */ 3161 public void deleteResource(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceDeleteMode siblingMode) 3162 throws CmsException { 3163 3164 // upgrade a potential inherited, non-shared lock into a common lock 3165 CmsLock currentLock = getLock(dbc, resource); 3166 if (currentLock.getEditionLock().isDirectlyInherited()) { 3167 // upgrade the lock status if required 3168 lockResource(dbc, resource, CmsLockType.EXCLUSIVE); 3169 } 3170 3171 // check if siblings of the resource exist and must be deleted as well 3172 if (resource.isFolder()) { 3173 // folder can have no siblings 3174 siblingMode = CmsResource.DELETE_PRESERVE_SIBLINGS; 3175 } 3176 3177 // if selected, add all siblings of this resource to the list of resources to be deleted 3178 boolean allSiblingsRemoved; 3179 List<CmsResource> resources; 3180 if (siblingMode == CmsResource.DELETE_REMOVE_SIBLINGS) { 3181 resources = new ArrayList<CmsResource>(readSiblings(dbc, resource, CmsResourceFilter.ALL)); 3182 allSiblingsRemoved = true; 3183 3184 // ensure that the resource requested to be deleted is the last resource that gets actually deleted 3185 // to keep the shared locks of the siblings while those get deleted. 3186 resources.remove(resource); 3187 resources.add(resource); 3188 } else { 3189 // only delete the resource, no siblings 3190 resources = Collections.singletonList(resource); 3191 allSiblingsRemoved = false; 3192 } 3193 3194 int size = resources.size(); 3195 // if we have only one resource no further check is required 3196 if (size > 1) { 3197 CmsMultiException me = new CmsMultiException(); 3198 // ensure that each sibling is unlocked or locked by the current user 3199 for (int i = 0; i < size; i++) { 3200 CmsResource currentResource = resources.get(i); 3201 currentLock = getLock(dbc, currentResource); 3202 if (!currentLock.getEditionLock().isUnlocked() && !currentLock.isOwnedBy(dbc.currentUser())) { 3203 // the resource is locked by a user different from the current user 3204 CmsRequestContext context = dbc.getRequestContext(); 3205 me.addException( 3206 new CmsLockException( 3207 org.opencms.lock.Messages.get().container( 3208 org.opencms.lock.Messages.ERR_SIBLING_LOCKED_2, 3209 context.getSitePath(currentResource), 3210 context.getSitePath(resource)))); 3211 } 3212 } 3213 if (!me.getExceptions().isEmpty()) { 3214 throw me; 3215 } 3216 } 3217 3218 boolean removeAce = true; 3219 3220 if (resource.isFolder()) { 3221 // check if the folder has any resources in it 3222 Iterator<CmsResource> childResources = getVfsDriver( 3223 dbc).readChildResources(dbc, dbc.currentProject(), resource, true, true).iterator(); 3224 3225 CmsUUID projectId = CmsProject.ONLINE_PROJECT_ID; 3226 if (dbc.currentProject().isOnlineProject()) { 3227 projectId = CmsUUID.getOpenCmsUUID(); // HACK: to get an offline project id 3228 } 3229 3230 // collect the names of the resources inside the folder, excluding the moved resources 3231 StringBuffer errorResNames = new StringBuffer(128); 3232 while (childResources.hasNext()) { 3233 CmsResource errorRes = childResources.next(); 3234 if (errorRes.getState().isDeleted()) { 3235 continue; 3236 } 3237 // if deleting offline, or not moved, or just renamed inside the deleted folder 3238 // so, it may remain some orphan online entries for moved resources 3239 // which will be fixed during the publishing of the moved resources 3240 boolean error = !dbc.currentProject().isOnlineProject(); 3241 if (!error) { 3242 try { 3243 String originalPath = getVfsDriver( 3244 dbc).readResource(dbc, projectId, errorRes.getRootPath(), true).getRootPath(); 3245 error = originalPath.equals(errorRes.getRootPath()) 3246 || originalPath.startsWith(resource.getRootPath()); 3247 } catch (CmsVfsResourceNotFoundException e) { 3248 // ignore 3249 } 3250 } 3251 if (error) { 3252 if (errorResNames.length() != 0) { 3253 errorResNames.append(", "); 3254 } 3255 errorResNames.append("[" + dbc.removeSiteRoot(errorRes.getRootPath()) + "]"); 3256 } 3257 } 3258 3259 // the current implementation only deletes empty folders 3260 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(errorResNames.toString())) { 3261 throw new CmsVfsException( 3262 org.opencms.db.generic.Messages.get().container( 3263 org.opencms.db.generic.Messages.ERR_DELETE_NONEMTY_FOLDER_2, 3264 dbc.removeSiteRoot(resource.getRootPath()), 3265 errorResNames.toString())); 3266 } 3267 } 3268 3269 // delete all collected resources 3270 for (int i = 0; i < size; i++) { 3271 CmsResource currentResource = resources.get(i); 3272 3273 // try to delete/remove the resource only if the user has write access to the resource 3274 // check permissions only for the sibling, the resource it self was already checked or 3275 // is to be removed without write permissions, ie. while deleting a folder 3276 if (!currentResource.equals(resource) 3277 && (I_CmsPermissionHandler.PERM_ALLOWED != m_securityManager.hasPermissions( 3278 dbc, 3279 currentResource, 3280 CmsPermissionSet.ACCESS_WRITE, 3281 LockCheck.yes, 3282 CmsResourceFilter.ALL))) { 3283 3284 // no write access to sibling - must keep ACE (see below) 3285 allSiblingsRemoved = false; 3286 } else { 3287 // write access to sibling granted 3288 boolean existsOnline = (getVfsDriver(dbc).validateStructureIdExists( 3289 dbc, 3290 CmsProject.ONLINE_PROJECT_ID, 3291 currentResource.getStructureId()) || !(currentResource.getState().equals(CmsResource.STATE_NEW))); 3292 if (!existsOnline) { 3293 // the resource does not exist online => remove the resource 3294 // this means the resource is "new" (blue) in the offline project 3295 3296 // delete all properties of this resource 3297 deleteAllProperties(dbc, currentResource.getRootPath()); 3298 3299 if (currentResource.isFolder()) { 3300 getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentResource); 3301 } else { 3302 // check labels 3303 if (currentResource.isLabeled() && !labelResource(dbc, currentResource, null, 2)) { 3304 // update the resource flags to "un label" the other siblings 3305 int flags = currentResource.getFlags(); 3306 flags &= ~CmsResource.FLAG_LABELED; 3307 currentResource.setFlags(flags); 3308 } 3309 getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentResource); 3310 } 3311 3312 // ensure an exclusive lock is removed in the lock manager for a deleted new resource, 3313 // otherwise it would "stick" in the lock manager, preventing other users from creating 3314 // a file with the same name (issue with temp files in editor) 3315 m_lockManager.removeDeletedResource(dbc, currentResource.getRootPath()); 3316 // delete relations 3317 getVfsDriver(dbc).deleteRelations( 3318 dbc, 3319 dbc.currentProject().getUuid(), 3320 currentResource, 3321 CmsRelationFilter.TARGETS); 3322 getVfsDriver(dbc).deleteUrlNameMappingEntries( 3323 dbc, 3324 false, 3325 CmsUrlNameMappingFilter.ALL.filterStructureId(currentResource.getStructureId())); 3326 getVfsDriver(dbc).deleteAliases( 3327 dbc, 3328 dbc.currentProject(), 3329 new CmsAliasFilter(null, null, currentResource.getStructureId())); 3330 log( 3331 dbc, 3332 new CmsLogEntry( 3333 dbc, 3334 currentResource.getStructureId(), 3335 CmsLogEntryType.RESOURCE_NEW_DELETED, 3336 new String[] {currentResource.getRootPath()}), 3337 true); 3338 } else { 3339 // the resource exists online => mark the resource as deleted 3340 // structure record is removed during next publish 3341 // if one (or more) siblings are not removed, the ACE can not be removed 3342 removeAce = false; 3343 // set resource state to deleted 3344 currentResource.setState(CmsResource.STATE_DELETED); 3345 getVfsDriver( 3346 dbc).writeResourceState(dbc, dbc.currentProject(), currentResource, UPDATE_STRUCTURE, false); 3347 3348 // update the project ID 3349 getVfsDriver(dbc).writeLastModifiedProjectId( 3350 dbc, 3351 dbc.currentProject(), 3352 dbc.currentProject().getUuid(), 3353 currentResource); 3354 // log it 3355 3356 log( 3357 dbc, 3358 new CmsLogEntry( 3359 dbc, 3360 currentResource.getStructureId(), 3361 CmsLogEntryType.RESOURCE_DELETED, 3362 new String[] {currentResource.getRootPath()}), 3363 true); 3364 } 3365 } 3366 } 3367 3368 if ((resource.getSiblingCount() <= 1) || allSiblingsRemoved) { 3369 if (removeAce) { 3370 // remove the access control entries 3371 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId()); 3372 } 3373 } 3374 3375 // flush all caches 3376 m_monitor.clearAccessControlListCache(); 3377 m_monitor.flushCache( 3378 CmsMemoryMonitor.CacheType.PROPERTY, 3379 CmsMemoryMonitor.CacheType.PROPERTY_LIST, 3380 CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 3381 3382 Map<String, Object> eventData = new HashMap<String, Object>(); 3383 eventData.put(I_CmsEventListener.KEY_RESOURCES, resources); 3384 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 3385 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_DELETED, eventData)); 3386 } 3387 3388 /** 3389 * Deletes an entry in the published resource table.<p> 3390 * 3391 * @param dbc the current database context 3392 * @param resourceName The name of the resource to be deleted in the static export 3393 * @param linkType the type of resource deleted (0= non-parameter, 1=parameter) 3394 * @param linkParameter the parameters of the resource 3395 * 3396 * @throws CmsException if something goes wrong 3397 */ 3398 public void deleteStaticExportPublishedResource( 3399 CmsDbContext dbc, 3400 String resourceName, 3401 int linkType, 3402 String linkParameter) 3403 throws CmsException { 3404 3405 getProjectDriver(dbc).deleteStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter); 3406 } 3407 3408 /** 3409 * Deletes a user, where all permissions and resources attributes of the user 3410 * were transfered to a replacement user, if given.<p> 3411 * 3412 * Only users, which are in the group "administrators" are granted.<p> 3413 * 3414 * @param dbc the current database context 3415 * @param project the current project 3416 * @param username the name of the user to be deleted 3417 * @param replacementUsername the name of the user to be transfered, can be <code>null</code> 3418 * 3419 * @throws CmsException if operation was not successful 3420 */ 3421 public void deleteUser(CmsDbContext dbc, CmsProject project, String username, String replacementUsername) 3422 throws CmsException { 3423 3424 // Test if the users exists 3425 CmsUser user = readUser(dbc, username); 3426 CmsUser replacementUser = null; 3427 if (replacementUsername != null) { 3428 replacementUser = readUser(dbc, replacementUsername); 3429 } 3430 3431 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 3432 boolean withACEs = true; 3433 if (replacementUser == null) { 3434 withACEs = false; 3435 replacementUser = readUser(dbc, OpenCms.getDefaultUsers().getUserDeletedResource()); 3436 } 3437 3438 boolean isVfsManager = m_securityManager.hasRole(dbc, replacementUser, CmsRole.VFS_MANAGER); 3439 3440 // iterate groups and roles 3441 for (int i = 0; i < 2; i++) { 3442 boolean readRoles = i != 0; 3443 Iterator<CmsGroup> itGroups = getGroupsOfUser( 3444 dbc, 3445 username, 3446 "", 3447 true, 3448 readRoles, 3449 true, 3450 dbc.getRequestContext().getRemoteAddress()).iterator(); 3451 while (itGroups.hasNext()) { 3452 CmsGroup group = itGroups.next(); 3453 if (!isVfsManager) { 3454 // add replacement user to user groups 3455 if (!userInGroup(dbc, replacementUser.getName(), group.getName(), readRoles)) { 3456 addUserToGroup(dbc, replacementUser.getName(), group.getName(), readRoles); 3457 } 3458 } 3459 // remove user from groups 3460 if (userInGroup(dbc, username, group.getName(), readRoles)) { 3461 // we need this additional check because removing a user from a group 3462 // may also automatically remove him from other groups if the group was 3463 // associated with a role. 3464 removeUserFromGroup(dbc, username, group.getName(), readRoles); 3465 } 3466 } 3467 } 3468 // remove all locks set for the deleted user 3469 m_lockManager.removeLocks(user.getId()); 3470 // offline 3471 if (dbc.getProjectId().isNullUUID()) { 3472 // offline project available 3473 transferPrincipalResources(dbc, project, user.getId(), replacementUser.getId(), withACEs); 3474 } 3475 // online 3476 transferPrincipalResources(dbc, onlineProject, user.getId(), replacementUser.getId(), withACEs); 3477 getUserDriver(dbc).removeAccessControlEntriesForPrincipal(dbc, project, onlineProject, user.getId()); 3478 getHistoryDriver(dbc).writePrincipal(dbc, user); 3479 getUserDriver(dbc).deleteUser(dbc, username); 3480 // delete user from cache 3481 m_monitor.clearUserCache(user); 3482 3483 if (!dbc.getProjectId().isNullUUID()) { 3484 // user modified event is not needed 3485 return; 3486 } 3487 // fire user modified event 3488 Map<String, Object> eventData = new HashMap<String, Object>(); 3489 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 3490 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 3491 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_DELETE_USER); 3492 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 3493 } 3494 3495 /** 3496 * Destroys this driver manager and releases all allocated resources.<p> 3497 */ 3498 public void destroy() { 3499 3500 try { 3501 if (m_projectDriver != null) { 3502 try { 3503 m_projectDriver.destroy(); 3504 } catch (Throwable t) { 3505 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_PROJECT_DRIVER_0), t); 3506 } 3507 m_projectDriver = null; 3508 } 3509 if (m_userDriver != null) { 3510 try { 3511 m_userDriver.destroy(); 3512 } catch (Throwable t) { 3513 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_USER_DRIVER_0), t); 3514 } 3515 m_userDriver = null; 3516 } 3517 if (m_vfsDriver != null) { 3518 try { 3519 m_vfsDriver.destroy(); 3520 } catch (Throwable t) { 3521 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_VFS_DRIVER_0), t); 3522 } 3523 m_vfsDriver = null; 3524 } 3525 if (m_historyDriver != null) { 3526 try { 3527 m_historyDriver.destroy(); 3528 } catch (Throwable t) { 3529 LOG.error(Messages.get().getBundle().key(Messages.ERR_CLOSE_HISTORY_DRIVER_0), t); 3530 } 3531 m_historyDriver = null; 3532 } 3533 3534 if (m_pools != null) { 3535 for (CmsDbPoolV11 pool : m_pools.values()) { 3536 try { 3537 pool.close(); 3538 if (CmsLog.INIT.isDebugEnabled()) { 3539 CmsLog.INIT.debug(Messages.get().getBundle().key(Messages.INIT_CLOSE_CONN_POOL_1, pool)); 3540 } 3541 3542 } catch (Throwable t) { 3543 LOG.error(Messages.get().getBundle().key(Messages.LOG_CLOSE_CONN_POOL_ERROR_1, pool), t); 3544 } 3545 } 3546 m_pools.clear(); 3547 } 3548 3549 m_monitor.clearCache(); 3550 3551 m_lockManager = null; 3552 m_htmlLinkValidator = null; 3553 } catch (Throwable t) { 3554 // ignore 3555 } 3556 if (CmsLog.INIT.isInfoEnabled()) { 3557 CmsLog.INIT.info( 3558 Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_DESTROY_1, getClass().getName())); 3559 } 3560 } 3561 3562 /** 3563 * Tests if a resource with the given resourceId does already exist in the Database.<p> 3564 * 3565 * @param dbc the current database context 3566 * @param resourceId the resource id to test for 3567 * @return true if a resource with the given id was found, false otherweise 3568 * @throws CmsException if something goes wrong 3569 */ 3570 public boolean existsResourceId(CmsDbContext dbc, CmsUUID resourceId) throws CmsException { 3571 3572 return getVfsDriver(dbc).validateResourceIdExists(dbc, dbc.currentProject().getUuid(), resourceId); 3573 } 3574 3575 /** 3576 * Fills the given publish list with the the VFS resources that actually get published.<p> 3577 * 3578 * Please refer to the source code of this method for the rules on how to decide whether a 3579 * new/changed/deleted <code>{@link CmsResource}</code> object can be published or not.<p> 3580 * 3581 * @param dbc the current database context 3582 * @param publishList must be initialized with basic publish information (Project or direct publish operation), 3583 * the given publish list will be filled with all new/changed/deleted files from the current 3584 * (offline) project that will be actually published 3585 * 3586 * @throws CmsException if something goes wrong 3587 * 3588 * @see org.opencms.db.CmsPublishList 3589 */ 3590 public void fillPublishList(CmsDbContext dbc, CmsPublishList publishList) throws CmsException { 3591 3592 if (!publishList.isDirectPublish()) { 3593 // when publishing a project 3594 // all modified resources with the last change done in the current project are candidates if unlocked 3595 List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree( 3596 dbc, 3597 dbc.currentProject().getUuid(), 3598 CmsDriverManager.READ_IGNORE_PARENT, 3599 CmsDriverManager.READ_IGNORE_TYPE, 3600 CmsResource.STATE_UNCHANGED, 3601 CmsDriverManager.READ_IGNORE_TIME, 3602 CmsDriverManager.READ_IGNORE_TIME, 3603 CmsDriverManager.READ_IGNORE_TIME, 3604 CmsDriverManager.READ_IGNORE_TIME, 3605 CmsDriverManager.READ_IGNORE_TIME, 3606 CmsDriverManager.READ_IGNORE_TIME, 3607 CmsDriverManager.READMODE_INCLUDE_TREE 3608 | CmsDriverManager.READMODE_INCLUDE_PROJECT 3609 | CmsDriverManager.READMODE_EXCLUDE_STATE 3610 | CmsDriverManager.READMODE_ONLY_FOLDERS); 3611 3612 List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree( 3613 dbc, 3614 dbc.currentProject().getUuid(), 3615 CmsDriverManager.READ_IGNORE_PARENT, 3616 CmsDriverManager.READ_IGNORE_TYPE, 3617 CmsResource.STATE_UNCHANGED, 3618 CmsDriverManager.READ_IGNORE_TIME, 3619 CmsDriverManager.READ_IGNORE_TIME, 3620 CmsDriverManager.READ_IGNORE_TIME, 3621 CmsDriverManager.READ_IGNORE_TIME, 3622 CmsDriverManager.READ_IGNORE_TIME, 3623 CmsDriverManager.READ_IGNORE_TIME, 3624 CmsDriverManager.READMODE_INCLUDE_TREE 3625 | CmsDriverManager.READMODE_INCLUDE_PROJECT 3626 | CmsDriverManager.READMODE_EXCLUDE_STATE 3627 | CmsDriverManager.READMODE_ONLY_FILES); 3628 CmsRequestContext context = dbc.getRequestContext(); 3629 if ((context != null) 3630 && (context.getAttribute(CmsDefaultWorkflowManager.ATTR_CHECK_PUBLISH_RESOURCE_LIMIT) != null)) { 3631 3632 // check if total size and if it exceeds the resource limit and the request 3633 // context attribute is set, throw an exception. 3634 // we do it here since filterResources() can be very expensive on large resource lists 3635 3636 int limit = OpenCms.getWorkflowManager().getResourceLimit(); 3637 int total = fileList.size() + folderList.size(); 3638 if (total > limit) { 3639 throw new CmsTooManyPublishResourcesException(total); 3640 } 3641 } 3642 publishList.addAll(filterResources(dbc, null, folderList), true); 3643 publishList.addAll(filterResources(dbc, publishList, fileList), true); 3644 } else { 3645 // this is a direct publish 3646 Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator(); 3647 while (it.hasNext()) { 3648 // iterate all resources in the direct publish list 3649 CmsResource directPublishResource = it.next(); 3650 if (directPublishResource.isFolder()) { 3651 // when publishing a folder directly, 3652 // the folder and all modified resources within the tree below this folder 3653 // and with the last change done in the current project are candidates if lockable 3654 CmsLock lock = getLock(dbc, directPublishResource); 3655 if (!directPublishResource.getState().isUnchanged() && lock.isLockableBy(dbc.currentUser())) { 3656 3657 try { 3658 m_securityManager.checkPermissions( 3659 dbc, 3660 directPublishResource, 3661 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 3662 false, 3663 CmsResourceFilter.ALL); 3664 publishList.add(directPublishResource, true); 3665 } catch (CmsException e) { 3666 // skip if not enough permissions 3667 } 3668 } 3669 boolean shouldPublishDeletedSubResources = publishList.isUserPublishList() 3670 && directPublishResource.getState().isDeleted(); 3671 if (publishList.isPublishSubResources() || shouldPublishDeletedSubResources) { 3672 addSubResources(dbc, publishList, directPublishResource, resource -> true); 3673 } 3674 } else if (directPublishResource.isFile() && !directPublishResource.getState().isUnchanged()) { 3675 3676 // when publishing a file directly this file is the only candidate 3677 // if it is modified and lockable 3678 CmsLock lock = getLock(dbc, directPublishResource); 3679 if (lock.isLockableBy(dbc.currentUser())) { 3680 // check permissions 3681 try { 3682 m_securityManager.checkPermissions( 3683 dbc, 3684 directPublishResource, 3685 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 3686 false, 3687 CmsResourceFilter.ALL); 3688 publishList.add(directPublishResource, true); 3689 } catch (CmsException e) { 3690 // skip if not enough permissions 3691 } 3692 } 3693 } 3694 } 3695 } 3696 3697 // Step 2: if desired, extend the list of files to publish with related siblings 3698 if (publishList.isPublishSiblings()) { 3699 List<CmsResource> publishFiles = publishList.getFileList(); 3700 int size = publishFiles.size(); 3701 3702 // Improved: first calculate closure of all siblings, then filter and add them 3703 Set<CmsResource> siblingsClosure = new HashSet<CmsResource>(publishFiles); 3704 for (int i = 0; i < size; i++) { 3705 CmsResource currentFile = publishFiles.get(i); 3706 if (currentFile.getSiblingCount() > 1) { 3707 siblingsClosure.addAll(readSiblings(dbc, currentFile, CmsResourceFilter.ALL_MODIFIED)); 3708 } 3709 } 3710 publishList.addAll(filterSiblings(dbc, publishList, siblingsClosure), true); 3711 } 3712 publishList.initialize(); 3713 } 3714 3715 /** 3716 * Returns the list of access control entries of a resource given its name.<p> 3717 * 3718 * @param dbc the current database context 3719 * @param resource the resource to read the access control entries for 3720 * @param getInherited true if the result should include all access control entries inherited by parent folders 3721 * 3722 * @return a list of <code>{@link CmsAccessControlEntry}</code> objects defining all permissions for the given resource 3723 * 3724 * @throws CmsException if something goes wrong 3725 */ 3726 public List<CmsAccessControlEntry> getAccessControlEntries( 3727 CmsDbContext dbc, 3728 CmsResource resource, 3729 boolean getInherited) 3730 throws CmsException { 3731 3732 // get the ACE of the resource itself 3733 I_CmsUserDriver userDriver = getUserDriver(dbc); 3734 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 3735 List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries( 3736 dbc, 3737 dbc.currentProject(), 3738 resource.getResourceId(), 3739 false); 3740 3741 // sort and check if we got the 'overwrite all' ace to stop looking up 3742 boolean overwriteAll = sortAceList(ace); 3743 3744 // get the ACE of each parent folder 3745 // Note: for the immediate parent, get non-inherited access control entries too, 3746 // if the resource is not a folder 3747 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 3748 int d = (resource.isFolder()) ? 1 : 0; 3749 3750 while (!overwriteAll && getInherited && (parentPath != null)) { 3751 resource = vfsDriver.readFolder(dbc, dbc.currentProject().getUuid(), parentPath); 3752 List<CmsAccessControlEntry> entries = userDriver.readAccessControlEntries( 3753 dbc, 3754 dbc.currentProject(), 3755 resource.getResourceId(), 3756 d > 0); 3757 3758 // sort and check if we got the 'overwrite all' ace to stop looking up 3759 overwriteAll = sortAceList(entries); 3760 3761 for (CmsAccessControlEntry e : entries) { 3762 e.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED); 3763 } 3764 3765 ace.addAll(entries); 3766 parentPath = CmsResource.getParentFolder(resource.getRootPath()); 3767 d++; 3768 } 3769 3770 return ace; 3771 } 3772 3773 /** 3774 * Returns the full access control list of a given resource.<p> 3775 * 3776 * @param dbc the current database context 3777 * @param resource the resource 3778 * 3779 * @return the access control list of the resource 3780 * 3781 * @throws CmsException if something goes wrong 3782 */ 3783 public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource) throws CmsException { 3784 3785 return getAccessControlList(dbc, resource, false); 3786 } 3787 3788 /** 3789 * Returns the access control list of a given resource.<p> 3790 * 3791 * If <code>inheritedOnly</code> is set, only inherited access control entries 3792 * are returned.<p> 3793 * 3794 * Note: For file resources, *all* permissions set at the immediate parent folder are inherited, 3795 * not only these marked to inherit. 3796 * 3797 * @param dbc the current database context 3798 * @param resource the resource 3799 * @param inheritedOnly skip non-inherited entries if set 3800 * 3801 * @return the access control list of the resource 3802 * 3803 * @throws CmsException if something goes wrong 3804 */ 3805 public CmsAccessControlList getAccessControlList(CmsDbContext dbc, CmsResource resource, boolean inheritedOnly) 3806 throws CmsException { 3807 3808 return getAccessControlList(dbc, resource, inheritedOnly, resource.isFolder(), 0); 3809 } 3810 3811 /** 3812 * Returns the number of active connections managed by a pool.<p> 3813 * 3814 * @param dbPoolUrl the url of a pool 3815 * @return the number of active connections 3816 * @throws CmsDbException if something goes wrong 3817 */ 3818 public int getActiveConnections(String dbPoolUrl) throws CmsDbException { 3819 3820 CmsDbPoolV11 pool = m_pools.get(dbPoolUrl); 3821 if (pool == null) { 3822 CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl); 3823 throw new CmsDbException(message); 3824 } 3825 try { 3826 return pool.getActiveConnections(); 3827 } catch (Exception exc) { 3828 CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl); 3829 throw new CmsDbException(message, exc); 3830 } 3831 3832 } 3833 3834 /** 3835 * Reads all access control entries.<p> 3836 * 3837 * @param dbc the current database context 3838 * @return all access control entries for the current project (offline/online) 3839 * 3840 * @throws CmsException if something goes wrong 3841 */ 3842 public List<CmsAccessControlEntry> getAllAccessControlEntries(CmsDbContext dbc) throws CmsException { 3843 3844 I_CmsUserDriver userDriver = getUserDriver(dbc); 3845 List<CmsAccessControlEntry> ace = userDriver.readAccessControlEntries( 3846 dbc, 3847 dbc.currentProject(), 3848 CmsAccessControlEntry.PRINCIPAL_READALL_ID, 3849 false); 3850 return ace; 3851 } 3852 3853 /** 3854 * Returns all projects which are owned by the current user or which are 3855 * accessible by the current user.<p> 3856 * 3857 * @param dbc the current database context 3858 * @param orgUnit the organizational unit to search project in 3859 * @param includeSubOus if to include sub organizational units 3860 * 3861 * @return a list of objects of type <code>{@link CmsProject}</code> 3862 * 3863 * @throws CmsException if something goes wrong 3864 */ 3865 public List<CmsProject> getAllAccessibleProjects( 3866 CmsDbContext dbc, 3867 CmsOrganizationalUnit orgUnit, 3868 boolean includeSubOus) 3869 throws CmsException { 3870 3871 Set<CmsProject> projects = new HashSet<CmsProject>(); 3872 3873 // get the ous where the user has the project manager role 3874 List<CmsOrganizationalUnit> ous = getOrgUnitsForRole( 3875 dbc, 3876 CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()), 3877 includeSubOus); 3878 3879 // get the groups of the user if needed 3880 Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>(); 3881 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 3882 while (itGroups.hasNext()) { 3883 CmsGroup group = itGroups.next(); 3884 userGroupIds.add(group.getId()); 3885 } 3886 3887 // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus' 3888 // get all projects that might come in question 3889 projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName())); 3890 3891 // filter hidden and not accessible projects 3892 Iterator<CmsProject> itProjects = projects.iterator(); 3893 while (itProjects.hasNext()) { 3894 CmsProject project = itProjects.next(); 3895 boolean accessible = true; 3896 // if hidden 3897 accessible = accessible && !project.isHidden(); 3898 3899 if (!includeSubOus) { 3900 // if not exact in the given ou 3901 accessible = accessible && project.getOuFqn().equals(orgUnit.getName()); 3902 } else { 3903 // if not in the given ou 3904 accessible = accessible && project.getOuFqn().startsWith(orgUnit.getName()); 3905 } 3906 3907 if (!accessible) { 3908 itProjects.remove(); 3909 continue; 3910 } 3911 3912 accessible = false; 3913 // online project 3914 accessible = accessible || project.isOnlineProject(); 3915 // if owner 3916 accessible = accessible || project.getOwnerId().equals(dbc.currentUser().getId()); 3917 3918 // project managers 3919 Iterator<CmsOrganizationalUnit> itOus = ous.iterator(); 3920 while (!accessible && itOus.hasNext()) { 3921 CmsOrganizationalUnit ou = itOus.next(); 3922 // for project managers check visibility 3923 accessible = accessible || project.getOuFqn().startsWith(ou.getName()); 3924 } 3925 3926 if (!accessible) { 3927 // if direct user or manager of project 3928 CmsUUID groupId = null; 3929 if (userGroupIds.contains(project.getGroupId())) { 3930 groupId = project.getGroupId(); 3931 } else if (userGroupIds.contains(project.getManagerGroupId())) { 3932 groupId = project.getManagerGroupId(); 3933 } 3934 if (groupId != null) { 3935 String oufqn = readGroup(dbc, groupId).getOuFqn(); 3936 accessible = accessible || (oufqn.startsWith(dbc.getRequestContext().getOuFqn())); 3937 } 3938 } 3939 if (!accessible) { 3940 // remove not accessible project 3941 itProjects.remove(); 3942 } 3943 } 3944 3945 List<CmsProject> accessibleProjects = new ArrayList<CmsProject>(projects); 3946 // sort the list of projects based on the project name 3947 Collections.sort(accessibleProjects); 3948 // ensure the online project is in first place 3949 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 3950 if (accessibleProjects.contains(onlineProject)) { 3951 accessibleProjects.remove(onlineProject); 3952 } 3953 accessibleProjects.add(0, onlineProject); 3954 3955 return accessibleProjects; 3956 } 3957 3958 /** 3959 * Returns a list with all projects from history.<p> 3960 * 3961 * @param dbc the current database context 3962 * 3963 * @return list of <code>{@link CmsHistoryProject}</code> objects 3964 * with all projects from history. 3965 * 3966 * @throws CmsException if operation was not successful 3967 */ 3968 public List<CmsHistoryProject> getAllHistoricalProjects(CmsDbContext dbc) throws CmsException { 3969 3970 // user is allowed to access all existing projects for the ous he has the project_manager role 3971 Set<CmsOrganizationalUnit> manOus = new HashSet<CmsOrganizationalUnit>( 3972 getOrgUnitsForRole(dbc, CmsRole.PROJECT_MANAGER, true)); 3973 3974 List<CmsHistoryProject> projects = getHistoryDriver(dbc).readProjects(dbc); 3975 Iterator<CmsHistoryProject> itProjects = projects.iterator(); 3976 while (itProjects.hasNext()) { 3977 CmsHistoryProject project = itProjects.next(); 3978 if (project.isHidden()) { 3979 // project is hidden 3980 itProjects.remove(); 3981 continue; 3982 } 3983 if (!project.getOuFqn().startsWith(dbc.currentUser().getOuFqn())) { 3984 // project is not visible from the users ou 3985 itProjects.remove(); 3986 continue; 3987 } 3988 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, project.getOuFqn()); 3989 if (manOus.contains(ou)) { 3990 // user is project manager for this project 3991 continue; 3992 } else if (project.getOwnerId().equals(dbc.currentUser().getId())) { 3993 // user is owner of the project 3994 continue; 3995 } else { 3996 boolean found = false; 3997 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 3998 while (itGroups.hasNext()) { 3999 CmsGroup group = itGroups.next(); 4000 if (project.getManagerGroupId().equals(group.getId())) { 4001 found = true; 4002 break; 4003 } 4004 } 4005 if (found) { 4006 // user is member of the manager group of the project 4007 continue; 4008 } 4009 } 4010 itProjects.remove(); 4011 } 4012 return projects; 4013 } 4014 4015 /** 4016 * Returns all projects which are owned by the current user or which are manageable 4017 * for the group of the user.<p> 4018 * 4019 * @param dbc the current database context 4020 * @param orgUnit the organizational unit to search project in 4021 * @param includeSubOus if to include sub organizational units 4022 * 4023 * @return a list of objects of type <code>{@link CmsProject}</code> 4024 * 4025 * @throws CmsException if operation was not successful 4026 */ 4027 public List<CmsProject> getAllManageableProjects( 4028 CmsDbContext dbc, 4029 CmsOrganizationalUnit orgUnit, 4030 boolean includeSubOus) 4031 throws CmsException { 4032 4033 Set<CmsProject> projects = new HashSet<CmsProject>(); 4034 4035 // get the ous where the user has the project manager role 4036 List<CmsOrganizationalUnit> ous = getOrgUnitsForRole( 4037 dbc, 4038 CmsRole.PROJECT_MANAGER.forOrgUnit(orgUnit.getName()), 4039 includeSubOus); 4040 4041 // get the groups of the user if needed 4042 Set<CmsUUID> userGroupIds = new HashSet<CmsUUID>(); 4043 Iterator<CmsGroup> itGroups = getGroupsOfUser(dbc, dbc.currentUser().getName(), false).iterator(); 4044 while (itGroups.hasNext()) { 4045 CmsGroup group = itGroups.next(); 4046 userGroupIds.add(group.getId()); 4047 } 4048 4049 // TODO: this could be optimize if this method would have an additional parameter 'includeSubOus' 4050 // get all projects that might come in question 4051 projects.addAll(getProjectDriver(dbc).readProjects(dbc, orgUnit.getName())); 4052 4053 // filter hidden and not manageable projects 4054 Iterator<CmsProject> itProjects = projects.iterator(); 4055 while (itProjects.hasNext()) { 4056 CmsProject project = itProjects.next(); 4057 boolean manageable = true; 4058 // if online 4059 manageable = manageable && !project.isOnlineProject(); 4060 // if hidden 4061 manageable = manageable && !project.isHidden(); 4062 4063 if (!includeSubOus) { 4064 // if not exact in the given ou 4065 manageable = manageable && project.getOuFqn().equals(orgUnit.getName()); 4066 } else { 4067 // if not in the given ou 4068 manageable = manageable && project.getOuFqn().startsWith(orgUnit.getName()); 4069 } 4070 4071 if (!manageable) { 4072 itProjects.remove(); 4073 continue; 4074 } 4075 4076 manageable = false; 4077 // if owner 4078 manageable = manageable || project.getOwnerId().equals(dbc.currentUser().getId()); 4079 4080 // project managers 4081 Iterator<CmsOrganizationalUnit> itOus = ous.iterator(); 4082 while (!manageable && itOus.hasNext()) { 4083 CmsOrganizationalUnit ou = itOus.next(); 4084 // for project managers check visibility 4085 manageable = manageable || project.getOuFqn().startsWith(ou.getName()); 4086 } 4087 4088 if (!manageable) { 4089 // if manager of project 4090 if (userGroupIds.contains(project.getManagerGroupId())) { 4091 String oufqn = readGroup(dbc, project.getManagerGroupId()).getOuFqn(); 4092 manageable = manageable || (oufqn.startsWith(dbc.getRequestContext().getOuFqn())); 4093 } 4094 } 4095 if (!manageable) { 4096 // remove not accessible project 4097 itProjects.remove(); 4098 } 4099 } 4100 4101 List<CmsProject> manageableProjects = new ArrayList<CmsProject>(projects); 4102 // sort the list of projects based on the project name 4103 Collections.sort(manageableProjects); 4104 // ensure the online project is not in the list 4105 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 4106 if (manageableProjects.contains(onlineProject)) { 4107 manageableProjects.remove(onlineProject); 4108 } 4109 4110 return manageableProjects; 4111 } 4112 4113 /** 4114 * Returns all child groups of a group.<p> 4115 * 4116 * @param dbc the current database context 4117 * @param group the group to get the child for 4118 * @param includeSubChildren if set also returns all sub-child groups of the given group 4119 * 4120 * @return a list of all child <code>{@link CmsGroup}</code> objects 4121 * 4122 * @throws CmsException if operation was not successful 4123 */ 4124 public List<CmsGroup> getChildren(CmsDbContext dbc, CmsGroup group, boolean includeSubChildren) 4125 throws CmsException { 4126 4127 if (!includeSubChildren) { 4128 return getUserDriver(dbc).readChildGroups(dbc, group.getName()); 4129 } 4130 Set<CmsGroup> allChildren = new TreeSet<CmsGroup>(); 4131 // iterate all child groups 4132 Iterator<CmsGroup> it = getUserDriver(dbc).readChildGroups(dbc, group.getName()).iterator(); 4133 while (it.hasNext()) { 4134 CmsGroup child = it.next(); 4135 // add the group itself 4136 allChildren.add(child); 4137 // now get all sub-children for each group 4138 allChildren.addAll(getChildren(dbc, child, true)); 4139 } 4140 return new ArrayList<CmsGroup>(allChildren); 4141 } 4142 4143 /** 4144 * Returns the date when the resource was last visited by the user.<p> 4145 * 4146 * @param dbc the database context 4147 * @param poolName the name of the database pool to use 4148 * @param user the user to check the date 4149 * @param resource the resource to check the date 4150 * 4151 * @return the date when the resource was last visited by the user 4152 * 4153 * @throws CmsException if something goes wrong 4154 */ 4155 public long getDateLastVisitedBy(CmsDbContext dbc, String poolName, CmsUser user, CmsResource resource) 4156 throws CmsException { 4157 4158 return m_subscriptionDriver.getDateLastVisitedBy(dbc, poolName, user, resource); 4159 } 4160 4161 /** 4162 * Returns all groups of the given organizational unit.<p> 4163 * 4164 * @param dbc the current db context 4165 * @param orgUnit the organizational unit to get the groups for 4166 * @param includeSubOus if all groups of sub-organizational units should be retrieved too 4167 * @param readRoles if to read roles or groups 4168 * 4169 * @return all <code>{@link CmsGroup}</code> objects in the organizational unit 4170 * 4171 * @throws CmsException if operation was not successful 4172 * 4173 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 4174 * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean) 4175 */ 4176 public List<CmsGroup> getGroups( 4177 CmsDbContext dbc, 4178 CmsOrganizationalUnit orgUnit, 4179 boolean includeSubOus, 4180 boolean readRoles) 4181 throws CmsException { 4182 4183 return getUserDriver(dbc).getGroups(dbc, orgUnit, includeSubOus, readRoles); 4184 } 4185 4186 /** 4187 * Returns the groups of an user filtered by the specified IP address.<p> 4188 * 4189 * @param dbc the current database context 4190 * @param username the name of the user 4191 * @param readRoles if to read roles or groups 4192 * 4193 * @return the groups of the given user, as a list of {@link CmsGroup} objects 4194 * 4195 * @throws CmsException if something goes wrong 4196 */ 4197 public List<CmsGroup> getGroupsOfUser(CmsDbContext dbc, String username, boolean readRoles) throws CmsException { 4198 4199 return getGroupsOfUser(dbc, username, "", true, readRoles, false, dbc.getRequestContext().getRemoteAddress()); 4200 } 4201 4202 /** 4203 * Returns the groups of an user filtered by the specified IP address.<p> 4204 * 4205 * @param dbc the current database context 4206 * @param username the name of the user 4207 * @param ouFqn the fully qualified name of the organizational unit to restrict the result set for 4208 * @param includeChildOus include groups of child organizational units 4209 * @param readRoles if to read roles or groups 4210 * @param directGroupsOnly if set only the direct assigned groups will be returned, if not also indirect groups 4211 * @param remoteAddress the IP address to filter the groups in the result list 4212 * 4213 * @return a list of <code>{@link CmsGroup}</code> objects 4214 * 4215 * @throws CmsException if operation was not successful 4216 */ 4217 public List<CmsGroup> getGroupsOfUser( 4218 CmsDbContext dbc, 4219 String username, 4220 String ouFqn, 4221 boolean includeChildOus, 4222 boolean readRoles, 4223 boolean directGroupsOnly, 4224 String remoteAddress) 4225 throws CmsException { 4226 4227 CmsUser user = readUser(dbc, username); 4228 String prefix = ouFqn + "_" + includeChildOus + "_" + directGroupsOnly + "_" + readRoles + "_" + remoteAddress; 4229 String cacheKey = m_keyGenerator.getCacheKeyForUserGroups(prefix, dbc, user); 4230 List<CmsGroup> groups = m_monitor.getCachedUserGroups(user.getId(), cacheKey); 4231 if (groups == null) { 4232 // get all groups of the user 4233 List<CmsGroup> directGroups = getUserDriver(dbc).readGroupsOfUser( 4234 dbc, 4235 user.getId(), 4236 readRoles ? "" : ouFqn, 4237 readRoles ? true : includeChildOus, 4238 remoteAddress, 4239 readRoles); 4240 Set<CmsGroup> allGroups = new HashSet<CmsGroup>(); 4241 if (!readRoles) { 4242 allGroups.addAll(directGroups); 4243 } 4244 if (!directGroupsOnly) { 4245 if (!readRoles) { 4246 // now get all parents of the groups 4247 for (int i = 0; i < directGroups.size(); i++) { 4248 CmsGroup parent = getParent(dbc, directGroups.get(i).getName()); 4249 while ((parent != null) && (!allGroups.contains(parent))) { 4250 if (parent.getOuFqn().startsWith(ouFqn)) { 4251 allGroups.add(parent); 4252 } 4253 // read next parent group 4254 parent = getParent(dbc, parent.getName()); 4255 } 4256 } 4257 } 4258 } 4259 if (readRoles) { 4260 // for each for role 4261 for (int i = 0; i < directGroups.size(); i++) { 4262 CmsGroup group = directGroups.get(i); 4263 CmsRole role = CmsRole.valueOf(group); 4264 if (!includeChildOus && role.getOuFqn().equals(ouFqn)) { 4265 allGroups.add(group); 4266 } 4267 if (includeChildOus && role.getOuFqn().startsWith(ouFqn)) { 4268 allGroups.add(group); 4269 } 4270 if (directGroupsOnly || (!includeChildOus && !role.getOuFqn().equals(ouFqn))) { 4271 // if roles of child OUs are not requested and the role does not belong to the requested OU don't include the role children 4272 continue; 4273 } 4274 CmsOrganizationalUnit currentOu = readOrganizationalUnit(dbc, group.getOuFqn()); 4275 boolean readChildRoleGroups = true; 4276 if (currentOu.hasFlagWebuser() && role.forOrgUnit(null).equals(CmsRole.ACCOUNT_MANAGER)) { 4277 readChildRoleGroups = false; 4278 } 4279 if (readChildRoleGroups) { 4280 // get the child roles 4281 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 4282 while (itChildRoles.hasNext()) { 4283 CmsRole childRole = itChildRoles.next(); 4284 if (childRole.isSystemRole()) { 4285 if (canReadRoleInOu(currentOu, childRole)) { 4286 // include system roles only 4287 try { 4288 allGroups.add(readGroup(dbc, childRole.getGroupName())); 4289 } catch (CmsDataAccessException e) { 4290 // should not happen, log error if it does 4291 LOG.error(e.getLocalizedMessage(), e); 4292 } 4293 } 4294 } 4295 } 4296 } else { 4297 LOG.info("Skipping child role group check for web user OU " + currentOu.getName()); 4298 } 4299 if (includeChildOus) { 4300 // if needed include the roles of child ous 4301 Iterator<CmsOrganizationalUnit> itSubOus = getOrganizationalUnits( 4302 dbc, 4303 readOrganizationalUnit(dbc, group.getOuFqn()), 4304 true).iterator(); 4305 while (itSubOus.hasNext()) { 4306 CmsOrganizationalUnit subOu = itSubOus.next(); 4307 // add role in child ou 4308 try { 4309 if (canReadRoleInOu(subOu, role)) { 4310 allGroups.add(readGroup(dbc, role.forOrgUnit(subOu.getName()).getGroupName())); 4311 } 4312 } catch (CmsDbEntryNotFoundException e) { 4313 // ignore, this may happen while deleting an orgunit 4314 if (LOG.isDebugEnabled()) { 4315 LOG.debug(e.getLocalizedMessage(), e); 4316 } 4317 } 4318 // add child roles in child ous 4319 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 4320 while (itChildRoles.hasNext()) { 4321 CmsRole childRole = itChildRoles.next(); 4322 try { 4323 if (canReadRoleInOu(subOu, childRole)) { 4324 allGroups.add( 4325 readGroup(dbc, childRole.forOrgUnit(subOu.getName()).getGroupName())); 4326 } 4327 } catch (CmsDbEntryNotFoundException e) { 4328 // ignore, this may happen while deleting an orgunit 4329 if (LOG.isDebugEnabled()) { 4330 LOG.debug(e.getLocalizedMessage(), e); 4331 } 4332 } 4333 } 4334 } 4335 } 4336 } 4337 } 4338 // make group list unmodifiable for caching 4339 groups = Collections.unmodifiableList(new ArrayList<CmsGroup>(allGroups)); 4340 if (dbc.getProjectId().isNullUUID()) { 4341 m_monitor.getGroupListCache().setGroups(user, cacheKey, groups); 4342 } 4343 } 4344 4345 return groups; 4346 } 4347 4348 /** 4349 * Returns the history driver.<p> 4350 * 4351 * @return the history driver 4352 */ 4353 public I_CmsHistoryDriver getHistoryDriver() { 4354 4355 return m_historyDriver; 4356 } 4357 4358 /** 4359 * Returns the history driver for a given database context.<p> 4360 * 4361 * @param dbc the database context 4362 * @return the history driver for the database context 4363 */ 4364 public I_CmsHistoryDriver getHistoryDriver(CmsDbContext dbc) { 4365 4366 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4367 return m_historyDriver; 4368 } 4369 I_CmsHistoryDriver driver = dbc.getHistoryDriver(dbc.getProjectId()); 4370 return driver != null ? driver : m_historyDriver; 4371 4372 } 4373 4374 /** 4375 * Returns the number of idle connections managed by a pool.<p> 4376 * 4377 * @param dbPoolUrl the url of a pool 4378 * @return the number of idle connections 4379 * @throws CmsDbException if something goes wrong 4380 */ 4381 public int getIdleConnections(String dbPoolUrl) throws CmsDbException { 4382 4383 CmsDbPoolV11 pool = m_pools.get(dbPoolUrl); 4384 if (pool == null) { 4385 CmsMessageContainer message = Messages.get().container(Messages.ERR_UNKNOWN_POOL_URL_1, dbPoolUrl); 4386 throw new CmsDbException(message); 4387 } 4388 try { 4389 return pool.getIdleConnections(); 4390 } catch (Exception exc) { 4391 CmsMessageContainer message = Messages.get().container(Messages.ERR_ACCESSING_POOL_1, dbPoolUrl); 4392 throw new CmsDbException(message, exc); 4393 } 4394 4395 } 4396 4397 /** 4398 * Returns the lock state of a resource.<p> 4399 * 4400 * @param dbc the current database context 4401 * @param resource the resource to return the lock state for 4402 * 4403 * @return the lock state of the resource 4404 * 4405 * @throws CmsException if something goes wrong 4406 */ 4407 public CmsLock getLock(CmsDbContext dbc, CmsResource resource) throws CmsException { 4408 4409 return m_lockManager.getLock(dbc, resource); 4410 } 4411 4412 /** 4413 * Returns all locked resources in a given folder.<p> 4414 * 4415 * @param dbc the current database context 4416 * @param resource the folder to search in 4417 * @param filter the lock filter 4418 * 4419 * @return a list of locked resource paths (relative to current site) 4420 * 4421 * @throws CmsException if the current project is locked 4422 */ 4423 public List<String> getLockedResources(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter) 4424 throws CmsException { 4425 4426 List<String> lockedResources = new ArrayList<String>(); 4427 // get locked resources 4428 Iterator<CmsLock> it = m_lockManager.getLocks(dbc, resource.getRootPath(), filter).iterator(); 4429 while (it.hasNext()) { 4430 CmsLock lock = it.next(); 4431 lockedResources.add(dbc.removeSiteRoot(lock.getResourceName())); 4432 } 4433 Collections.sort(lockedResources); 4434 return lockedResources; 4435 } 4436 4437 /** 4438 * Returns all locked resources in a given folder.<p> 4439 * 4440 * @param dbc the current database context 4441 * @param resource the folder to search in 4442 * @param filter the lock filter 4443 * 4444 * @return a list of locked resources 4445 * 4446 * @throws CmsException if the current project is locked 4447 */ 4448 public List<CmsResource> getLockedResourcesObjects(CmsDbContext dbc, CmsResource resource, CmsLockFilter filter) 4449 throws CmsException { 4450 4451 return m_lockManager.getLockedResources(dbc, resource, filter); 4452 } 4453 4454 /** 4455 * Returns all locked resources in a given folder, but uses a cache for resource lookups.<p> 4456 * 4457 * @param dbc the current database context 4458 * @param resource the folder to search in 4459 * @param filter the lock filter 4460 * @param cache the cache to use for resource lookups 4461 * 4462 * @return a list of locked resources 4463 * 4464 * @throws CmsException if the current project is locked 4465 */ 4466 public List<CmsResource> getLockedResourcesObjectsWithCache( 4467 CmsDbContext dbc, 4468 CmsResource resource, 4469 CmsLockFilter filter, 4470 Map<String, CmsResource> cache) 4471 throws CmsException { 4472 4473 return m_lockManager.getLockedResourcesWithCache(dbc, resource, filter, cache); 4474 } 4475 4476 /** 4477 * Returns all log entries matching the given filter.<p> 4478 * 4479 * @param dbc the current db context 4480 * @param filter the filter to match the log entries 4481 * 4482 * @return all log entries matching the given filter 4483 * 4484 * @throws CmsException if something goes wrong 4485 * 4486 * @see CmsSecurityManager#getLogEntries(CmsRequestContext, CmsLogFilter) 4487 */ 4488 public List<CmsLogEntry> getLogEntries(CmsDbContext dbc, CmsLogFilter filter) throws CmsException { 4489 4490 updateLog(dbc); 4491 return m_projectDriver.readLog(dbc, filter); 4492 } 4493 4494 /** 4495 * Returns the next publish tag for the published historical resources.<p> 4496 * 4497 * @param dbc the current database context 4498 * 4499 * @return the next available publish tag 4500 */ 4501 public int getNextPublishTag(CmsDbContext dbc) { 4502 4503 if (dbc.getProjectId().isNullUUID()) { 4504 synchronized (m_publishTagLock) { 4505 int dbNextPublishTag = getHistoryDriver(dbc).readNextPublishTag(dbc); 4506 if (m_lastPublishTag == null) { 4507 m_lastPublishTag = Integer.valueOf(dbNextPublishTag); 4508 return dbNextPublishTag; 4509 } else { 4510 int newValue = Math.max(dbNextPublishTag, m_lastPublishTag.intValue() + 1); 4511 m_lastPublishTag = Integer.valueOf(newValue); 4512 return newValue; 4513 4514 } 4515 } 4516 } else { 4517 return getHistoryDriver(dbc).readNextPublishTag(dbc); 4518 } 4519 4520 } 4521 4522 /** 4523 * Returns all child organizational units of the given parent organizational unit including 4524 * hierarchical deeper organization units if needed.<p> 4525 * 4526 * @param dbc the current db context 4527 * @param parent the parent organizational unit, or <code>null</code> for the root 4528 * @param includeChildren if hierarchical deeper organization units should also be returned 4529 * 4530 * @return a list of <code>{@link CmsOrganizationalUnit}</code> objects 4531 * 4532 * @throws CmsException if operation was not successful 4533 * 4534 * @see org.opencms.security.CmsOrgUnitManager#getOrganizationalUnits(CmsObject, String, boolean) 4535 */ 4536 public List<CmsOrganizationalUnit> getOrganizationalUnits( 4537 CmsDbContext dbc, 4538 CmsOrganizationalUnit parent, 4539 boolean includeChildren) 4540 throws CmsException { 4541 4542 if (parent == null) { 4543 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_PARENT_ORGUNIT_NULL_0)); 4544 } 4545 return getUserDriver(dbc).getOrganizationalUnits(dbc, parent, includeChildren); 4546 } 4547 4548 /** 4549 * Returns all the organizational units for which the current user has the given role.<p> 4550 * 4551 * @param dbc the current database context 4552 * @param role the role to check 4553 * @param includeSubOus if sub organizational units should be included in the search 4554 * 4555 * @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects 4556 * 4557 * @throws CmsException if something goes wrong 4558 */ 4559 public List<CmsOrganizationalUnit> getOrgUnitsForRole(CmsDbContext dbc, CmsRole role, boolean includeSubOus) 4560 throws CmsException { 4561 4562 String ouFqn = role.getOuFqn(); 4563 if (ouFqn == null) { 4564 ouFqn = ""; 4565 role = role.forOrgUnit(""); 4566 } 4567 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, ouFqn); 4568 List<CmsOrganizationalUnit> orgUnits = new ArrayList<CmsOrganizationalUnit>(); 4569 if (m_securityManager.hasRole(dbc, dbc.currentUser(), role)) { 4570 orgUnits.add(ou); 4571 } 4572 if (includeSubOus) { 4573 Iterator<CmsOrganizationalUnit> it = getOrganizationalUnits(dbc, ou, true).iterator(); 4574 while (it.hasNext()) { 4575 CmsOrganizationalUnit orgUnit = it.next(); 4576 if (m_securityManager.hasRole(dbc, dbc.currentUser(), role.forOrgUnit(orgUnit.getName()))) { 4577 orgUnits.add(orgUnit); 4578 } 4579 } 4580 } 4581 return orgUnits; 4582 } 4583 4584 /** 4585 * Returns the parent group of a group.<p> 4586 * 4587 * @param dbc the current database context 4588 * @param groupname the name of the group 4589 * 4590 * @return group the parent group or <code>null</code> 4591 * 4592 * @throws CmsException if operation was not successful 4593 */ 4594 public CmsGroup getParent(CmsDbContext dbc, String groupname) throws CmsException { 4595 4596 CmsGroup group = readGroup(dbc, groupname); 4597 if (group.getParentId().isNullUUID()) { 4598 return null; 4599 } 4600 4601 // try to read from cache 4602 CmsGroup parent = m_monitor.getCachedGroup(group.getParentId().toString()); 4603 if (parent == null) { 4604 parent = getUserDriver(dbc).readGroup(dbc, group.getParentId()); 4605 m_monitor.cacheGroup(parent); 4606 } 4607 return parent; 4608 } 4609 4610 /** 4611 * Returns the set of permissions of the current user for a given resource.<p> 4612 * 4613 * @param dbc the current database context 4614 * @param resource the resource 4615 * @param user the user 4616 * 4617 * @return bit set with allowed permissions 4618 * 4619 * @throws CmsException if something goes wrong 4620 */ 4621 public CmsPermissionSetCustom getPermissions(CmsDbContext dbc, CmsResource resource, CmsUser user) 4622 throws CmsException { 4623 4624 CmsAccessControlList acList = getAccessControlList(dbc, resource, false); 4625 List<CmsGroup> groups = getGroupsOfUser(dbc, user.getName(), false); 4626 List<CmsRole> roles = getRolesForUser(dbc, user); 4627 CmsPermissionSetCustom permissions = acList.getPermissions(user, groups, roles); 4628 4629 if (acList.getExclusiveAccessPrincipals().size() > 0) { 4630 long now; 4631 @SuppressWarnings("unchecked") 4632 Supplier<Long> alternativeClock = (Supplier<Long>)(dbc.getRequestContext().getAttribute( 4633 ATTR_EXCLUSIVE_ACCESS_CLOCK)); 4634 if (alternativeClock != null) { 4635 // used for testing 4636 now = alternativeClock.get().longValue(); 4637 } else { 4638 // *NOT* using dbc.getRequestContext().getRequestTime(), even though that value is used for normal resource availability checks, because 4639 // that value may be manipulated by some workplace classes, and we want the real time for permission checks 4640 now = System.currentTimeMillis(); 4641 } 4642 permissions.setCacheable(false); // resources going in/out of availability can change permissions - don't cache 4643 if (!resource.isReleasedAndNotExpired(now)) { 4644 boolean hasExclusiveAccess = false; 4645 for (CmsGroup group : groups) { 4646 if (acList.getExclusiveAccessPrincipals().contains(group.getId())) { 4647 hasExclusiveAccess = true; 4648 break; 4649 } 4650 } 4651 hasExclusiveAccess |= acList.getExclusiveAccessPrincipals().contains(user.getId()); 4652 if (!hasExclusiveAccess) { 4653 permissions.denyPermissions(CmsPermissionSet.PERMISSION_FULL); 4654 } 4655 } 4656 } 4657 return permissions; 4658 } 4659 4660 /** 4661 * Returns the project driver.<p> 4662 * 4663 * @return the project driver 4664 */ 4665 public I_CmsProjectDriver getProjectDriver() { 4666 4667 return m_projectDriver; 4668 } 4669 4670 /** 4671 * Returns the project driver for a given DB context.<p> 4672 * 4673 * @param dbc the database context 4674 * 4675 * @return the project driver for the database context 4676 */ 4677 public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc) { 4678 4679 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4680 return m_projectDriver; 4681 } 4682 I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId()); 4683 return driver != null ? driver : m_projectDriver; 4684 } 4685 4686 /** 4687 * Returns either the project driver for the DB context (if it has one) or a default project driver.<p> 4688 * 4689 * @param dbc the DB context 4690 * @param defaultDriver the driver which should be returned if there is no project driver for the DB context 4691 * 4692 * @return either the project driver for the DB context, or the default driver 4693 */ 4694 public I_CmsProjectDriver getProjectDriver(CmsDbContext dbc, I_CmsProjectDriver defaultDriver) { 4695 4696 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 4697 return defaultDriver; 4698 } 4699 I_CmsProjectDriver driver = dbc.getProjectDriver(dbc.getProjectId()); 4700 return driver != null ? driver : defaultDriver; 4701 } 4702 4703 /** 4704 * Returns the uuid id for the given id.<p> 4705 * 4706 * TODO: remove this method as soon as possible 4707 * 4708 * @param dbc the current database context 4709 * @param id the old project id 4710 * 4711 * @return the new uuid for the given id 4712 * 4713 * @throws CmsException if something goes wrong 4714 */ 4715 public CmsUUID getProjectId(CmsDbContext dbc, int id) throws CmsException { 4716 4717 Iterator<CmsProject> itProjects = getAllAccessibleProjects( 4718 dbc, 4719 readOrganizationalUnit(dbc, ""), 4720 true).iterator(); 4721 while (itProjects.hasNext()) { 4722 CmsProject project = itProjects.next(); 4723 if (project.getUuid().hashCode() == id) { 4724 return project.getUuid(); 4725 } 4726 } 4727 return null; 4728 } 4729 4730 /** 4731 * Returns the configuration read from the <code>opencms.properties</code> file.<p> 4732 * 4733 * @return the configuration read from the <code>opencms.properties</code> file 4734 */ 4735 public CmsParameterConfiguration getPropertyConfiguration() { 4736 4737 return m_propertyConfiguration; 4738 } 4739 4740 /** 4741 * Returns a new publish list that contains the unpublished resources related 4742 * to all resources in the given publish list, the related resources exclude 4743 * all resources in the given publish list and also locked (by other users) resources.<p> 4744 * 4745 * @param dbc the current database context 4746 * @param publishList the publish list to exclude from result 4747 * @param filter the relation filter to use to get the related resources 4748 * 4749 * @return a new publish list that contains the related resources 4750 * 4751 * @throws CmsException if something goes wrong 4752 * 4753 * @see org.opencms.publish.CmsPublishManager#getRelatedResourcesToPublish(CmsObject, CmsPublishList) 4754 */ 4755 public CmsPublishList getRelatedResourcesToPublish( 4756 CmsDbContext dbc, 4757 CmsPublishList publishList, 4758 CmsRelationFilter filter) 4759 throws CmsException { 4760 4761 Map<String, CmsResource> relations = new HashMap<String, CmsResource>(); 4762 4763 // check if progress should be set in the thread 4764 A_CmsProgressThread thread = null; 4765 if (Thread.currentThread() instanceof A_CmsProgressThread) { 4766 thread = (A_CmsProgressThread)Thread.currentThread(); 4767 } 4768 4769 // get all resources to publish 4770 List<CmsResource> publishResources = publishList.getAllResources(); 4771 Iterator<CmsResource> itCheckList = publishResources.iterator(); 4772 // iterate over them 4773 int count = 0; 4774 while (itCheckList.hasNext()) { 4775 4776 // set progress in thread 4777 count++; 4778 if (thread != null) { 4779 4780 if (thread.isInterrupted()) { 4781 throw new CmsIllegalStateException( 4782 org.opencms.workplace.commons.Messages.get().container( 4783 org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0)); 4784 } 4785 thread.setProgress((count * 20) / publishResources.size()); 4786 thread.setDescription( 4787 org.opencms.workplace.commons.Messages.get().getBundle().key( 4788 org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP1_2, 4789 Integer.valueOf(count), 4790 Integer.valueOf(publishResources.size()))); 4791 } 4792 4793 CmsResource checkResource = itCheckList.next(); 4794 // get and iterate over all related resources 4795 Iterator<CmsRelation> itRelations = getRelationsForResource(dbc, checkResource, filter).iterator(); 4796 while (itRelations.hasNext()) { 4797 CmsRelation relation = itRelations.next(); 4798 try { 4799 // get the target of the relation, see CmsRelation#getTarget(CmsObject, CmsResourceFilter) 4800 CmsResource target; 4801 try { 4802 // first look up by id 4803 target = readResource(dbc, relation.getTargetId(), CmsResourceFilter.ALL); 4804 } catch (CmsVfsResourceNotFoundException e) { 4805 // then look up by name, but from the root site 4806 String storedSiteRoot = dbc.getRequestContext().getSiteRoot(); 4807 try { 4808 dbc.getRequestContext().setSiteRoot(""); 4809 target = readResource(dbc, relation.getTargetPath(), CmsResourceFilter.ALL); 4810 } finally { 4811 dbc.getRequestContext().setSiteRoot(storedSiteRoot); 4812 } 4813 } 4814 CmsLock lock = getLock(dbc, target); 4815 // just add resources that may come in question 4816 if (!publishResources.contains(target) // is not in the original list 4817 && !relations.containsKey(target.getRootPath()) // has not been already added by another relation 4818 && !target.getState().isUnchanged() // has been changed 4819 && lock.isLockableBy(dbc.currentUser())) { // is lockable by current user 4820 4821 relations.put(target.getRootPath(), target); 4822 // now check the folder structure 4823 CmsResource parent = getVfsDriver(dbc).readParentFolder( 4824 dbc, 4825 dbc.currentProject().getUuid(), 4826 target.getStructureId()); 4827 while ((parent != null) && parent.getState().isNew()) { 4828 // just add resources that may come in question 4829 if (!publishResources.contains(parent) // is not in the original list 4830 && !relations.containsKey(parent.getRootPath())) { // has not been already added by another relation 4831 4832 relations.put(parent.getRootPath(), parent); 4833 } 4834 parent = getVfsDriver(dbc).readParentFolder( 4835 dbc, 4836 dbc.currentProject().getUuid(), 4837 parent.getStructureId()); 4838 } 4839 } 4840 } catch (CmsVfsResourceNotFoundException e) { 4841 // ignore broken links 4842 if (LOG.isDebugEnabled()) { 4843 LOG.debug(e.getLocalizedMessage(), e); 4844 } 4845 } 4846 } 4847 } 4848 4849 CmsPublishList ret = new CmsPublishList(publishList.getDirectPublishResources(), false, false); 4850 ret.addAll(relations.values(), false); 4851 ret.initialize(); 4852 return ret; 4853 } 4854 4855 /** 4856 * Returns all relations for the given resource matching the given filter.<p> 4857 * 4858 * @param dbc the current db context 4859 * @param resource the resource to retrieve the relations for 4860 * @param filter the filter to match the relation 4861 * 4862 * @return all relations for the given resource matching the given filter 4863 * 4864 * @throws CmsException if something goes wrong 4865 * 4866 * @see CmsSecurityManager#getRelationsForResource(CmsRequestContext, CmsResource, CmsRelationFilter) 4867 */ 4868 public List<CmsRelation> getRelationsForResource(CmsDbContext dbc, CmsResource resource, CmsRelationFilter filter) 4869 throws CmsException { 4870 4871 CmsUUID projectId = getProjectIdForContext(dbc); 4872 return getVfsDriver(dbc).readRelations(dbc, projectId, resource, filter); 4873 } 4874 4875 /** 4876 * Returns the list of organizational units the given resource belongs to.<p> 4877 * 4878 * @param dbc the current database context 4879 * @param resource the resource 4880 * 4881 * @return list of {@link CmsOrganizationalUnit} objects 4882 * 4883 * @throws CmsException if something goes wrong 4884 */ 4885 public List<CmsOrganizationalUnit> getResourceOrgUnits(CmsDbContext dbc, CmsResource resource) throws CmsException { 4886 4887 boolean nullDbcProjectId = (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID(); 4888 if (nullDbcProjectId && resourceOrgUnitCachingEnabled) { 4889 try { 4890 return m_monitor.getResourceOuCache().get(new ResourceOUCacheKey(this, dbc)).getResourceOrgUnits( 4891 resource.getRootPath()); 4892 } catch (ExecutionException e) { 4893 LOG.error(e.getLocalizedMessage(), e); 4894 } 4895 } 4896 List<CmsOrganizationalUnit> result = getVfsDriver(dbc).getResourceOus( 4897 dbc, 4898 dbc.currentProject().getUuid(), 4899 resource); 4900 4901 return result; 4902 } 4903 4904 /** 4905 * Returns all resources of the given organizational unit.<p> 4906 * 4907 * @param dbc the current db context 4908 * @param orgUnit the organizational unit to get all resources for 4909 * 4910 * @return all <code>{@link CmsResource}</code> objects in the organizational unit 4911 * 4912 * @throws CmsException if operation was not successful 4913 * 4914 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 4915 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 4916 * @see org.opencms.security.CmsOrgUnitManager#getGroups(CmsObject, String, boolean) 4917 */ 4918 public List<CmsResource> getResourcesForOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit) 4919 throws CmsException { 4920 4921 return getUserDriver(dbc).getResourcesForOrganizationalUnit(dbc, orgUnit); 4922 } 4923 4924 /** 4925 * Returns all resources associated to a given principal via an ACE with the given permissions.<p> 4926 * 4927 * If the <code>includeAttr</code> flag is set it returns also all resources associated to 4928 * a given principal through some of following attributes.<p> 4929 * 4930 * <ul> 4931 * <li>User Created</li> 4932 * <li>User Last Modified</li> 4933 * </ul><p> 4934 * 4935 * @param dbc the current database context 4936 * @param project the to read the entries from 4937 * @param principalId the id of the principal 4938 * @param permissions a set of permissions to match, can be <code>null</code> for all ACEs 4939 * @param includeAttr a flag to include resources associated by attributes 4940 * 4941 * @return a set of <code>{@link CmsResource}</code> objects 4942 * 4943 * @throws CmsException if something goes wrong 4944 */ 4945 public Set<CmsResource> getResourcesForPrincipal( 4946 CmsDbContext dbc, 4947 CmsProject project, 4948 CmsUUID principalId, 4949 CmsPermissionSet permissions, 4950 boolean includeAttr) 4951 throws CmsException { 4952 4953 Set<CmsResource> resources = new HashSet<CmsResource>( 4954 getVfsDriver(dbc).readResourcesForPrincipalACE(dbc, project, principalId)); 4955 if (permissions != null) { 4956 Iterator<CmsResource> itRes = resources.iterator(); 4957 while (itRes.hasNext()) { 4958 CmsAccessControlEntry ace = readAccessControlEntry(dbc, itRes.next(), principalId); 4959 if ((ace.getPermissions().getPermissions() 4960 & permissions.getPermissions()) != permissions.getPermissions()) { 4961 // remove if permissions does not match 4962 itRes.remove(); 4963 } 4964 } 4965 } 4966 if (includeAttr) { 4967 resources.addAll(getVfsDriver(dbc).readResourcesForPrincipalAttr(dbc, project, principalId)); 4968 } 4969 return resources; 4970 } 4971 4972 /** 4973 * Gets the rewrite aliases matching a given filter.<p> 4974 * 4975 * @param dbc the current database context 4976 * @param filter the filter used for filtering rewrite aliases 4977 * 4978 * @return the rewrite aliases matching the given filter 4979 * 4980 * @throws CmsException if something goes wrong 4981 */ 4982 public List<CmsRewriteAlias> getRewriteAliases(CmsDbContext dbc, CmsRewriteAliasFilter filter) throws CmsException { 4983 4984 return getVfsDriver(dbc).readRewriteAliases(dbc, filter); 4985 } 4986 4987 /** 4988 * Collects the groups which constitute a given role.<p> 4989 * 4990 * @param dbc the database context 4991 * @param roleGroupName the group related to the role 4992 * @param directUsersOnly if true, only the group belonging to the entry itself wil 4993 * 4994 * @return the set of groups which constitute the role 4995 * 4996 * @throws CmsException if something goes wrong 4997 */ 4998 public Set<CmsGroup> getRoleGroups(CmsDbContext dbc, String roleGroupName, boolean directUsersOnly) 4999 throws CmsException { 5000 5001 return getRoleGroupsImpl(dbc, roleGroupName, directUsersOnly, new HashMap<String, Set<CmsGroup>>()); 5002 } 5003 5004 /** 5005 * Collects the groups which constitute a given role.<p> 5006 * 5007 * @param dbc the database context 5008 * @param roleGroupName the group related to the role 5009 * @param directUsersOnly if true, only the group belonging to the entry itself wil 5010 * @param accumulator a map for memoizing return values of recursive calls 5011 * 5012 * @return the set of groups which constitute the role 5013 * 5014 * @throws CmsException if something goes wrong 5015 */ 5016 public Set<CmsGroup> getRoleGroupsImpl( 5017 CmsDbContext dbc, 5018 String roleGroupName, 5019 boolean directUsersOnly, 5020 Map<String, Set<CmsGroup>> accumulator) 5021 throws CmsException { 5022 5023 Set<CmsGroup> result = new HashSet<CmsGroup>(); 5024 if (accumulator.get(roleGroupName) != null) { 5025 return accumulator.get(roleGroupName); 5026 } 5027 CmsGroup group = readGroup(dbc, roleGroupName); // check that the group really exists 5028 if ((group == null) || (!group.isRole())) { 5029 throw new CmsDbEntryNotFoundException( 5030 Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, roleGroupName)); 5031 } 5032 result.add(group); 5033 if (!directUsersOnly) { 5034 CmsRole role = CmsRole.valueOf(group); 5035 if (role.getParentRole() != null) { 5036 try { 5037 String parentGroup = role.getParentRole().getGroupName(); 5038 // iterate the parent roles 5039 result.addAll(getRoleGroupsImpl(dbc, parentGroup, directUsersOnly, accumulator)); 5040 } catch (CmsDbEntryNotFoundException e) { 5041 // ignore, this may happen while deleting an orgunit 5042 if (LOG.isDebugEnabled()) { 5043 LOG.debug(e.getLocalizedMessage(), e); 5044 } 5045 } 5046 } 5047 String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn()); 5048 if (parentOu != null) { 5049 // iterate the parent ou's 5050 result.addAll(getRoleGroupsImpl(dbc, parentOu + group.getSimpleName(), directUsersOnly, accumulator)); 5051 } 5052 } 5053 accumulator.put(roleGroupName, result); 5054 return result; 5055 } 5056 5057 /** 5058 * Returns all roles the given user has for the given resource.<p> 5059 * 5060 * @param dbc the current database context 5061 * @param user the user to check 5062 * @param resource the resource to check the roles for 5063 * 5064 * @return a list of {@link CmsRole} objects 5065 * 5066 * @throws CmsException if something goes wrong 5067 */ 5068 public List<CmsRole> getRolesForResource(CmsDbContext dbc, CmsUser user, CmsResource resource) throws CmsException { 5069 5070 // guest user has no role 5071 if (user.isGuestUser()) { 5072 return Collections.emptyList(); 5073 } 5074 5075 // try to read from cache 5076 String key = user.getId().toString() + resource.getRootPath(); 5077 List<CmsRole> result = m_monitor.getCachedRoleList(key); 5078 if (result != null) { 5079 return result; 5080 } 5081 result = new ArrayList<CmsRole>(); 5082 5083 Iterator<CmsOrganizationalUnit> itOus = getResourceOrgUnits(dbc, resource).iterator(); 5084 while (itOus.hasNext()) { 5085 CmsOrganizationalUnit ou = itOus.next(); 5086 5087 // read all roles of the current user 5088 List<CmsGroup> groups = new ArrayList<CmsGroup>( 5089 getGroupsOfUser( 5090 dbc, 5091 user.getName(), 5092 ou.getName(), 5093 false, 5094 true, 5095 false, 5096 dbc.getRequestContext().getRemoteAddress())); 5097 // check the roles applying to the given resource 5098 Iterator<CmsGroup> it = groups.iterator(); 5099 while (it.hasNext()) { 5100 CmsGroup group = it.next(); 5101 CmsRole givenRole = CmsRole.valueOf(group).forOrgUnit(null); 5102 if (givenRole.isOrganizationalUnitIndependent() || result.contains(givenRole)) { 5103 // skip already added roles 5104 continue; 5105 } 5106 result.add(givenRole); 5107 } 5108 } 5109 5110 result = Collections.unmodifiableList(result); 5111 m_monitor.cacheRoleList(key, result); 5112 return result; 5113 } 5114 5115 /** 5116 * Returns all roles the given user has independent of the resource.<p> 5117 * 5118 * @param dbc the current database context 5119 * @param user the user to check 5120 * 5121 * @return a list of {@link CmsRole} objects 5122 * 5123 * @throws CmsException if something goes wrong 5124 */ 5125 public List<CmsRole> getRolesForUser(CmsDbContext dbc, CmsUser user) throws CmsException { 5126 5127 // guest user has no role 5128 if (user.isGuestUser()) { 5129 return Collections.emptyList(); 5130 } 5131 5132 // try to read from cache 5133 List<CmsRole> result = m_monitor.getGroupListCache().getBareRoles(user.getId()); 5134 if (result != null) { 5135 return result; 5136 } 5137 result = new ArrayList<CmsRole>(); 5138 5139 // read all roles of the current user 5140 List<CmsGroup> groups = new ArrayList<CmsGroup>( 5141 getGroupsOfUser(dbc, user.getName(), "", true, true, false, dbc.getRequestContext().getRemoteAddress())); 5142 5143 // check the roles applying to the given resource 5144 Iterator<CmsGroup> it = groups.iterator(); 5145 while (it.hasNext()) { 5146 CmsGroup group = it.next(); 5147 CmsRole givenRole = CmsRole.valueOf(group); 5148 givenRole = givenRole.forOrgUnit(null); 5149 if (!result.contains(givenRole)) { 5150 result.add(givenRole); 5151 } 5152 } 5153 result = Collections.unmodifiableList(result); 5154 m_monitor.getGroupListCache().setBareRoles(user, result); 5155 return result; 5156 } 5157 5158 /** 5159 * Returns the security manager this driver manager belongs to.<p> 5160 * 5161 * @return the security manager this driver manager belongs to 5162 */ 5163 public CmsSecurityManager getSecurityManager() { 5164 5165 return m_securityManager; 5166 } 5167 5168 /** 5169 * Returns an instance of the common sql manager.<p> 5170 * 5171 * @return an instance of the common sql manager 5172 */ 5173 public CmsSqlManager getSqlManager() { 5174 5175 return m_sqlManager; 5176 } 5177 5178 /** 5179 * Returns the subscription driver of this driver manager.<p> 5180 * 5181 * @return a subscription driver 5182 */ 5183 public I_CmsSubscriptionDriver getSubscriptionDriver() { 5184 5185 return m_subscriptionDriver; 5186 } 5187 5188 /** 5189 * Returns the user driver.<p> 5190 * 5191 * @return the user driver 5192 */ 5193 public I_CmsUserDriver getUserDriver() { 5194 5195 return m_userDriver; 5196 } 5197 5198 /** 5199 * Returns the user driver for a given database context.<p> 5200 * 5201 * @param dbc the database context 5202 * 5203 * @return the user driver for the database context 5204 */ 5205 public I_CmsUserDriver getUserDriver(CmsDbContext dbc) { 5206 5207 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5208 return m_userDriver; 5209 } 5210 I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId()); 5211 return driver != null ? driver : m_userDriver; 5212 5213 } 5214 5215 /** 5216 * Returns either the user driver for the given DB context (if it has one) or a default value instead.<p> 5217 * 5218 * @param dbc the DB context 5219 * @param defaultDriver the driver that should be returned if no driver for the DB context was found 5220 * 5221 * @return either the user driver for the DB context, or <code>defaultDriver</code> if none were found 5222 */ 5223 public I_CmsUserDriver getUserDriver(CmsDbContext dbc, I_CmsUserDriver defaultDriver) { 5224 5225 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5226 return defaultDriver; 5227 } 5228 I_CmsUserDriver driver = dbc.getUserDriver(dbc.getProjectId()); 5229 return driver != null ? driver : defaultDriver; 5230 } 5231 5232 /** 5233 * Returns all direct users of the given organizational unit.<p> 5234 * 5235 * @param dbc the current db context 5236 * @param orgUnit the organizational unit to get all users for 5237 * @param recursive if all groups of sub-organizational units should be retrieved too 5238 * 5239 * @return all <code>{@link CmsUser}</code> objects in the organizational unit 5240 * 5241 * @throws CmsException if operation was not successful 5242 * 5243 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5244 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5245 */ 5246 public List<CmsUser> getUsers(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, boolean recursive) 5247 throws CmsException { 5248 5249 return getUserDriver(dbc).getUsers(dbc, orgUnit, recursive); 5250 } 5251 5252 /** 5253 * Returns a list of users in a group.<p> 5254 * 5255 * @param dbc the current database context 5256 * @param groupname the name of the group to list users from 5257 * @param includeOtherOuUsers include users of other organizational units 5258 * @param directUsersOnly if set only the direct assigned users will be returned, 5259 * if not also indirect users, ie. members of parent roles, 5260 * this parameter only works with roles 5261 * @param readRoles if to read roles or groups 5262 * 5263 * @return all <code>{@link CmsUser}</code> objects in the group 5264 * 5265 * @throws CmsException if operation was not successful 5266 */ 5267 public List<CmsUser> getUsersOfGroup( 5268 CmsDbContext dbc, 5269 String groupname, 5270 boolean includeOtherOuUsers, 5271 boolean directUsersOnly, 5272 boolean readRoles) 5273 throws CmsException { 5274 5275 return internalUsersOfGroup( 5276 dbc, 5277 CmsOrganizationalUnit.getParentFqn(groupname), 5278 groupname, 5279 includeOtherOuUsers, 5280 directUsersOnly, 5281 readRoles); 5282 } 5283 5284 /** 5285 * Returns the given user's publish list.<p> 5286 * 5287 * @param dbc the database context 5288 * @param userId the user's id 5289 * 5290 * @return the given user's publish list 5291 * 5292 * @throws CmsDataAccessException if something goes wrong 5293 */ 5294 public List<CmsResource> getUsersPubList(CmsDbContext dbc, CmsUUID userId) throws CmsDataAccessException { 5295 5296 synchronized (m_publishListUpdateLock) { 5297 updateLog(dbc); 5298 return m_projectDriver.getUsersPubList(dbc, userId); 5299 } 5300 } 5301 5302 /** 5303 * Returns all direct users of the given organizational unit, without their additional info.<p> 5304 * 5305 * @param dbc the current db context 5306 * @param orgUnit the organizational unit to get all users for 5307 * @param recursive if all groups of sub-organizational units should be retrieved too 5308 * 5309 * @return all <code>{@link CmsUser}</code> objects in the organizational unit 5310 * 5311 * @throws CmsException if operation was not successful 5312 * 5313 * @see org.opencms.security.CmsOrgUnitManager#getResourcesForOrganizationalUnit(CmsObject, String) 5314 * @see org.opencms.security.CmsOrgUnitManager#getUsers(CmsObject, String, boolean) 5315 */ 5316 public List<CmsUser> getUsersWithoutAdditionalInfo( 5317 CmsDbContext dbc, 5318 CmsOrganizationalUnit orgUnit, 5319 boolean recursive) 5320 throws CmsException { 5321 5322 return getUserDriver(dbc).getUsersWithoutAdditionalInfo(dbc, orgUnit, recursive); 5323 } 5324 5325 /** 5326 * Returns the VFS driver.<p> 5327 * 5328 * @return the VFS driver 5329 */ 5330 public I_CmsVfsDriver getVfsDriver() { 5331 5332 return m_vfsDriver; 5333 } 5334 5335 /** 5336 * Returns the VFS driver for the given database context.<p> 5337 * 5338 * @param dbc the database context 5339 * 5340 * @return a VFS driver 5341 */ 5342 public I_CmsVfsDriver getVfsDriver(CmsDbContext dbc) { 5343 5344 if ((dbc == null) || (dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) { 5345 return m_vfsDriver; 5346 } 5347 I_CmsVfsDriver driver = dbc.getVfsDriver(dbc.getProjectId()); 5348 return driver != null ? driver : m_vfsDriver; 5349 5350 } 5351 5352 /** 5353 * Writes a vector of access control entries as new access control entries of a given resource.<p> 5354 * 5355 * Already existing access control entries of this resource are removed before. 5356 * Access is granted, if:<p> 5357 * <ul> 5358 * <li>the current user has control permission on the resource</li> 5359 * </ul> 5360 * 5361 * @param dbc the current database context 5362 * @param resource the resource 5363 * @param acEntries a list of <code>{@link CmsAccessControlEntry}</code> objects 5364 * 5365 * @throws CmsException if something goes wrong 5366 */ 5367 public void importAccessControlEntries( 5368 CmsDbContext dbc, 5369 CmsResource resource, 5370 List<CmsAccessControlEntry> acEntries) 5371 throws CmsException { 5372 5373 I_CmsUserDriver userDriver = getUserDriver(dbc); 5374 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), resource.getResourceId()); 5375 List<CmsAccessControlEntry> fixedAces = new ArrayList<>(); 5376 for (CmsAccessControlEntry entry : acEntries) { 5377 if (entry.getResource() == null) { 5378 entry = new CmsAccessControlEntry( 5379 resource.getResourceId(), 5380 entry.getPrincipal(), 5381 entry.getPermissions(), 5382 entry.getFlags()); 5383 } 5384 fixedAces.add(entry); 5385 } 5386 5387 Iterator<CmsAccessControlEntry> i = fixedAces.iterator(); 5388 while (i.hasNext()) { 5389 userDriver.writeAccessControlEntry(dbc, dbc.currentProject(), i.next()); 5390 } 5391 m_monitor.clearAccessControlListCache(); 5392 } 5393 5394 /** 5395 * Imports a rewrite alias.<p> 5396 * 5397 * @param dbc the database context 5398 * @param siteRoot the site root of the alias 5399 * @param source the source of the alias 5400 * @param target the target of the alias 5401 * @param mode the alias mode 5402 * 5403 * @return the import result 5404 * 5405 * @throws CmsException if something goes wrong 5406 */ 5407 public CmsAliasImportResult importRewriteAlias( 5408 CmsDbContext dbc, 5409 String siteRoot, 5410 String source, 5411 String target, 5412 CmsAliasMode mode) 5413 throws CmsException { 5414 5415 I_CmsVfsDriver vfs = getVfsDriver(dbc); 5416 List<CmsRewriteAlias> existingAliases = vfs.readRewriteAliases( 5417 dbc, 5418 new CmsRewriteAliasFilter().setSiteRoot(siteRoot)); 5419 CmsUUID idToDelete = null; 5420 for (CmsRewriteAlias alias : existingAliases) { 5421 if (alias.getPatternString().equals(source)) { 5422 idToDelete = alias.getId(); 5423 } 5424 } 5425 if (idToDelete != null) { 5426 vfs.deleteRewriteAliases(dbc, new CmsRewriteAliasFilter().setId(idToDelete)); 5427 } 5428 CmsRewriteAlias alias = new CmsRewriteAlias(new CmsUUID(), siteRoot, source, target, mode); 5429 List<CmsRewriteAlias> aliases = new ArrayList<CmsRewriteAlias>(); 5430 aliases.add(alias); 5431 getVfsDriver(dbc).insertRewriteAliases(dbc, aliases); 5432 CmsAliasImportResult result = new CmsAliasImportResult( 5433 CmsAliasImportStatus.aliasNew, 5434 "OK", 5435 source, 5436 target, 5437 mode); 5438 return result; 5439 } 5440 5441 /** 5442 * Creates a new user by import.<p> 5443 * 5444 * @param dbc the current database context 5445 * @param id the id of the user 5446 * @param name the new name for the user 5447 * @param password the new password for the user (already encrypted) 5448 * @param firstname the firstname of the user 5449 * @param lastname the lastname of the user 5450 * @param email the email of the user 5451 * @param flags the flags for a user (for example <code>{@link I_CmsPrincipal#FLAG_ENABLED}</code>) 5452 * @param dateCreated the creation date 5453 * @param additionalInfos the additional user infos 5454 * 5455 * @return the imported user 5456 * 5457 * @throws CmsException if something goes wrong 5458 */ 5459 public CmsUser importUser( 5460 CmsDbContext dbc, 5461 String id, 5462 String name, 5463 String password, 5464 String firstname, 5465 String lastname, 5466 String email, 5467 int flags, 5468 long dateCreated, 5469 Map<String, Object> additionalInfos) 5470 throws CmsException { 5471 5472 // no space before or after the name 5473 name = name.trim(); 5474 // check the user name 5475 String userName = CmsOrganizationalUnit.getSimpleName(name); 5476 OpenCms.getValidationHandler().checkUserName(userName); 5477 if (CmsStringUtil.isEmptyOrWhitespaceOnly(userName)) { 5478 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_USER_1, userName)); 5479 } 5480 // check the ou 5481 CmsOrganizationalUnit ou = readOrganizationalUnit(dbc, CmsOrganizationalUnit.getParentFqn(name)); 5482 5483 // check webuser ou 5484 if (ou.hasFlagWebuser() && ((flags & I_CmsPrincipal.FLAG_USER_WEBUSER) == 0)) { 5485 flags += I_CmsPrincipal.FLAG_USER_WEBUSER; 5486 } 5487 CmsUser newUser = getUserDriver(dbc).createUser( 5488 dbc, 5489 new CmsUUID(id), 5490 name, 5491 password, 5492 firstname, 5493 lastname, 5494 email, 5495 0, 5496 flags, 5497 dateCreated, 5498 additionalInfos); 5499 return newUser; 5500 } 5501 5502 /** 5503 * Increments a counter and returns its value before incrementing.<p> 5504 * 5505 * @param dbc the current database context 5506 * @param name the name of the counter which should be incremented 5507 * 5508 * @return the value of the counter 5509 * 5510 * @throws CmsException if something goes wrong 5511 */ 5512 public int incrementCounter(CmsDbContext dbc, String name) throws CmsException { 5513 5514 return getVfsDriver(dbc).incrementCounter(dbc, name); 5515 } 5516 5517 /** 5518 * Initializes the driver and sets up all required modules and connections.<p> 5519 * 5520 * @param configurationManager the configuration manager 5521 * @param dbContextFactory the db context factory 5522 * 5523 * @throws CmsException if something goes wrong 5524 * @throws Exception if something goes wrong 5525 */ 5526 public void init(CmsConfigurationManager configurationManager, I_CmsDbContextFactory dbContextFactory) 5527 throws CmsException, Exception { 5528 5529 // initialize the access-module. 5530 if (CmsLog.INIT.isInfoEnabled()) { 5531 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_MANAGER_START_PHASE4_0)); 5532 } 5533 // store local reference to the memory monitor to avoid multiple lookups through the OpenCms singelton 5534 m_monitor = OpenCms.getMemoryMonitor(); 5535 5536 CmsSystemConfiguration systemConfiguation = (CmsSystemConfiguration)configurationManager.getConfiguration( 5537 CmsSystemConfiguration.class); 5538 CmsCacheSettings settings = systemConfiguation.getCacheSettings(); 5539 5540 // initialize the key generator 5541 m_keyGenerator = (I_CmsCacheKey)Class.forName(settings.getCacheKeyGenerator()).newInstance(); 5542 5543 // initialize the HTML link validator 5544 m_htmlLinkValidator = new CmsRelationSystemValidator(this); 5545 5546 // fills the defaults if needed 5547 CmsDbContext dbc1 = dbContextFactory.getDbContext(); 5548 getUserDriver().fillDefaults(dbc1); 5549 getProjectDriver().fillDefaults(dbc1); 5550 5551 // set the driver manager in the publish engine 5552 m_publishEngine.setDriverManager(this); 5553 // create the root organizational unit if needed 5554 CmsDbContext dbc2 = dbContextFactory.getDbContext( 5555 new CmsRequestContext( 5556 readUser(dbc1, OpenCms.getDefaultUsers().getUserAdmin()), 5557 readProject(dbc1, CmsProject.ONLINE_PROJECT_ID), 5558 null, 5559 CmsSiteMatcher.DEFAULT_MATCHER, 5560 "", 5561 false, 5562 null, 5563 null, 5564 null, 5565 0, 5566 null, 5567 null, 5568 "", 5569 false)); 5570 dbc1.clear(); 5571 getUserDriver().createRootOrganizationalUnit(dbc2); 5572 dbc2.clear(); 5573 } 5574 5575 /** 5576 * Initializes the organizational unit.<p> 5577 * 5578 * @param dbc the DB context 5579 * @param ou the organizational unit 5580 */ 5581 public void initOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit ou) { 5582 5583 try { 5584 dbc.setAttribute(ATTR_INIT_OU, ou); 5585 m_userDriver.fillDefaults(dbc); 5586 } finally { 5587 dbc.removeAttribute(ATTR_INIT_OU); 5588 } 5589 } 5590 5591 /** 5592 * Checks if the specified resource is inside the current project.<p> 5593 * 5594 * The project "view" is determined by a set of path prefixes. 5595 * If the resource starts with any one of this prefixes, it is considered to 5596 * be "inside" the project.<p> 5597 * 5598 * @param dbc the current database context 5599 * @param resourcename the specified resource name (full path) 5600 * 5601 * @return <code>true</code>, if the specified resource is inside the current project 5602 */ 5603 public boolean isInsideCurrentProject(CmsDbContext dbc, String resourcename) { 5604 5605 List<String> projectResources = null; 5606 try { 5607 projectResources = readProjectResources(dbc, dbc.currentProject()); 5608 } catch (CmsException e) { 5609 if (LOG.isErrorEnabled()) { 5610 LOG.error( 5611 Messages.get().getBundle().key( 5612 Messages.LOG_CHECK_RESOURCE_INSIDE_CURRENT_PROJECT_2, 5613 resourcename, 5614 dbc.currentProject().getName()), 5615 e); 5616 } 5617 return false; 5618 } 5619 return CmsProject.isInsideProject(projectResources, resourcename); 5620 } 5621 5622 /** 5623 * Checks whether the subscription driver is available.<p> 5624 * 5625 * @return true if the subscription driver is available 5626 */ 5627 public boolean isSubscriptionDriverAvailable() { 5628 5629 return m_subscriptionDriver != null; 5630 } 5631 5632 /** 5633 * Checks if a project is the tempfile project.<p> 5634 * @param project the project to test 5635 * @return true if the project is the tempfile project 5636 */ 5637 public boolean isTempfileProject(CmsProject project) { 5638 5639 return project.getName().equals("tempFileProject"); 5640 } 5641 5642 /** 5643 * Checks if one of the resources (except the resource itself) 5644 * is a sibling in a "labeled" site folder.<p> 5645 * 5646 * This method is used when creating a new sibling 5647 * (use the <code>newResource</code> parameter & <code>action = 1</code>) 5648 * or deleting/importing a resource (call with <code>action = 2</code>).<p> 5649 * 5650 * @param dbc the current database context 5651 * @param resource the resource 5652 * @param newResource absolute path for a resource sibling which will be created 5653 * @param action the action which has to be performed (1: create VFS link, 2: all other actions) 5654 * 5655 * @return <code>true</code> if the flag should be set for the resource, otherwise <code>false</code> 5656 * 5657 * @throws CmsDataAccessException if something goes wrong 5658 */ 5659 public boolean labelResource(CmsDbContext dbc, CmsResource resource, String newResource, int action) 5660 throws CmsDataAccessException { 5661 5662 // get the list of labeled site folders from the runtime property 5663 List<String> labeledSites = OpenCms.getWorkplaceManager().getLabelSiteFolders(); 5664 5665 if (labeledSites.size() == 0) { 5666 // no labeled sites defined, just return false 5667 return false; 5668 } 5669 5670 if (action == 1) { 5671 // CASE 1: a new resource is created, check the sites 5672 if (!resource.isLabeled()) { 5673 // source isn't labeled yet, so check! 5674 boolean linkInside = false; 5675 boolean sourceInside = false; 5676 for (int i = 0; i < labeledSites.size(); i++) { 5677 String curSite = labeledSites.get(i); 5678 if (newResource.startsWith(curSite)) { 5679 // the link lies in a labeled site 5680 linkInside = true; 5681 } 5682 if (resource.getRootPath().startsWith(curSite)) { 5683 // the source lies in a labeled site 5684 sourceInside = true; 5685 } 5686 if (linkInside && sourceInside) { 5687 break; 5688 } 5689 } 5690 // return true when either source or link is in labeled site, otherwise false 5691 return (linkInside != sourceInside); 5692 } 5693 // resource is already labeled 5694 return false; 5695 5696 } else { 5697 // CASE 2: the resource will be deleted or created (import) 5698 // check if at least one of the other siblings resides inside a "labeled site" 5699 // and if at least one of the other siblings resides outside a "labeled site" 5700 boolean isInside = false; 5701 boolean isOutside = false; 5702 // check if one of the other vfs links lies in a labeled site folder 5703 List<CmsResource> siblings = getVfsDriver( 5704 dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, false); 5705 updateContextDates(dbc, siblings); 5706 Iterator<CmsResource> i = siblings.iterator(); 5707 while (i.hasNext() && (!isInside || !isOutside)) { 5708 CmsResource currentResource = i.next(); 5709 if (currentResource.equals(resource)) { 5710 // dont't check the resource itself! 5711 continue; 5712 } 5713 String curPath = currentResource.getRootPath(); 5714 boolean curInside = false; 5715 for (int k = 0; k < labeledSites.size(); k++) { 5716 if (curPath.startsWith(labeledSites.get(k))) { 5717 // the link is in the labeled site 5718 isInside = true; 5719 curInside = true; 5720 break; 5721 } 5722 } 5723 if (!curInside) { 5724 // the current link was not found in labeled site, so it is outside 5725 isOutside = true; 5726 } 5727 } 5728 // now check the new resource name if present 5729 if (newResource != null) { 5730 boolean curInside = false; 5731 for (int k = 0; k < labeledSites.size(); k++) { 5732 if (newResource.startsWith(labeledSites.get(k))) { 5733 // the new resource is in the labeled site 5734 isInside = true; 5735 curInside = true; 5736 break; 5737 } 5738 } 5739 if (!curInside) { 5740 // the new resource was not found in labeled site, so it is outside 5741 isOutside = true; 5742 } 5743 } 5744 return (isInside && isOutside); 5745 } 5746 } 5747 5748 /** 5749 * Returns the user, who had locked the resource.<p> 5750 * 5751 * A user can lock a resource, so he is the only one who can write this 5752 * resource. This methods checks, if a resource was locked. 5753 * 5754 * @param dbc the current database context 5755 * @param resource the resource 5756 * 5757 * @return the user, who had locked the resource 5758 * 5759 * @throws CmsException will be thrown, if the user has not the rights for this resource 5760 */ 5761 public CmsUser lockedBy(CmsDbContext dbc, CmsResource resource) throws CmsException { 5762 5763 return readUser(dbc, m_lockManager.getLock(dbc, resource).getEditionLock().getUserId()); 5764 } 5765 5766 /** 5767 * Locks a resource.<p> 5768 * 5769 * The <code>type</code> parameter controls what kind of lock is used.<br> 5770 * Possible values for this parameter are: <br> 5771 * <ul> 5772 * <li><code>{@link org.opencms.lock.CmsLockType#EXCLUSIVE}</code></li> 5773 * <li><code>{@link org.opencms.lock.CmsLockType#TEMPORARY}</code></li> 5774 * <li><code>{@link org.opencms.lock.CmsLockType#PUBLISH}</code></li> 5775 * </ul><p> 5776 * 5777 * @param dbc the current database context 5778 * @param resource the resource to lock 5779 * @param type type of the lock 5780 * 5781 * @throws CmsException if something goes wrong 5782 * 5783 * @see CmsObject#lockResource(String) 5784 * @see CmsObject#lockResourceTemporary(String) 5785 * @see org.opencms.file.types.I_CmsResourceType#lockResource(CmsObject, CmsSecurityManager, CmsResource, CmsLockType) 5786 */ 5787 public void lockResource(CmsDbContext dbc, CmsResource resource, CmsLockType type) throws CmsException { 5788 5789 // update the resource cache 5790 m_monitor.clearResourceCache(); 5791 5792 CmsProject project = dbc.currentProject(); 5793 5794 // add the resource to the lock dispatcher 5795 m_lockManager.addResource(dbc, resource, dbc.currentUser(), project, type); 5796 boolean changedProjectLastModified = false; 5797 if (!resource.getState().isUnchanged() && !resource.getState().isKeep()) { 5798 // update the project flag of a modified resource as "last modified inside the current project" 5799 getVfsDriver(dbc).writeLastModifiedProjectId(dbc, project, project.getUuid(), resource); 5800 changedProjectLastModified = true; 5801 } 5802 5803 // we must also clear the permission cache 5804 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION); 5805 5806 // fire resource modification event 5807 Map<String, Object> data = new HashMap<String, Object>(2); 5808 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 5809 data.put( 5810 I_CmsEventListener.KEY_CHANGE, 5811 Integer.valueOf(changedProjectLastModified ? CHANGED_PROJECT : NOTHING_CHANGED)); 5812 data.put(I_CmsEventListener.KEY_SKIPINDEX, Boolean.TRUE); 5813 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 5814 } 5815 5816 /** 5817 * Adds the given log entry to the current user's log.<p> 5818 * 5819 * This operation works only on memory, to get the log entries actually 5820 * written to DB you have to call the {@link #updateLog(CmsDbContext)} method.<p> 5821 * 5822 * @param dbc the current database context 5823 * @param logEntry the log entry to create 5824 * @param force forces the log entry to be counted, 5825 * if not only the first log entry in a transaction will be taken into account 5826 */ 5827 public void log(CmsDbContext dbc, CmsLogEntry logEntry, boolean force) { 5828 5829 if (dbc == null) { 5830 return; 5831 } 5832 // check log level 5833 if (!logEntry.getType().isActive()) { 5834 // do not log inactive entries 5835 return; 5836 } 5837 // if not forcing 5838 if (!force) { 5839 // operation already logged 5840 boolean abort = (dbc.getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null); 5841 // disabled logging from outside 5842 abort |= (dbc.getRequestContext().getAttribute(CmsLogEntry.ATTR_LOG_ENTRY) != null); 5843 if (abort) { 5844 return; 5845 } 5846 } 5847 // prevent several entries for the same operation 5848 dbc.setAttribute(CmsLogEntry.ATTR_LOG_ENTRY, Boolean.TRUE); 5849 // keep it for later 5850 m_log.add(logEntry); 5851 } 5852 5853 /** 5854 * Attempts to authenticate a user into OpenCms with the given password. 5855 * 5856 * <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, 5857 * while check mode merely checks the login details without firing the events normally fired during login, and without modifying the user. However, 5858 * in the case an incorrect password is given, the invalid login counter is still incremented. 5859 * 5860 * @param dbc the current database context 5861 * @param userName the name of the user to be logged in 5862 * @param password the password of the user 5863 * @param secondFactorInfo the second factor information for 2FA (may be null) 5864 * @param remoteAddress the ip address of the request 5865 * @param mode the mode to use (real login or check only) 5866 * 5867 * @return the logged in user 5868 * 5869 * @throws CmsAuthentificationException if the login was not successful 5870 * @throws CmsDataAccessException in case of errors accessing the database 5871 * @throws CmsPasswordEncryptionException in case of errors encrypting the users password 5872 */ 5873 public CmsUser loginUser( 5874 CmsDbContext dbc, 5875 String userName, 5876 String password, 5877 CmsSecondFactorInfo secondFactorInfo, 5878 String remoteAddress, 5879 LoginUserMode mode) 5880 throws CmsAuthentificationException, CmsDataAccessException, CmsPasswordEncryptionException { 5881 5882 if (CmsStringUtil.isEmptyOrWhitespaceOnly(password)) { 5883 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_USER_1, userName)); 5884 } 5885 CmsUser newUser; 5886 CmsUser userCopy; 5887 try { 5888 // read the user from the driver to avoid the cache 5889 newUser = getUserDriver(dbc).readUser(dbc, userName, password, remoteAddress); 5890 userCopy = newUser.clone(); 5891 userName = newUser.getName(); 5892 5893 } catch (CmsDbEntryNotFoundException e) { 5894 // this indicates that the username / password combination does not exist 5895 // any other exception indicates database issues, these are not catched here 5896 5897 // check if a user with this name exists at all 5898 CmsUser user = null; 5899 try { 5900 user = readUser(dbc, userName); 5901 userName = user.getName(); 5902 } catch (CmsDataAccessException e2) { 5903 // apparently this user does not exist in the database 5904 } 5905 5906 if (user != null) { 5907 if (dbc.currentUser().isGuestUser()) { 5908 // add an invalid login attempt for this user to the storage 5909 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 5910 } 5911 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 5912 throw new CmsAuthentificationException( 5913 org.opencms.security.Messages.get().container( 5914 org.opencms.security.Messages.ERR_LOGIN_FAILED_2, 5915 userName, 5916 remoteAddress), 5917 e); 5918 } else { 5919 String userOu = CmsOrganizationalUnit.getParentFqn(userName); 5920 if (userOu != null) { 5921 String parentOu = CmsOrganizationalUnit.getParentFqn(userOu); 5922 if (parentOu != null) { 5923 // try a higher level ou 5924 String uName = CmsOrganizationalUnit.getSimpleName(userName); 5925 return loginUser(dbc, parentOu + uName, password, secondFactorInfo, remoteAddress, mode); 5926 } 5927 } 5928 throw new CmsAuthentificationException( 5929 org.opencms.security.Messages.get().container( 5930 org.opencms.security.Messages.ERR_LOGIN_FAILED_NO_USER_2, 5931 userName, 5932 remoteAddress), 5933 e); 5934 } 5935 } 5936 // check if the "enabled" flag is set for the user 5937 if (!newUser.isEnabled()) { 5938 // user is disabled, throw a securiy exception 5939 throw new CmsAuthentificationException( 5940 org.opencms.security.Messages.get().container( 5941 org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2, 5942 userName, 5943 remoteAddress)); 5944 } 5945 5946 if (mode == LoginUserMode.standard) { 5947 CmsTwoFactorAuthenticationHandler handler = OpenCms.getTwoFactorAuthenticationHandler(); 5948 if (handler.needsTwoFactorAuthentication(newUser)) { 5949 // note that password check must already have been successful at this stage 5950 5951 if (handler.hasSecondFactor(newUser)) { 5952 if (!handler.verifySecondFactor(newUser, secondFactorInfo)) { 5953 if (dbc.currentUser().isGuestUser()) { 5954 // add an invalid login attempt for this user to the storage 5955 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 5956 } 5957 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 5958 throw new CmsAuthentificationException( 5959 org.opencms.security.Messages.get().container( 5960 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 5961 userName)); 5962 } 5963 } else { 5964 try { 5965 if (handler.setUpAndVerifySecondFactor(newUser, secondFactorInfo)) { 5966 LOG.info("Second factor setup successful for user " + newUser.getName()); 5967 } else { 5968 if (dbc.currentUser().isGuestUser()) { 5969 // add an invalid login attempt for this user to the storage 5970 OpenCms.getLoginManager().addInvalidLogin(userName, remoteAddress); 5971 } 5972 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 5973 throw new CmsAuthentificationException( 5974 org.opencms.security.Messages.get().container( 5975 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 5976 userName)); 5977 } 5978 } catch (CmsSecondFactorSetupException e) { 5979 throw new CmsAuthentificationException( 5980 org.opencms.security.Messages.get().container( 5981 org.opencms.security.Messages.ERR_VERIFICATION_FAILED_1, 5982 userName), 5983 e); 5984 } 5985 } 5986 } 5987 } 5988 if (dbc.currentUser().isGuestUser()) { 5989 // check if this account is temporarily disabled because of too many invalid login attempts 5990 // this will throw an exception if the test fails 5991 OpenCms.getLoginManager().checkInvalidLogins(userName, remoteAddress); 5992 if (mode == LoginUserMode.standard) { 5993 // test successful, remove all previous invalid login attempts for this user from the storage 5994 OpenCms.getLoginManager().removeInvalidLogins(userName, remoteAddress); 5995 } 5996 } 5997 5998 if (!m_securityManager.hasRole( 5999 dbc, 6000 newUser, 6001 CmsRole.ADMINISTRATOR.forOrgUnit(dbc.getRequestContext().getOuFqn()))) { 6002 // new user is not Administrator, check if login is currently allowed 6003 OpenCms.getLoginManager().checkLoginAllowed(); 6004 } 6005 6006 if (mode == LoginUserMode.standard) { 6007 6008 newUser.setLastlogin(System.currentTimeMillis()); 6009 m_monitor.clearUserCache(newUser); 6010 6011 // write the changed user object back to the user driver 6012 Map<String, Object> additionalInfosForRepositories = OpenCms.getRepositoryManager().getAdditionalInfoForLogin( 6013 newUser.getName(), 6014 password); 6015 boolean requiresAddInfoUpdate = false; 6016 6017 // check for changes 6018 for (Entry<String, Object> entry : additionalInfosForRepositories.entrySet()) { 6019 Object value = entry.getValue(); 6020 Object current = newUser.getAdditionalInfo(entry.getKey()); 6021 if (((value == null) && (current != null)) || ((value != null) && !value.equals(current))) { 6022 requiresAddInfoUpdate = true; 6023 break; 6024 } 6025 } 6026 if (requiresAddInfoUpdate) { 6027 newUser.getAdditionalInfo().putAll(additionalInfosForRepositories); 6028 } 6029 String lastPasswordChange = (String)newUser.getAdditionalInfo( 6030 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE); 6031 if (lastPasswordChange == null) { 6032 requiresAddInfoUpdate = true; 6033 newUser.getAdditionalInfo().put( 6034 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 6035 "" + System.currentTimeMillis()); 6036 } 6037 if (!requiresAddInfoUpdate) { 6038 dbc.setAttribute(ATTRIBUTE_LOGIN, newUser.getName()); 6039 } 6040 6041 if (mode == LoginUserMode.standard) { 6042 OpenCms.getTwoFactorAuthenticationHandler().trackUserChange(dbc.getRequestContext(), userCopy, newUser); 6043 getUserDriver(dbc).writeUser(dbc, newUser); 6044 } 6045 int changes = CmsUser.FLAG_LAST_LOGIN; 6046 6047 // check if we need to update the password 6048 if (!OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), false) 6049 && OpenCms.getPasswordHandler().checkPassword(password, newUser.getPassword(), true)) { 6050 // the password does not check with the current hash algorithm but with the fall back, update the password 6051 getUserDriver(dbc).writePassword(dbc, userName, password, password); 6052 changes = changes | CmsUser.FLAG_CORE_DATA; 6053 } 6054 6055 // update cache 6056 m_monitor.cacheUser(newUser); 6057 6058 // invalidate all user dependent caches 6059 m_monitor.flushCache( 6060 CmsMemoryMonitor.CacheType.ACL, 6061 CmsMemoryMonitor.CacheType.GROUP, 6062 CmsMemoryMonitor.CacheType.ORG_UNIT, 6063 CmsMemoryMonitor.CacheType.USER_LIST, 6064 CmsMemoryMonitor.CacheType.PERMISSION, 6065 CmsMemoryMonitor.CacheType.RESOURCE_LIST); 6066 6067 // fire user modified event 6068 Map<String, Object> eventData = new HashMap<String, Object>(); 6069 eventData.put(I_CmsEventListener.KEY_USER_ID, newUser.getId().toString()); 6070 eventData.put(I_CmsEventListener.KEY_USER_NAME, newUser.getName()); 6071 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 6072 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(changes)); 6073 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 6074 } 6075 6076 // return the user object read from the driver 6077 return newUser.clone(); 6078 } 6079 6080 /** 6081 * Lookup and read the user or group with the given UUID.<p> 6082 * 6083 * @param dbc the current database context 6084 * @param principalId the UUID of the principal to lookup 6085 * 6086 * @return the principal (group or user) if found, otherwise <code>null</code> 6087 */ 6088 public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, CmsUUID principalId) { 6089 6090 try { 6091 CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalId); 6092 if (group != null) { 6093 return group; 6094 } 6095 } catch (Exception e) { 6096 // ignore this exception 6097 } 6098 6099 try { 6100 CmsUser user = readUser(dbc, principalId); 6101 if (user != null) { 6102 return user; 6103 } 6104 } catch (Exception e) { 6105 // ignore this exception 6106 } 6107 6108 return null; 6109 } 6110 6111 /** 6112 * Lookup and read the user or group with the given name.<p> 6113 * 6114 * @param dbc the current database context 6115 * @param principalName the name of the principal to lookup 6116 * 6117 * @return the principal (group or user) if found, otherwise <code>null</code> 6118 */ 6119 public I_CmsPrincipal lookupPrincipal(CmsDbContext dbc, String principalName) { 6120 6121 try { 6122 CmsGroup group = getUserDriver(dbc).readGroup(dbc, principalName); 6123 if (group != null) { 6124 return group; 6125 } 6126 } catch (Exception e) { 6127 // ignore this exception 6128 } 6129 6130 try { 6131 CmsUser user = readUser(dbc, principalName); 6132 if (user != null) { 6133 return user; 6134 } 6135 } catch (Exception e) { 6136 // ignore this exception 6137 } 6138 6139 return null; 6140 } 6141 6142 /** 6143 * Mark the given resource as visited by the user.<p> 6144 * 6145 * @param dbc the database context 6146 * @param poolName the name of the database pool to use 6147 * @param resource the resource to mark as visited 6148 * @param user the user that visited the resource 6149 * 6150 * @throws CmsException if something goes wrong 6151 */ 6152 public void markResourceAsVisitedBy(CmsDbContext dbc, String poolName, CmsResource resource, CmsUser user) 6153 throws CmsException { 6154 6155 getSubscriptionDriver().markResourceAsVisitedBy(dbc, poolName, resource, user); 6156 } 6157 6158 /** 6159 * Moves a resource.<p> 6160 * 6161 * You must ensure that the parent of the destination path is an absolute, valid and 6162 * existing VFS path. Relative paths from the source are not supported.<p> 6163 * 6164 * The moved resource will always be locked to the current user 6165 * after the move operation.<p> 6166 * 6167 * In case the target resource already exists, it will be overwritten with the 6168 * source resource if possible.<p> 6169 * 6170 * @param dbc the current database context 6171 * @param source the resource to move 6172 * @param destination the name of the move destination with complete path 6173 * @param internal if set nothing more than the path is modified 6174 * 6175 * @throws CmsException if something goes wrong 6176 * 6177 * @see CmsSecurityManager#moveResource(CmsRequestContext, CmsResource, String) 6178 */ 6179 public void moveResource(CmsDbContext dbc, CmsResource source, String destination, boolean internal) 6180 throws CmsException { 6181 6182 CmsFolder destinationFolder = readFolder(dbc, CmsResource.getParentFolder(destination), CmsResourceFilter.ALL); 6183 m_securityManager.checkPermissions( 6184 dbc, 6185 destinationFolder, 6186 CmsPermissionSet.ACCESS_WRITE, 6187 false, 6188 CmsResourceFilter.ALL); 6189 6190 if (source.isFolder()) { 6191 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 6192 } 6193 getVfsDriver(dbc).moveResource(dbc, dbc.getRequestContext().getCurrentProject().getUuid(), source, destination); 6194 6195 if (!internal) { 6196 CmsResourceState newState = CmsResource.STATE_CHANGED; 6197 if (source.getState().isNew()) { 6198 newState = CmsResource.STATE_NEW; 6199 } else if (source.getState().isDeleted()) { 6200 newState = CmsResource.STATE_DELETED; 6201 } 6202 source.setState(newState); 6203 // safe since this operation always uses the ids instead of the resource path 6204 getVfsDriver(dbc).writeResourceState( 6205 dbc, 6206 dbc.currentProject(), 6207 source, 6208 CmsDriverManager.UPDATE_STRUCTURE_STATE, 6209 false); 6210 // log it 6211 log( 6212 dbc, 6213 new CmsLogEntry( 6214 dbc, 6215 source.getStructureId(), 6216 CmsLogEntryType.RESOURCE_MOVED, 6217 new String[] {source.getRootPath(), destination}), 6218 false); 6219 } 6220 6221 CmsResource destRes = readResource(dbc, destination, CmsResourceFilter.ALL); 6222 // move lock 6223 m_lockManager.moveResource(source.getRootPath(), destRes.getRootPath()); 6224 6225 // flush all relevant caches 6226 m_monitor.clearAccessControlListCache(); 6227 m_monitor.flushCache( 6228 CmsMemoryMonitor.CacheType.PROPERTY, 6229 CmsMemoryMonitor.CacheType.PROPERTY_LIST, 6230 CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 6231 6232 List<CmsResource> resources = new ArrayList<CmsResource>(4); 6233 // source 6234 resources.add(source); 6235 try { 6236 resources.add(readFolder(dbc, CmsResource.getParentFolder(source.getRootPath()), CmsResourceFilter.ALL)); 6237 } catch (Exception e) { 6238 if (LOG.isDebugEnabled()) { 6239 LOG.debug(e.getLocalizedMessage(), e); 6240 } 6241 } 6242 // destination 6243 resources.add(destRes); 6244 resources.add(destinationFolder); 6245 6246 Map<String, Object> eventData = new HashMap<String, Object>(); 6247 eventData.put(I_CmsEventListener.KEY_RESOURCES, resources); 6248 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 6249 6250 // fire the events 6251 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MOVED, eventData)); 6252 } 6253 6254 /** 6255 * Moves a resource to the "lost and found" folder.<p> 6256 * 6257 * The method can also be used to check get the name of a resource 6258 * in the "lost and found" folder only without actually moving the 6259 * the resource. To do this, the <code>returnNameOnly</code> flag 6260 * must be set to <code>true</code>.<p> 6261 * 6262 * @param dbc the current database context 6263 * @param resource the resource to apply this operation to 6264 * @param returnNameOnly if <code>true</code>, only the name of the resource in the "lost and found" 6265 * folder is returned, the move operation is not really performed 6266 * 6267 * @return the name of the resource inside the "lost and found" folder 6268 * 6269 * @throws CmsException if something goes wrong 6270 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 6271 * 6272 * @see CmsObject#moveToLostAndFound(String) 6273 * @see CmsObject#getLostAndFoundName(String) 6274 */ 6275 public String moveToLostAndFound(CmsDbContext dbc, CmsResource resource, boolean returnNameOnly) 6276 throws CmsException, CmsIllegalArgumentException { 6277 6278 String resourcename = dbc.removeSiteRoot(resource.getRootPath()); 6279 6280 String siteRoot = dbc.getRequestContext().getSiteRoot(); 6281 dbc.getRequestContext().setSiteRoot(""); 6282 String destination = CmsDriverManager.LOST_AND_FOUND_FOLDER + resourcename; 6283 // create the required folders if necessary 6284 try { 6285 // collect all folders... 6286 String folderPath = CmsResource.getParentFolder(destination); 6287 folderPath = folderPath.substring(1, folderPath.length() - 1); // cut out leading and trailing '/' 6288 Iterator<String> folders = CmsStringUtil.splitAsList(folderPath, '/').iterator(); 6289 // ...now create them.... 6290 folderPath = "/"; 6291 while (folders.hasNext()) { 6292 folderPath += folders.next().toString() + "/"; 6293 try { 6294 readFolder(dbc, folderPath, CmsResourceFilter.IGNORE_EXPIRATION); 6295 } catch (Exception e1) { 6296 if (returnNameOnly) { 6297 // we can use the original name without risk, and we do not need to recreate the parent folders 6298 break; 6299 } 6300 // the folder is not existing, so create it 6301 createResource( 6302 dbc, 6303 folderPath, 6304 CmsResourceTypeFolder.RESOURCE_TYPE_ID, 6305 null, 6306 new ArrayList<CmsProperty>()); 6307 } 6308 } 6309 // check if this resource name does already exist 6310 // if so add a postfix to the name 6311 String des = destination; 6312 int postfix = 1; 6313 boolean found = true; 6314 while (found) { 6315 try { 6316 // try to read the file..... 6317 found = true; 6318 readResource(dbc, des, CmsResourceFilter.ALL); 6319 // ....it's there, so add a postfix and try again 6320 String path = destination.substring(0, destination.lastIndexOf('/') + 1); 6321 String filename = destination.substring(destination.lastIndexOf('/') + 1, destination.length()); 6322 6323 des = path; 6324 6325 if (filename.lastIndexOf('.') > 0) { 6326 des += filename.substring(0, filename.lastIndexOf('.')); 6327 } else { 6328 des += filename; 6329 } 6330 des += "_" + postfix; 6331 if (filename.lastIndexOf('.') > 0) { 6332 des += filename.substring(filename.lastIndexOf('.'), filename.length()); 6333 } 6334 postfix++; 6335 } catch (CmsException e3) { 6336 // the file does not exist, so we can use this filename 6337 found = false; 6338 } 6339 } 6340 destination = des; 6341 6342 if (!returnNameOnly) { 6343 // do not use the move semantic here! to prevent links pointing to the lost & found folder 6344 copyResource(dbc, resource, destination, CmsResource.COPY_AS_SIBLING); 6345 deleteResource(dbc, resource, CmsResource.DELETE_PRESERVE_SIBLINGS); 6346 } 6347 } catch (CmsException e2) { 6348 throw e2; 6349 } finally { 6350 // set the site root to the old value again 6351 dbc.getRequestContext().setSiteRoot(siteRoot); 6352 } 6353 return destination; 6354 } 6355 6356 /** 6357 * Gets a new driver instance.<p> 6358 * 6359 * @param dbc the database context 6360 * @param configurationManager the configuration manager 6361 * @param driverName the driver name 6362 * @param successiveDrivers the list of successive drivers 6363 * 6364 * @return the driver object 6365 * @throws CmsInitException if the selected driver could not be initialized 6366 */ 6367 public Object newDriverInstance( 6368 CmsDbContext dbc, 6369 CmsConfigurationManager configurationManager, 6370 String driverName, 6371 List<String> successiveDrivers) 6372 throws CmsInitException { 6373 6374 Class<?> driverClass = null; 6375 I_CmsDriver driver = null; 6376 6377 try { 6378 // try to get the class 6379 driverClass = Class.forName(driverName); 6380 if (CmsLog.INIT.isInfoEnabled()) { 6381 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName)); 6382 } 6383 6384 // try to create a instance 6385 driver = (I_CmsDriver)driverClass.newInstance(); 6386 if (CmsLog.INIT.isInfoEnabled()) { 6387 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName)); 6388 } 6389 6390 // invoke the init-method of this access class 6391 driver.init(dbc, configurationManager, successiveDrivers, this); 6392 if (CmsLog.INIT.isInfoEnabled()) { 6393 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_0)); 6394 } 6395 6396 } catch (Throwable t) { 6397 CmsMessageContainer message = Messages.get().container( 6398 Messages.ERR_ERROR_INITIALIZING_DRIVER_1, 6399 driverName); 6400 if (LOG.isErrorEnabled()) { 6401 LOG.error(message.key(), t); 6402 } 6403 throw new CmsInitException(message, t); 6404 } 6405 6406 return driver; 6407 } 6408 6409 /** 6410 * Method to create a new instance of a driver.<p> 6411 * 6412 * @param configuration the configurations from the propertyfile 6413 * @param driverName the class name of the driver 6414 * @param driverPoolUrl the pool url for the driver 6415 * @return an initialized instance of the driver 6416 * @throws CmsException if something goes wrong 6417 */ 6418 public Object newDriverInstance(CmsParameterConfiguration configuration, String driverName, String driverPoolUrl) 6419 throws CmsException { 6420 6421 Class<?>[] initParamClasses = {CmsParameterConfiguration.class, String.class, CmsDriverManager.class}; 6422 Object[] initParams = {configuration, driverPoolUrl, this}; 6423 6424 Class<?> driverClass = null; 6425 Object driver = null; 6426 6427 try { 6428 // try to get the class 6429 driverClass = Class.forName(driverName); 6430 if (CmsLog.INIT.isInfoEnabled()) { 6431 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_START_1, driverName)); 6432 } 6433 6434 // try to create a instance 6435 driver = driverClass.newInstance(); 6436 if (CmsLog.INIT.isInfoEnabled()) { 6437 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INITIALIZING_1, driverName)); 6438 } 6439 6440 // invoke the init-method of this access class 6441 driver.getClass().getMethod("init", initParamClasses).invoke(driver, initParams); 6442 if (CmsLog.INIT.isInfoEnabled()) { 6443 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_INIT_FINISHED_1, driverPoolUrl)); 6444 } 6445 6446 } catch (Exception exc) { 6447 6448 CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_DRIVER_MANAGER_1); 6449 if (LOG.isFatalEnabled()) { 6450 LOG.fatal(message.key(), exc); 6451 } 6452 throw new CmsDbException(message, exc); 6453 6454 } 6455 6456 return driver; 6457 } 6458 6459 /** 6460 * Method to create a new instance of a pool.<p> 6461 * 6462 * @param configuration the configurations from the propertyfile 6463 * @param poolName the configuration name of the pool 6464 * 6465 * @throws CmsInitException if the pools could not be initialized 6466 */ 6467 public void newPoolInstance(CmsParameterConfiguration configuration, String poolName) throws CmsInitException { 6468 6469 CmsDbPoolV11 pool; 6470 6471 try { 6472 pool = new CmsDbPoolV11(configuration, poolName); 6473 } catch (Exception e) { 6474 6475 CmsMessageContainer message = Messages.get().container(Messages.ERR_INIT_CONN_POOL_1, poolName); 6476 if (LOG.isErrorEnabled()) { 6477 LOG.error(message.key(), e); 6478 } 6479 throw new CmsInitException(message, e); 6480 } 6481 addPool(pool); 6482 } 6483 6484 /** 6485 * Publishes the given publish job.<p> 6486 * 6487 * @param cms the cms context 6488 * @param dbc the db context 6489 * @param publishList the list of resources to publish 6490 * @param report the report to write to 6491 * 6492 * @throws CmsException if something goes wrong 6493 */ 6494 public void publishJob(CmsObject cms, CmsDbContext dbc, CmsPublishList publishList, I_CmsReport report) 6495 throws CmsException { 6496 6497 try { 6498 // check state and lock 6499 List<CmsResource> allResources = new ArrayList<CmsResource>(publishList.getFolderList()); 6500 allResources.addAll(publishList.getDeletedFolderList()); 6501 allResources.addAll(publishList.getFileList()); 6502 Iterator<CmsResource> itResources = allResources.iterator(); 6503 while (itResources.hasNext()) { 6504 CmsResource resource = itResources.next(); 6505 try { 6506 resource = readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL); 6507 } catch (CmsVfsResourceNotFoundException e) { 6508 continue; 6509 } 6510 if (resource.getState().isUnchanged()) { 6511 // remove files that were published by a concurrent job 6512 if (LOG.isDebugEnabled()) { 6513 LOG.debug( 6514 Messages.get().getBundle().key( 6515 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6516 dbc.removeSiteRoot(resource.getRootPath()))); 6517 } 6518 publishList.remove(resource); 6519 unlockResource(dbc, resource, true, true); 6520 continue; 6521 } 6522 if (!CmsModificationContext.isInOnlineFolder(resource.getRootPath())) { 6523 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6524 if (!lock.getSystemLock().isPublish()) { 6525 // remove files that are not locked for publishing 6526 if (LOG.isDebugEnabled()) { 6527 LOG.debug( 6528 Messages.get().getBundle().key( 6529 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6530 dbc.removeSiteRoot(resource.getRootPath()))); 6531 } 6532 publishList.remove(resource); 6533 continue; 6534 } 6535 } 6536 } 6537 6538 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 6539 6540 // clear the cache 6541 m_monitor.clearCacheForPublishing(); 6542 6543 int publishTag = getNextPublishTag(dbc); 6544 getProjectDriver(dbc).publishProject(dbc, report, onlineProject, publishList, publishTag); 6545 6546 // iterate the initialized module action instances 6547 Iterator<String> i = OpenCms.getModuleManager().getModuleNames().iterator(); 6548 while (i.hasNext()) { 6549 CmsModule module = OpenCms.getModuleManager().getModule(i.next()); 6550 if ((module != null) && (module.getActionInstance() != null)) { 6551 module.getActionInstance().publishProject(cms, publishList, publishTag, report); 6552 } 6553 } 6554 6555 boolean temporaryProject = (cms.getRequestContext().getCurrentProject().getType() == CmsProject.PROJECT_TYPE_TEMPORARY); 6556 // the project was stored in the history tables for history 6557 // it will be deleted if the project_flag is PROJECT_TYPE_TEMPORARY 6558 if ((temporaryProject) && (!publishList.isDirectPublish())) { 6559 try { 6560 getProjectDriver(dbc).deleteProject(dbc, dbc.currentProject()); 6561 } catch (CmsException e) { 6562 LOG.error( 6563 Messages.get().getBundle().key( 6564 Messages.LOG_DELETE_TEMP_PROJECT_FAILED_1, 6565 cms.getRequestContext().getCurrentProject().getName())); 6566 } 6567 // if project was temporary set context to online project 6568 cms.getRequestContext().setCurrentProject(onlineProject); 6569 } 6570 } finally { 6571 // clear the cache again 6572 m_monitor.clearCacheForPublishing(); 6573 } 6574 } 6575 6576 /** 6577 * Publishes the resources of a specified publish list.<p> 6578 * 6579 * @param cms the current request context 6580 * @param dbc the current database context 6581 * @param publishList a publish list 6582 * @param report an instance of <code>{@link I_CmsReport}</code> to print messages 6583 * 6584 * @throws CmsException if something goes wrong 6585 * 6586 * @see #fillPublishList(CmsDbContext, CmsPublishList) 6587 */ 6588 public synchronized void publishProject( 6589 CmsObject cms, 6590 CmsDbContext dbc, 6591 CmsPublishList publishList, 6592 I_CmsReport report) 6593 throws CmsException { 6594 6595 // check the parent folders 6596 checkParentFolders(dbc, publishList); 6597 ensureSubResourcesOfMovedFoldersPublished(cms, dbc, publishList); 6598 OpenCms.getPublishManager().getPublishListVerifier().checkPublishList(publishList); 6599 6600 try { 6601 // fire an event that a project is to be published 6602 Map<String, Object> eventData = new HashMap<String, Object>(); 6603 eventData.put(I_CmsEventListener.KEY_REPORT, report); 6604 eventData.put(I_CmsEventListener.KEY_PUBLISHLIST, publishList); 6605 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 6606 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 6607 CmsEvent beforePublishEvent = new CmsEvent(I_CmsEventListener.EVENT_BEFORE_PUBLISH_PROJECT, eventData); 6608 OpenCms.fireCmsEvent(beforePublishEvent); 6609 } catch (Throwable t) { 6610 if (report != null) { 6611 report.addError(t); 6612 report.println(t); 6613 } 6614 if (LOG.isErrorEnabled()) { 6615 LOG.error(t.getLocalizedMessage(), t); 6616 } 6617 } 6618 6619 // lock all resources with the special publish lock 6620 Iterator<CmsResource> itResources = new ArrayList<CmsResource>(publishList.getAllResources()).iterator(); 6621 while (itResources.hasNext()) { 6622 CmsResource resource = itResources.next(); 6623 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6624 if (lock.getSystemLock().isUnlocked() && lock.isLockableBy(dbc.currentUser())) { 6625 if (getLock(dbc, resource).getEditionLock().isNullLock()) { 6626 lockResource(dbc, resource, CmsLockType.PUBLISH); 6627 } else { 6628 changeLock(dbc, resource, CmsLockType.PUBLISH); 6629 } 6630 } else if (lock.getSystemLock().isPublish()) { 6631 if (LOG.isWarnEnabled()) { 6632 LOG.warn( 6633 Messages.get().getBundle().key( 6634 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6635 dbc.removeSiteRoot(resource.getRootPath()))); 6636 } 6637 // remove files that are already waiting to be published 6638 publishList.remove(resource); 6639 continue; 6640 } else { 6641 // this is needed to fix TestPublishIsssues#testPublishScenarioE 6642 changeLock(dbc, resource, CmsLockType.PUBLISH); 6643 } 6644 // now re-check the lock state 6645 lock = m_lockManager.getLock(dbc, resource, false); 6646 if (!lock.getSystemLock().isPublish()) { 6647 if (report != null) { 6648 report.println( 6649 Messages.get().container( 6650 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6651 dbc.removeSiteRoot(resource.getRootPath())), 6652 I_CmsReport.FORMAT_WARNING); 6653 } 6654 if (LOG.isWarnEnabled()) { 6655 LOG.warn( 6656 Messages.get().getBundle().key( 6657 Messages.RPT_PUBLISH_REMOVED_RESOURCE_1, 6658 dbc.removeSiteRoot(resource.getRootPath()))); 6659 } 6660 // remove files that could not be locked 6661 publishList.remove(resource); 6662 } 6663 } 6664 6665 // enqueue the publish job 6666 CmsException enqueueException = null; 6667 try { 6668 m_publishEngine.enqueuePublishJob(cms, publishList, report); 6669 } catch (CmsException exc) { 6670 enqueueException = exc; 6671 } 6672 6673 // if an exception was raised, remove the publish locks 6674 // and throw the exception again 6675 if (enqueueException != null) { 6676 itResources = publishList.getAllResources().iterator(); 6677 while (itResources.hasNext()) { 6678 CmsResource resource = itResources.next(); 6679 CmsLock lock = m_lockManager.getLock(dbc, resource, false); 6680 if (lock.getSystemLock().isPublish() 6681 && lock.getSystemLock().isOwnedInProjectBy( 6682 cms.getRequestContext().getCurrentUser(), 6683 cms.getRequestContext().getCurrentProject())) { 6684 unlockResource(dbc, resource, true, true); 6685 } 6686 } 6687 6688 throw enqueueException; 6689 } 6690 } 6691 6692 /** 6693 * Transfers the new URL name mappings (if any) for a given resource to the online project.<p> 6694 * 6695 * @param dbc the current database context 6696 * @param res the resource whose new URL name mappings should be transferred to the online project 6697 * 6698 * @throws CmsDataAccessException if something goes wrong 6699 */ 6700 public void publishUrlNameMapping(CmsDbContext dbc, CmsResource res) throws CmsDataAccessException { 6701 6702 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 6703 6704 if (res.getState().isDeleted()) { 6705 // remove both offline and online mappings 6706 CmsUrlNameMappingFilter idFilter = CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()); 6707 vfsDriver.deleteUrlNameMappingEntries(dbc, true, idFilter); 6708 vfsDriver.deleteUrlNameMappingEntries(dbc, false, idFilter); 6709 } else { 6710 // copy the new entries to the online table 6711 List<CmsUrlNameMappingEntry> entries = vfsDriver.readUrlNameMappingEntries( 6712 dbc, 6713 false, 6714 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates( 6715 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 6716 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 6717 6718 boolean isReplaceOnPublish = false; 6719 for (CmsUrlNameMappingEntry entry : entries) { 6720 isReplaceOnPublish |= entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH; 6721 } 6722 6723 if (!entries.isEmpty()) { 6724 6725 long now = System.currentTimeMillis(); 6726 if (isReplaceOnPublish) { 6727 vfsDriver.deleteUrlNameMappingEntries( 6728 dbc, 6729 true, 6730 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId())); 6731 vfsDriver.deleteUrlNameMappingEntries( 6732 dbc, 6733 false, 6734 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId())); 6735 } 6736 6737 for (CmsUrlNameMappingEntry entry : entries) { 6738 CmsUrlNameMappingFilter nameFilter = CmsUrlNameMappingFilter.ALL.filterName(entry.getName()); 6739 if (!isReplaceOnPublish) { // we already handled the other case above 6740 vfsDriver.deleteUrlNameMappingEntries(dbc, true, nameFilter); 6741 vfsDriver.deleteUrlNameMappingEntries(dbc, false, nameFilter); 6742 } 6743 } 6744 for (CmsUrlNameMappingEntry entry : entries) { 6745 CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry( 6746 entry.getName(), 6747 entry.getStructureId(), 6748 entry.getState() == CmsUrlNameMappingEntry.MAPPING_STATUS_NEW 6749 ? CmsUrlNameMappingEntry.MAPPING_STATUS_PUBLISHED 6750 : CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH_PUBLISHED, 6751 now, 6752 entry.getLocale()); 6753 vfsDriver.addUrlNameMappingEntry(dbc, true, newEntry); 6754 vfsDriver.addUrlNameMappingEntry(dbc, false, newEntry); 6755 } 6756 } 6757 } 6758 } 6759 6760 /** 6761 * Reads an access control entry from the cms.<p> 6762 * 6763 * The access control entries of a resource are readable by everyone. 6764 * 6765 * @param dbc the current database context 6766 * @param resource the resource 6767 * @param principal the id of a group or a user any other entity 6768 * @return an access control entry that defines the permissions of the entity for the given resource 6769 * @throws CmsException if something goes wrong 6770 */ 6771 public CmsAccessControlEntry readAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) 6772 throws CmsException { 6773 6774 return getUserDriver( 6775 dbc).readAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal); 6776 } 6777 6778 /** 6779 * Finds the alias with a given path.<p> 6780 * 6781 * If no alias is found, null is returned.<p> 6782 * 6783 * @param dbc the current database context 6784 * @param project the current project 6785 * @param siteRoot the site root 6786 * @param path the path of the alias 6787 * 6788 * @return the alias with the given path 6789 * 6790 * @throws CmsException if something goes wrong 6791 */ 6792 6793 public CmsAlias readAliasByPath(CmsDbContext dbc, CmsProject project, String siteRoot, String path) 6794 throws CmsException { 6795 6796 List<CmsAlias> aliases = getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(siteRoot, path, null)); 6797 if (aliases.isEmpty()) { 6798 return null; 6799 } else { 6800 return aliases.get(0); 6801 } 6802 } 6803 6804 /** 6805 * Reads the aliases for a given site root.<p> 6806 * 6807 * @param dbc the current database context 6808 * @param currentProject the current project 6809 * @param siteRoot the site root 6810 * 6811 * @return the list of aliases for the given site root 6812 * 6813 * @throws CmsException if something goes wrong 6814 */ 6815 public List<CmsAlias> readAliasesBySite(CmsDbContext dbc, CmsProject currentProject, String siteRoot) 6816 throws CmsException { 6817 6818 return getVfsDriver(dbc).readAliases(dbc, currentProject, new CmsAliasFilter(siteRoot, null, null)); 6819 } 6820 6821 /** 6822 * Reads the aliases which point to a given structure id.<p> 6823 * 6824 * @param dbc the current database context 6825 * @param project the current project 6826 * @param structureId the structure id for which we want to read the aliases 6827 * 6828 * @return the list of aliases pointing to the structure id 6829 * @throws CmsException if something goes wrong 6830 */ 6831 public List<CmsAlias> readAliasesByStructureId(CmsDbContext dbc, CmsProject project, CmsUUID structureId) 6832 throws CmsException { 6833 6834 return getVfsDriver(dbc).readAliases(dbc, project, new CmsAliasFilter(null, null, structureId)); 6835 } 6836 6837 /** 6838 * Reads all versions of the given resource.<br> 6839 * 6840 * This method returns a list with the history of the given resource, i.e. 6841 * the historical resource entries, independent of the project they were attached to.<br> 6842 * 6843 * The reading excludes the file content.<p> 6844 * 6845 * @param dbc the current database context 6846 * @param resource the resource to read the history for 6847 * 6848 * @return a list of file headers, as <code>{@link I_CmsHistoryResource}</code> objects 6849 * 6850 * @throws CmsException if something goes wrong 6851 */ 6852 public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsResource resource) 6853 throws CmsException { 6854 6855 // read the historical resources 6856 List<I_CmsHistoryResource> versions = getHistoryDriver(dbc).readAllAvailableVersions( 6857 dbc, 6858 resource.getStructureId()); 6859 if ((versions.size() > OpenCms.getSystemInfo().getHistoryVersions()) 6860 && (OpenCms.getSystemInfo().getHistoryVersions() > -1)) { 6861 return versions.subList(0, OpenCms.getSystemInfo().getHistoryVersions()); 6862 } 6863 return versions; 6864 } 6865 6866 /** 6867 * Reads all property definitions for the given mapping type.<p> 6868 * 6869 * @param dbc the current database context 6870 * 6871 * @return a list with the <code>{@link CmsPropertyDefinition}</code> objects (may be empty) 6872 * 6873 * @throws CmsException if something goes wrong 6874 */ 6875 public List<CmsPropertyDefinition> readAllPropertyDefinitions(CmsDbContext dbc) throws CmsException { 6876 6877 List<CmsPropertyDefinition> result = getVfsDriver(dbc).readPropertyDefinitions( 6878 dbc, 6879 dbc.currentProject().getUuid()); 6880 Collections.sort(result); 6881 return result; 6882 } 6883 6884 /** 6885 * Returns all resources subscribed by the given user or group.<p> 6886 * 6887 * @param dbc the database context 6888 * @param poolName the name of the database pool to use 6889 * @param principal the principal to read the subscribed resources 6890 * 6891 * @return all resources subscribed by the given user or group 6892 * 6893 * @throws CmsException if something goes wrong 6894 */ 6895 public List<CmsResource> readAllSubscribedResources(CmsDbContext dbc, String poolName, CmsPrincipal principal) 6896 throws CmsException { 6897 6898 List<CmsResource> result = getSubscriptionDriver().readAllSubscribedResources(dbc, poolName, principal); 6899 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 6900 return result; 6901 } 6902 6903 /** 6904 * Selects the best url name for a given resource and locale.<p> 6905 * 6906 * @param dbc the database context 6907 * @param id the resource's structure id 6908 * @param locale the requested locale 6909 * @param defaultLocales the default locales to use if the locale isn't available 6910 * 6911 * @return the URL name which was found 6912 * 6913 * @throws CmsDataAccessException if the database operation failed 6914 */ 6915 public String readBestUrlName(CmsDbContext dbc, CmsUUID id, Locale locale, List<Locale> defaultLocales) 6916 throws CmsDataAccessException { 6917 6918 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 6919 dbc, 6920 dbc.currentProject().isOnlineProject(), 6921 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 6922 if (entries.isEmpty()) { 6923 return null; 6924 } 6925 6926 ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create(); 6927 for (CmsUrlNameMappingEntry entry : entries) { 6928 entriesByLocale.put(entry.getLocale(), entry); 6929 } 6930 List<CmsUrlNameMappingEntry> lastEntries = new ArrayList<CmsUrlNameMappingEntry>(); 6931 Comparator<CmsUrlNameMappingEntry> dateChangedComparator = new UrlNameMappingComparator(); 6932 for (String localeKey : entriesByLocale.keySet()) { 6933 // for each locale select the latest mapping entry 6934 CmsUrlNameMappingEntry latestEntryForLocale = Collections.max( 6935 entriesByLocale.get(localeKey), 6936 dateChangedComparator); 6937 lastEntries.add(latestEntryForLocale); 6938 } 6939 CmsLocaleManager localeManager = OpenCms.getLocaleManager(); 6940 List<Locale> availableLocales = new ArrayList<Locale>(); 6941 for (CmsUrlNameMappingEntry entry : lastEntries) { 6942 availableLocales.add(CmsLocaleManager.getLocale(entry.getLocale())); 6943 } 6944 Locale bestLocale = localeManager.getBestMatchingLocale(locale, defaultLocales, availableLocales); 6945 String bestLocaleStr = bestLocale.toString(); 6946 for (CmsUrlNameMappingEntry entry : lastEntries) { 6947 if (entry.getLocale().equals(bestLocaleStr)) { 6948 return entry.getName(); 6949 } 6950 } 6951 return null; 6952 } 6953 6954 /** 6955 * Returns the child resources of a resource, that is the resources 6956 * contained in a folder.<p> 6957 * 6958 * With the parameters <code>getFolders</code> and <code>getFiles</code> 6959 * you can control what type of resources you want in the result list: 6960 * files, folders, or both.<p> 6961 * 6962 * This method is mainly used by the workplace explorer.<p> 6963 * 6964 * @param dbc the current database context 6965 * @param resource the resource to return the child resources for 6966 * @param filter the resource filter to use 6967 * @param getFolders if true the child folders are included in the result 6968 * @param getFiles if true the child files are included in the result 6969 * @param checkPermissions if the resources should be filtered with the current user permissions 6970 * 6971 * @return a list of all child resources 6972 * 6973 * @throws CmsException if something goes wrong 6974 */ 6975 public List<CmsResource> readChildResources( 6976 CmsDbContext dbc, 6977 CmsResource resource, 6978 CmsResourceFilter filter, 6979 boolean getFolders, 6980 boolean getFiles, 6981 boolean checkPermissions) 6982 throws CmsException { 6983 6984 String cacheKey = null; 6985 List<CmsResource> resourceList = null; 6986 if (m_monitor.isEnabled(CmsMemoryMonitor.CacheType.RESOURCE_LIST)) { // check this here to skip the complex cache key generation 6987 String time = ""; 6988 if (checkPermissions) { 6989 // ensure correct caching if site time offset is set 6990 if ((dbc.getRequestContext() != null) 6991 && (OpenCms.getSiteManager().getSiteForSiteRoot(dbc.getRequestContext().getSiteRoot()) != null)) { 6992 time += OpenCms.getSiteManager().getSiteForSiteRoot( 6993 dbc.getRequestContext().getSiteRoot()).getSiteMatcher().getTimeOffset(); 6994 } 6995 } 6996 // try to get the sub resources from the cache 6997 cacheKey = getCacheKey( 6998 new String[] { 6999 dbc.currentUser().getName(), 7000 getFolders 7001 ? (getFiles ? CmsCacheKey.CACHE_KEY_SUBALL : CmsCacheKey.CACHE_KEY_SUBFOLDERS) 7002 : CmsCacheKey.CACHE_KEY_SUBFILES, 7003 checkPermissions ? "+" + time : "-", 7004 filter.getCacheId(), 7005 resource.getRootPath()}, 7006 dbc); 7007 7008 resourceList = m_monitor.getCachedResourceList(cacheKey); 7009 } 7010 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 7011 // read the result form the database 7012 resourceList = getVfsDriver( 7013 dbc).readChildResources(dbc, dbc.currentProject(), resource, getFolders, getFiles); 7014 7015 if (checkPermissions) { 7016 // apply the permission filter 7017 resourceList = filterPermissions(dbc, resourceList, filter); 7018 } 7019 // cache the sub resources 7020 if (dbc.getProjectId().isNullUUID()) { 7021 m_monitor.cacheResourceList(cacheKey, resourceList); 7022 } 7023 } 7024 7025 // we must always apply the result filter and update the context dates 7026 return updateContextDates(dbc, resourceList, filter); 7027 } 7028 7029 /** 7030 * Returns the default file for the given folder.<p> 7031 * 7032 * If the given resource is a file, then this file is returned.<p> 7033 * 7034 * Otherwise, in case of a folder:<br> 7035 * <ol> 7036 * <li>the {@link CmsPropertyDefinition#PROPERTY_DEFAULT_FILE} is checked, and 7037 * <li>if still no file could be found, the configured default files in the 7038 * <code>opencms-vfs.xml</code> configuration are iterated until a match is 7039 * found, and 7040 * <li>if still no file could be found, <code>null</code> is retuned 7041 * </ol> 7042 * 7043 * @param dbc the database context 7044 * @param resource the folder to get the default file for 7045 * @param resourceFilter the resource filter 7046 * 7047 * @return the default file for the given folder 7048 */ 7049 public CmsResource readDefaultFile(CmsDbContext dbc, CmsResource resource, CmsResourceFilter resourceFilter) { 7050 7051 // resource exists, lets check if we have a file or a folder 7052 if (resource.isFolder()) { 7053 // the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder 7054 try { 7055 String defaultFileName = readPropertyObject( 7056 dbc, 7057 resource, 7058 CmsPropertyDefinition.PROPERTY_DEFAULT_FILE, 7059 false).getValue(); 7060 // check if the default file property does not match the navigation level folder marker value 7061 if ((defaultFileName != null) && !CmsJspNavBuilder.NAVIGATION_LEVEL_FOLDER.equals(defaultFileName)) { 7062 // property was set, so look up this file first 7063 String folderName = CmsResource.getFolderPath(resource.getRootPath()); 7064 resource = readResource(dbc, folderName + defaultFileName, resourceFilter.addRequireFile()); 7065 } 7066 } catch (CmsException e) { 7067 // ignore all other exceptions and continue the lookup process 7068 if (LOG.isDebugEnabled()) { 7069 LOG.debug(e.getLocalizedMessage(), e); 7070 } 7071 } 7072 if (resource.isFolder()) { 7073 String folderName = CmsResource.getFolderPath(resource.getRootPath()); 7074 // resource is (still) a folder, check default files specified in configuration 7075 Iterator<String> it = OpenCms.getDefaultFiles().iterator(); 7076 while (it.hasNext()) { 7077 String tmpResourceName = folderName + it.next(); 7078 try { 7079 resource = readResource(dbc, tmpResourceName, resourceFilter.addRequireFile()); 7080 // no exception? So we have found the default file 7081 // stop looking for default files 7082 break; 7083 } catch (CmsException e) { 7084 // ignore all other exceptions and continue the lookup process 7085 if (LOG.isDebugEnabled()) { 7086 LOG.debug(e.getLocalizedMessage(), e); 7087 } 7088 } 7089 } 7090 } 7091 } 7092 if (resource.isFolder()) { 7093 // we only want files as a result for further processing 7094 resource = null; 7095 } 7096 return resource; 7097 } 7098 7099 /** 7100 * Reads all deleted (historical) resources below the given path, 7101 * including the full tree below the path, if required.<p> 7102 * 7103 * @param dbc the current db context 7104 * @param resource the parent resource to read the resources from 7105 * @param readTree <code>true</code> to read all subresources 7106 * @param isVfsManager <code>true</code> if the current user has the vfs manager role 7107 * 7108 * @return a list of <code>{@link I_CmsHistoryResource}</code> objects 7109 * 7110 * @throws CmsException if something goes wrong 7111 * 7112 * @see CmsObject#readResource(CmsUUID, int) 7113 * @see CmsObject#readResources(String, CmsResourceFilter, boolean) 7114 * @see CmsObject#readDeletedResources(String, boolean) 7115 */ 7116 public List<I_CmsHistoryResource> readDeletedResources( 7117 CmsDbContext dbc, 7118 CmsResource resource, 7119 boolean readTree, 7120 boolean isVfsManager) 7121 throws CmsException { 7122 7123 Set<I_CmsHistoryResource> result = new HashSet<I_CmsHistoryResource>(); 7124 List<I_CmsHistoryResource> deletedResources; 7125 dbc.getRequestContext().setAttribute("ATTR_RESOURCE_NAME", resource.getRootPath()); 7126 try { 7127 deletedResources = getHistoryDriver(dbc).readDeletedResources( 7128 dbc, 7129 resource.getStructureId(), 7130 isVfsManager ? null : dbc.currentUser().getId()); 7131 } finally { 7132 dbc.getRequestContext().removeAttribute("ATTR_RESOURCE_NAME"); 7133 } 7134 result.addAll(deletedResources); 7135 Set<I_CmsHistoryResource> newResult = new HashSet<I_CmsHistoryResource>(result.size()); 7136 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 7137 Iterator<I_CmsHistoryResource> it = result.iterator(); 7138 while (it.hasNext()) { 7139 I_CmsHistoryResource histRes = it.next(); 7140 // adjust the paths 7141 try { 7142 if (vfsDriver.validateStructureIdExists( 7143 dbc, 7144 dbc.currentProject().getUuid(), 7145 histRes.getStructureId())) { 7146 newResult.add(histRes); 7147 continue; 7148 } 7149 // adjust the path in case of deleted files 7150 String resourcePath = histRes.getRootPath(); 7151 String resName = CmsResource.getName(resourcePath); 7152 String path = CmsResource.getParentFolder(resourcePath); 7153 7154 CmsUUID parentId = histRes.getParentId(); 7155 try { 7156 // first look for the path through the parent id 7157 path = readResource(dbc, parentId, CmsResourceFilter.IGNORE_EXPIRATION).getRootPath(); 7158 } catch (CmsDataAccessException e) { 7159 // if the resource with the parent id is not found, try to get a new parent id with the path 7160 try { 7161 parentId = readResource(dbc, path, CmsResourceFilter.IGNORE_EXPIRATION).getStructureId(); 7162 } catch (CmsDataAccessException e1) { 7163 // ignore, the parent folder has been completely deleted 7164 } 7165 } 7166 resourcePath = path + resName; 7167 7168 boolean isFolder = resourcePath.endsWith("/"); 7169 if (isFolder) { 7170 newResult.add( 7171 new CmsHistoryFolder( 7172 histRes.getPublishTag(), 7173 histRes.getStructureId(), 7174 histRes.getResourceId(), 7175 resourcePath, 7176 histRes.getTypeId(), 7177 histRes.getFlags(), 7178 histRes.getProjectLastModified(), 7179 histRes.getState(), 7180 histRes.getDateCreated(), 7181 histRes.getUserCreated(), 7182 histRes.getDateLastModified(), 7183 histRes.getUserLastModified(), 7184 histRes.getDateReleased(), 7185 histRes.getDateExpired(), 7186 histRes.getVersion(), 7187 parentId, 7188 histRes.getResourceVersion(), 7189 histRes.getStructureVersion())); 7190 } else { 7191 newResult.add( 7192 new CmsHistoryFile( 7193 histRes.getPublishTag(), 7194 histRes.getStructureId(), 7195 histRes.getResourceId(), 7196 resourcePath, 7197 histRes.getTypeId(), 7198 histRes.getFlags(), 7199 histRes.getProjectLastModified(), 7200 histRes.getState(), 7201 histRes.getDateCreated(), 7202 histRes.getUserCreated(), 7203 histRes.getDateLastModified(), 7204 histRes.getUserLastModified(), 7205 histRes.getDateReleased(), 7206 histRes.getDateExpired(), 7207 histRes.getLength(), 7208 histRes.getDateContent(), 7209 histRes.getVersion(), 7210 parentId, 7211 null, 7212 histRes.getResourceVersion(), 7213 histRes.getStructureVersion())); 7214 } 7215 } catch (CmsDataAccessException e) { 7216 // should never happen 7217 if (LOG.isErrorEnabled()) { 7218 LOG.error(e.getLocalizedMessage(), e); 7219 } 7220 } 7221 } 7222 if (readTree) { 7223 Iterator<I_CmsHistoryResource> itDeleted = deletedResources.iterator(); 7224 while (itDeleted.hasNext()) { 7225 I_CmsHistoryResource delResource = itDeleted.next(); 7226 if (delResource.isFolder()) { 7227 newResult.addAll(readDeletedResources(dbc, (CmsFolder)delResource, readTree, isVfsManager)); 7228 } 7229 } 7230 try { 7231 readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL); 7232 // resource exists, so recurse 7233 Iterator<CmsResource> itResources = readResources( 7234 dbc, 7235 resource, 7236 CmsResourceFilter.ALL.addRequireFolder(), 7237 readTree).iterator(); 7238 while (itResources.hasNext()) { 7239 CmsResource subResource = itResources.next(); 7240 if (subResource.isFolder()) { 7241 newResult.addAll(readDeletedResources(dbc, subResource, readTree, isVfsManager)); 7242 } 7243 } 7244 } catch (Exception e) { 7245 // resource does not exists 7246 if (LOG.isDebugEnabled()) { 7247 LOG.debug(e.getLocalizedMessage(), e); 7248 } 7249 } 7250 } 7251 List<I_CmsHistoryResource> finalRes = new ArrayList<I_CmsHistoryResource>(newResult); 7252 Collections.sort(finalRes, I_CmsResource.COMPARE_ROOT_PATH); 7253 return finalRes; 7254 } 7255 7256 /** 7257 * Reads a file resource (including it's binary content) from the VFS, 7258 * using the specified resource filter.<p> 7259 * 7260 * In case you do not need the file content, 7261 * use <code>{@link #readResource(CmsDbContext, String, CmsResourceFilter)}</code> instead.<p> 7262 * 7263 * The specified filter controls what kind of resources should be "found" 7264 * during the read operation. This will depend on the application. For example, 7265 * using <code>{@link CmsResourceFilter#DEFAULT}</code> will only return currently 7266 * "valid" resources, while using <code>{@link CmsResourceFilter#IGNORE_EXPIRATION}</code> 7267 * will ignore the date release / date expired information of the resource.<p> 7268 * 7269 * @param dbc the current database context 7270 * @param resource the base file resource (without content) 7271 * @return the file read from the VFS 7272 * @throws CmsException if operation was not successful 7273 */ 7274 public CmsFile readFile(CmsDbContext dbc, CmsResource resource) throws CmsException { 7275 7276 if (resource.isFolder()) { 7277 throw new CmsVfsResourceNotFoundException( 7278 Messages.get().container( 7279 Messages.ERR_ACCESS_FOLDER_AS_FILE_1, 7280 dbc.removeSiteRoot(resource.getRootPath()))); 7281 } 7282 7283 CmsUUID projectId = dbc.currentProject().getUuid(); 7284 CmsFile file = null; 7285 if (resource instanceof I_CmsHistoryResource) { 7286 file = new CmsHistoryFile((I_CmsHistoryResource)resource); 7287 file.setContents( 7288 getHistoryDriver(dbc).readContent( 7289 dbc, 7290 resource.getResourceId(), 7291 ((I_CmsHistoryResource)resource).getPublishTag())); 7292 } else { 7293 file = new CmsFile(resource); 7294 file.setContents(getVfsDriver(dbc).readContent(dbc, projectId, resource.getResourceId())); 7295 } 7296 return file; 7297 } 7298 7299 /** 7300 * Reads a folder from the VFS, 7301 * using the specified resource filter.<p> 7302 * 7303 * @param dbc the current database context 7304 * @param resourcename the name of the folder to read (full path) 7305 * @param filter the resource filter to use while reading 7306 * 7307 * @return the folder that was read 7308 * 7309 * @throws CmsDataAccessException if something goes wrong 7310 * 7311 * @see #readResource(CmsDbContext, String, CmsResourceFilter) 7312 * @see CmsObject#readFolder(String) 7313 * @see CmsObject#readFolder(String, CmsResourceFilter) 7314 */ 7315 public CmsFolder readFolder(CmsDbContext dbc, String resourcename, CmsResourceFilter filter) 7316 throws CmsDataAccessException { 7317 7318 CmsResource resource = readResource(dbc, resourcename, filter); 7319 7320 return convertResourceToFolder(resource); 7321 } 7322 7323 public List<CmsFolderSizeEntry> readFolderSizeStats(CmsDbContext dbc, CmsFolderSizeOptions options) 7324 throws CmsException { 7325 7326 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 7327 List<CmsFolderSizeEntry> stats = vfsDriver.readFolderSizeStats(dbc, false, options); 7328 return stats; 7329 7330 } 7331 7332 /** 7333 * Reads the group of a project.<p> 7334 * 7335 * @param dbc the current database context 7336 * @param project the project to read from 7337 * 7338 * @return the group of a resource 7339 */ 7340 public CmsGroup readGroup(CmsDbContext dbc, CmsProject project) { 7341 7342 try { 7343 return readGroup(dbc, project.getGroupId()); 7344 } catch (CmsException exc) { 7345 return new CmsGroup( 7346 CmsUUID.getNullUUID(), 7347 CmsUUID.getNullUUID(), 7348 project.getGroupId() + "", 7349 "deleted group", 7350 0); 7351 } 7352 } 7353 7354 /** 7355 * Reads a group based on its id.<p> 7356 * 7357 * @param dbc the current database context 7358 * @param groupId the id of the group that is to be read 7359 * 7360 * @return the requested group 7361 * 7362 * @throws CmsException if operation was not successful 7363 */ 7364 public CmsGroup readGroup(CmsDbContext dbc, CmsUUID groupId) throws CmsException { 7365 7366 CmsGroup group = null; 7367 // try to read group from cache 7368 group = m_monitor.getCachedGroup(groupId.toString()); 7369 if (group == null) { 7370 group = getUserDriver(dbc).readGroup(dbc, groupId); 7371 m_monitor.cacheGroup(group); 7372 } 7373 return group; 7374 } 7375 7376 /** 7377 * Reads a group based on its name.<p> 7378 * 7379 * @param dbc the current database context 7380 * @param groupname the name of the group that is to be read 7381 * 7382 * @return the requested group 7383 * 7384 * @throws CmsDataAccessException if operation was not successful 7385 */ 7386 public CmsGroup readGroup(CmsDbContext dbc, String groupname) throws CmsDataAccessException { 7387 7388 CmsGroup group = null; 7389 // try to read group from cache 7390 group = m_monitor.getCachedGroup(groupname); 7391 if (group == null) { 7392 group = getUserDriver(dbc).readGroup(dbc, groupname); 7393 m_monitor.cacheGroup(group); 7394 } 7395 return group; 7396 } 7397 7398 /** 7399 * Reads a principal (an user or group) from the historical archive based on its ID.<p> 7400 * 7401 * @param dbc the current database context 7402 * @param principalId the id of the principal to read 7403 * 7404 * @return the historical principal entry with the given id 7405 * 7406 * @throws CmsException if something goes wrong, ie. {@link CmsDbEntryNotFoundException} 7407 * 7408 * @see CmsObject#readUser(CmsUUID) 7409 * @see CmsObject#readGroup(CmsUUID) 7410 * @see CmsObject#readHistoryPrincipal(CmsUUID) 7411 */ 7412 public CmsHistoryPrincipal readHistoricalPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsException { 7413 7414 return getHistoryDriver(dbc).readPrincipal(dbc, principalId); 7415 } 7416 7417 /** 7418 * Returns the latest historical project entry with the given id.<p> 7419 * 7420 * @param dbc the current database context 7421 * @param projectId the project id 7422 * 7423 * @return the requested historical project entry 7424 * 7425 * @throws CmsException if something goes wrong 7426 */ 7427 public CmsHistoryProject readHistoryProject(CmsDbContext dbc, CmsUUID projectId) throws CmsException { 7428 7429 return getHistoryDriver(dbc).readProject(dbc, projectId); 7430 } 7431 7432 /** 7433 * Returns a historical project entry.<p> 7434 * 7435 * @param dbc the current database context 7436 * @param publishTag the publish tag of the project 7437 * 7438 * @return the requested historical project entry 7439 * 7440 * @throws CmsException if something goes wrong 7441 */ 7442 public CmsHistoryProject readHistoryProject(CmsDbContext dbc, int publishTag) throws CmsException { 7443 7444 return getHistoryDriver(dbc).readProject(dbc, publishTag); 7445 } 7446 7447 /** 7448 * Reads the list of all <code>{@link CmsProperty}</code> objects that belongs to the given historical resource.<p> 7449 * 7450 * @param dbc the current database context 7451 * @param historyResource the historical resource to read the properties for 7452 * 7453 * @return the list of <code>{@link CmsProperty}</code> objects 7454 * 7455 * @throws CmsException if something goes wrong 7456 */ 7457 public List<CmsProperty> readHistoryPropertyObjects(CmsDbContext dbc, I_CmsHistoryResource historyResource) 7458 throws CmsException { 7459 7460 return getHistoryDriver(dbc).readProperties(dbc, historyResource); 7461 } 7462 7463 /** 7464 * Reads the structure id which is mapped to a given URL name.<p> 7465 * 7466 * @param dbc the current database context 7467 * @param name the name for which the mapped structure id should be looked up 7468 * 7469 * @return the structure id which is mapped to the given name, or null if there is no such id 7470 * 7471 * @throws CmsDataAccessException if something goes wrong 7472 */ 7473 public CmsUUID readIdForUrlName(CmsDbContext dbc, String name) throws CmsDataAccessException { 7474 7475 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7476 dbc, 7477 dbc.currentProject().isOnlineProject(), 7478 CmsUrlNameMappingFilter.ALL.filterName(name)); 7479 if (entries.isEmpty()) { 7480 return null; 7481 } 7482 return entries.get(0).getStructureId(); 7483 } 7484 7485 /** 7486 * Reads the locks that were saved to the database in the previous run of OpenCms.<p> 7487 * 7488 * @param dbc the current database context 7489 * 7490 * @throws CmsException if something goes wrong 7491 */ 7492 public void readLocks(CmsDbContext dbc) throws CmsException { 7493 7494 m_lockManager.readLocks(dbc); 7495 } 7496 7497 /** 7498 * Reads the manager group of a project.<p> 7499 * 7500 * @param dbc the current database context 7501 * @param project the project to read from 7502 * 7503 * @return the group of a resource 7504 */ 7505 public CmsGroup readManagerGroup(CmsDbContext dbc, CmsProject project) { 7506 7507 try { 7508 return readGroup(dbc, project.getManagerGroupId()); 7509 } catch (CmsException exc) { 7510 // the group does not exist any more - return a dummy-group 7511 return new CmsGroup( 7512 CmsUUID.getNullUUID(), 7513 CmsUUID.getNullUUID(), 7514 project.getManagerGroupId() + "", 7515 "deleted group", 7516 0); 7517 } 7518 } 7519 7520 /** 7521 * Reads the URL name which has been most recently mapped to the given structure id, or null 7522 * if no URL name is mapped to the id.<p> 7523 * 7524 * @param dbc the current database context 7525 * @param id a structure id 7526 * @return the name which has been most recently mapped to the given structure id 7527 * 7528 * @throws CmsDataAccessException if something goes wrong 7529 */ 7530 public String readNewestUrlNameForId(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 7531 7532 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 7533 dbc, 7534 dbc.currentProject().isOnlineProject(), 7535 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 7536 if (entries.isEmpty()) { 7537 return null; 7538 } 7539 7540 Collections.sort(entries, new UrlNameMappingComparator()); 7541 CmsUrlNameMappingEntry lastEntry = entries.get(entries.size() - 1); 7542 return lastEntry.getName(); 7543 } 7544 7545 /** 7546 * Reads an organizational Unit based on its fully qualified name.<p> 7547 * 7548 * @param dbc the current db context 7549 * @param ouFqn the fully qualified name of the organizational Unit to be read 7550 * 7551 * @return the organizational Unit that with the provided fully qualified name 7552 * 7553 * @throws CmsException if something goes wrong 7554 */ 7555 public CmsOrganizationalUnit readOrganizationalUnit(CmsDbContext dbc, String ouFqn) throws CmsException { 7556 7557 CmsOrganizationalUnit organizationalUnit = null; 7558 // try to read organizational unit from cache 7559 organizationalUnit = m_monitor.getCachedOrgUnit(ouFqn); 7560 if (organizationalUnit == null) { 7561 organizationalUnit = getUserDriver(dbc).readOrganizationalUnit(dbc, ouFqn); 7562 m_monitor.cacheOrgUnit(organizationalUnit); 7563 } 7564 return organizationalUnit; 7565 } 7566 7567 /** 7568 * Reads the owner of a project.<p> 7569 * 7570 * @param dbc the current database context 7571 * @param project the project to get the owner from 7572 * 7573 * @return the owner of a resource 7574 * @throws CmsException if something goes wrong 7575 */ 7576 public CmsUser readOwner(CmsDbContext dbc, CmsProject project) throws CmsException { 7577 7578 return readUser(dbc, project.getOwnerId()); 7579 } 7580 7581 /** 7582 * Reads the parent folder to a given structure id.<p> 7583 * 7584 * @param dbc the current database context 7585 * @param structureId the structure id of the child 7586 * 7587 * @return the parent folder resource 7588 * 7589 * @throws CmsDataAccessException if something goes wrong 7590 */ 7591 public CmsResource readParentFolder(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException { 7592 7593 return getVfsDriver(dbc).readParentFolder(dbc, dbc.currentProject().getUuid(), structureId); 7594 } 7595 7596 /** 7597 * Builds a list of resources for a given path.<p> 7598 * 7599 * @param dbc the current database context 7600 * @param path the requested path 7601 * @param filter a filter object (only "includeDeleted" information is used!) 7602 * 7603 * @return list of <code>{@link CmsResource}</code>s 7604 * 7605 * @throws CmsException if something goes wrong 7606 */ 7607 public List<CmsResource> readPath(CmsDbContext dbc, String path, CmsResourceFilter filter) throws CmsException { 7608 7609 // splits the path into folder and filename tokens 7610 List<String> tokens = CmsStringUtil.splitAsList(path, '/'); 7611 7612 // the root folder is no token in the path but a resource which has to be added to the path 7613 int count = tokens.size() + 1; 7614 // holds the CmsResource instances in the path 7615 List<CmsResource> pathList = new ArrayList<CmsResource>(count); 7616 7617 // true if the path doesn't end with a folder 7618 boolean lastResourceIsFile = false; 7619 // number of folders in the path 7620 int folderCount = count; 7621 if (!path.endsWith("/")) { 7622 folderCount--; 7623 lastResourceIsFile = true; 7624 } 7625 7626 // read the root folder, because it's ID is required to read any sub-resources 7627 String currentResourceName = "/"; 7628 StringBuffer currentPath = new StringBuffer(64); 7629 currentPath.append('/'); 7630 7631 String cp = currentPath.toString(); 7632 CmsUUID projectId = getProjectIdForContext(dbc); 7633 7634 // key to cache the resources 7635 String cacheKey = getCacheKey(null, false, projectId, cp); 7636 // the current resource 7637 CmsResource currentResource = m_monitor.getCachedResource(cacheKey); 7638 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7639 currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp); 7640 if (dbc.getProjectId().isNullUUID()) { 7641 m_monitor.cacheResource(cacheKey, currentResource); 7642 } 7643 } 7644 7645 pathList.add(0, currentResource); 7646 7647 if (count == 1) { 7648 // the root folder was requested- no further operations required 7649 return pathList; 7650 } 7651 7652 Iterator<String> it = tokens.iterator(); 7653 currentResourceName = it.next(); 7654 7655 // read the folder resources in the path /a/b/c/ 7656 int i = 0; 7657 for (i = 1; i < folderCount; i++) { 7658 currentPath.append(currentResourceName); 7659 currentPath.append('/'); 7660 // read the folder 7661 cp = currentPath.toString(); 7662 cacheKey = getCacheKey(null, false, projectId, cp); 7663 currentResource = m_monitor.getCachedResource(cacheKey); 7664 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7665 currentResource = getVfsDriver(dbc).readFolder(dbc, projectId, cp); 7666 if (dbc.getProjectId().isNullUUID()) { 7667 m_monitor.cacheResource(cacheKey, currentResource); 7668 } 7669 } 7670 7671 pathList.add(i, currentResource); 7672 7673 if (i < (folderCount - 1)) { 7674 currentResourceName = it.next(); 7675 } 7676 } 7677 7678 // read the (optional) last file resource in the path /x.html 7679 if (lastResourceIsFile) { 7680 if (it.hasNext()) { 7681 // this will only be false if a resource in the 7682 // top level root folder (e.g. "/index.html") was requested 7683 currentResourceName = it.next(); 7684 } 7685 currentPath.append(currentResourceName); 7686 7687 // read the file 7688 cp = currentPath.toString(); 7689 cacheKey = getCacheKey(null, false, projectId, cp); 7690 currentResource = m_monitor.getCachedResource(cacheKey); 7691 if ((currentResource == null) || !dbc.getProjectId().isNullUUID()) { 7692 currentResource = getVfsDriver(dbc).readResource(dbc, projectId, cp, filter.includeDeleted()); 7693 if (dbc.getProjectId().isNullUUID()) { 7694 m_monitor.cacheResource(cacheKey, currentResource); 7695 } 7696 } 7697 7698 pathList.add(i, currentResource); 7699 } 7700 7701 return pathList; 7702 } 7703 7704 /** 7705 * Reads a project given the projects id.<p> 7706 * 7707 * @param dbc the current database context 7708 * @param id the id of the project 7709 * 7710 * @return the project read 7711 * 7712 * @throws CmsDataAccessException if something goes wrong 7713 */ 7714 public CmsProject readProject(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 7715 7716 CmsProject project = null; 7717 project = m_monitor.getCachedProject(id.toString()); 7718 if (project == null) { 7719 project = getProjectDriver(dbc).readProject(dbc, id); 7720 m_monitor.cacheProject(project); 7721 } 7722 return project; 7723 } 7724 7725 /** 7726 * Reads a project.<p> 7727 * 7728 * Important: Since a project name can be used multiple times, this is NOT the most efficient 7729 * way to read the project. This is only a convenience for front end developing. 7730 * Reading a project by name will return the first project with that name. 7731 * All core classes must use the id version {@link #readProject(CmsDbContext, CmsUUID)} to ensure the right project is read.<p> 7732 * 7733 * @param dbc the current database context 7734 * @param name the name of the project 7735 * 7736 * @return the project read 7737 * 7738 * @throws CmsException if something goes wrong 7739 */ 7740 public CmsProject readProject(CmsDbContext dbc, String name) throws CmsException { 7741 7742 CmsProject project = null; 7743 project = m_monitor.getCachedProject(name); 7744 if (project == null) { 7745 project = getProjectDriver(dbc).readProject(dbc, name); 7746 m_monitor.cacheProject(project); 7747 } 7748 return project; 7749 } 7750 7751 /** 7752 * Returns the list of all resource names that define the "view" of the given project.<p> 7753 * 7754 * @param dbc the current database context 7755 * @param project the project to get the project resources for 7756 * 7757 * @return the list of all resources, as <code>{@link String}</code> objects 7758 * that define the "view" of the given project. 7759 * 7760 * @throws CmsException if something goes wrong 7761 */ 7762 public List<String> readProjectResources(CmsDbContext dbc, CmsProject project) throws CmsException { 7763 7764 return getProjectDriver(dbc).readProjectResources(dbc, project); 7765 } 7766 7767 /** 7768 * Reads all resources of a project that match a given state from the VFS.<p> 7769 * 7770 * Possible values for the <code>state</code> parameter are:<br> 7771 * <ul> 7772 * <li><code>{@link CmsResource#STATE_CHANGED}</code>: Read all "changed" resources in the project</li> 7773 * <li><code>{@link CmsResource#STATE_NEW}</code>: Read all "new" resources in the project</li> 7774 * <li><code>{@link CmsResource#STATE_DELETED}</code>: Read all "deleted" resources in the project</li> 7775 * <li><code>{@link CmsResource#STATE_KEEP}</code>: Read all resources either "changed", "new" or "deleted" in the project</li> 7776 * </ul><p> 7777 * 7778 * @param dbc the current database context 7779 * @param projectId the id of the project to read the file resources for 7780 * @param state the resource state to match 7781 * 7782 * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria 7783 * 7784 * @throws CmsException if something goes wrong 7785 * 7786 * @see CmsObject#readProjectView(CmsUUID, CmsResourceState) 7787 */ 7788 public List<CmsResource> readProjectView(CmsDbContext dbc, CmsUUID projectId, CmsResourceState state) 7789 throws CmsException { 7790 7791 List<CmsResource> resources; 7792 if (state.isNew() || state.isChanged() || state.isDeleted()) { 7793 // get all resources form the database that match the selected state 7794 resources = getVfsDriver(dbc).readResources(dbc, projectId, state, CmsDriverManager.READMODE_MATCHSTATE); 7795 } else { 7796 // get all resources form the database that are somehow changed (i.e. not unchanged) 7797 resources = getVfsDriver( 7798 dbc).readResources(dbc, projectId, CmsResource.STATE_UNCHANGED, CmsDriverManager.READMODE_UNMATCHSTATE); 7799 } 7800 7801 // filter the permissions 7802 List<CmsResource> result = filterPermissions(dbc, resources, CmsResourceFilter.ALL); 7803 // sort the result 7804 Collections.sort(result); 7805 // set the full resource names 7806 return updateContextDates(dbc, result); 7807 } 7808 7809 /** 7810 * Reads a property definition.<p> 7811 * 7812 * If no property definition with the given name is found, 7813 * <code>null</code> is returned.<p> 7814 * 7815 * @param dbc the current database context 7816 * @param name the name of the property definition to read 7817 * 7818 * @return the property definition that was read 7819 * 7820 * @throws CmsException a CmsDbEntryNotFoundException is thrown if the property definition does not exist 7821 */ 7822 public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name) throws CmsException { 7823 7824 return getVfsDriver(dbc).readPropertyDefinition(dbc, name, dbc.currentProject().getUuid()); 7825 } 7826 7827 /** 7828 * Reads a property object from a resource specified by a property name.<p> 7829 * 7830 * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p> 7831 * 7832 * @param dbc the current database context 7833 * @param resource the resource where the property is read from 7834 * @param key the property key name 7835 * @param search if <code>true</code>, the property is searched on all parent folders of the resource. 7836 * if it's not found attached directly to the resource. 7837 * 7838 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 7839 * 7840 * @throws CmsException if something goes wrong 7841 */ 7842 public CmsProperty readPropertyObject(CmsDbContext dbc, CmsResource resource, String key, boolean search) 7843 throws CmsException { 7844 7845 // NOTE: Do not call readPropertyObject(dbc, resource, key, search, null) for performance reasons 7846 7847 // use the list reading method to obtain all properties for the resource 7848 List<CmsProperty> properties = readPropertyObjects(dbc, resource, search); 7849 7850 int i = properties.indexOf(new CmsProperty(key, null, null)); 7851 if (i >= 0) { 7852 // property has been found in the map 7853 CmsProperty result = properties.get(i); 7854 // ensure the result value is not frozen 7855 return result.cloneAsProperty(); 7856 } 7857 return CmsProperty.getNullProperty(); 7858 7859 } 7860 7861 /** 7862 * Reads a property object from a resource specified by a property name.<p> 7863 * 7864 * Returns <code>{@link CmsProperty#getNullProperty()}</code> if the property is not found.<p> 7865 * 7866 * @param dbc the current database context 7867 * @param resource the resource where the property is read from 7868 * @param key the property key name 7869 * @param search if <code>true</code>, the property is searched on all parent folders of the resource. 7870 * if it's not found attached directly to the resource. 7871 * @param locale the locale for which the property should be read. 7872 * 7873 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 7874 * 7875 * @throws CmsException if something goes wrong 7876 */ 7877 public CmsProperty readPropertyObject( 7878 CmsDbContext dbc, 7879 CmsResource resource, 7880 String key, 7881 boolean search, 7882 Locale locale) 7883 throws CmsException { 7884 7885 // use the list reading method to obtain all properties for the resource 7886 List<CmsProperty> properties = readPropertyObjects(dbc, resource, search); 7887 // create a lookup property object and look this up in the result map 7888 CmsProperty result = null; 7889 // handle the case without locale separately to improve performance 7890 for (String localizedKey : CmsLocaleManager.getLocaleVariants(key, locale, true, false)) { 7891 int i = properties.indexOf(new CmsProperty(localizedKey, null, null)); 7892 if (i >= 0) { 7893 // property has been found in the map 7894 result = properties.get(i); 7895 // ensure the result value is not frozen 7896 return result.cloneAsProperty(); 7897 } 7898 } 7899 return CmsProperty.getNullProperty(); 7900 } 7901 7902 /** 7903 * Reads all property objects mapped to a specified resource from the database.<p> 7904 * 7905 * All properties in the result List will be in frozen (read only) state, so you can't change the values.<p> 7906 * 7907 * Returns an empty list if no properties are found at all.<p> 7908 * 7909 * @param dbc the current database context 7910 * @param resource the resource where the properties are read from 7911 * @param search true, if the properties should be searched on all parent folders if not found on the resource 7912 * 7913 * @return a list of CmsProperty objects containing the structure and/or resource value 7914 * 7915 * @throws CmsException if something goes wrong 7916 * 7917 * @see CmsObject#readPropertyObjects(String, boolean) 7918 */ 7919 public List<CmsProperty> readPropertyObjects(CmsDbContext dbc, CmsResource resource, boolean search) 7920 throws CmsException { 7921 7922 // check if we have the result already cached 7923 CmsUUID projectId = getProjectIdForContext(dbc); 7924 String cacheKey = getCacheKey(CACHE_ALL_PROPERTIES, search, projectId, resource.getRootPath()); 7925 7926 List<CmsProperty> properties = m_monitor.getCachedPropertyList(cacheKey); 7927 7928 if ((properties == null) || !dbc.getProjectId().isNullUUID()) { 7929 // result not cached, let's look it up in the DB 7930 if (search) { 7931 boolean cont; 7932 properties = new ArrayList<CmsProperty>(); 7933 List<CmsProperty> parentProperties = null; 7934 7935 do { 7936 try { 7937 parentProperties = readPropertyObjects(dbc, resource, false); 7938 7939 // make sure properties from lower folders "overwrite" properties from upper folders 7940 parentProperties.removeAll(properties); 7941 parentProperties.addAll(properties); 7942 7943 properties.clear(); 7944 properties.addAll(parentProperties); 7945 7946 cont = resource.getRootPath().length() > 1; 7947 } catch (CmsSecurityException se) { 7948 // a security exception (probably no read permission) we return the current result 7949 cont = false; 7950 } 7951 if (cont) { 7952 // no permission check on parent folder is required since we must have "read" 7953 // permissions to read the child resource anyway 7954 resource = readResource( 7955 dbc, 7956 CmsResource.getParentFolder(resource.getRootPath()), 7957 CmsResourceFilter.ALL); 7958 } 7959 } while (cont); 7960 } else { 7961 properties = getVfsDriver(dbc).readPropertyObjects(dbc, dbc.currentProject(), resource); 7962 // for (CmsProperty prop : properties) { 7963 // prop.setOrigin(resource.getRootPath()); 7964 // } 7965 } 7966 7967 // set all properties in the result list as frozen 7968 CmsProperty.setFrozen(properties); 7969 if (dbc.getProjectId().isNullUUID()) { 7970 // store the result in the cache if needed 7971 m_monitor.cachePropertyList(cacheKey, properties); 7972 } 7973 } 7974 7975 return new ArrayList<CmsProperty>(properties); 7976 } 7977 7978 /** 7979 * Reads the resources that were published in a publish task for a given publish history ID.<p> 7980 * 7981 * @param dbc the current database context 7982 * @param publishHistoryId unique int ID to identify each publish task in the publish history 7983 * 7984 * @return a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects 7985 * 7986 * @throws CmsException if something goes wrong 7987 */ 7988 public List<CmsPublishedResource> readPublishedResources(CmsDbContext dbc, CmsUUID publishHistoryId) 7989 throws CmsException { 7990 7991 String cacheKey = publishHistoryId.toString(); 7992 List<CmsPublishedResource> resourceList = m_monitor.getCachedPublishedResources(cacheKey); 7993 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 7994 resourceList = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId); 7995 // store the result in the cache 7996 if (dbc.getProjectId().isNullUUID()) { 7997 m_monitor.cachePublishedResources(cacheKey, resourceList); 7998 } 7999 } 8000 return resourceList; 8001 } 8002 8003 /** 8004 * Reads a single publish job identified by its publish history id.<p> 8005 * 8006 * @param dbc the current database context 8007 * @param publishHistoryId unique id to identify the publish job in the publish history 8008 * @return an object of type <code>{@link CmsPublishJobInfoBean}</code> 8009 * 8010 * @throws CmsException if something goes wrong 8011 */ 8012 public CmsPublishJobInfoBean readPublishJob(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8013 8014 return getProjectDriver(dbc).readPublishJob(dbc, publishHistoryId); 8015 } 8016 8017 /** 8018 * Reads all available publish jobs.<p> 8019 * 8020 * @param dbc the current database context 8021 * @param startTime the start of the time range for finish time 8022 * @param endTime the end of the time range for finish time 8023 * @return a list of objects of type <code>{@link CmsPublishJobInfoBean}</code> 8024 * 8025 * @throws CmsException if something goes wrong 8026 */ 8027 public List<CmsPublishJobInfoBean> readPublishJobs(CmsDbContext dbc, long startTime, long endTime) 8028 throws CmsException { 8029 8030 return getProjectDriver(dbc).readPublishJobs(dbc, startTime, endTime); 8031 } 8032 8033 /** 8034 * Reads the publish list assigned to a publish job.<p> 8035 * 8036 * @param dbc the current database context 8037 * @param publishHistoryId the history id identifying the publish job 8038 * @return the assigned publish list 8039 * @throws CmsException if something goes wrong 8040 */ 8041 public CmsPublishList readPublishList(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8042 8043 return getProjectDriver(dbc).readPublishList(dbc, publishHistoryId); 8044 } 8045 8046 /** 8047 * Reads the publish report assigned to a publish job.<p> 8048 * 8049 * @param dbc the current database context 8050 * @param publishHistoryId the history id identifying the publish job 8051 * @return the content of the assigned publish report 8052 * @throws CmsException if something goes wrong 8053 */ 8054 public byte[] readPublishReportContents(CmsDbContext dbc, CmsUUID publishHistoryId) throws CmsException { 8055 8056 return getProjectDriver(dbc).readPublishReportContents(dbc, publishHistoryId); 8057 } 8058 8059 /** 8060 * Reads an historical resource entry for the given resource and with the given version number.<p> 8061 * 8062 * @param dbc the current db context 8063 * @param resource the resource to be read 8064 * @param version the version number to retrieve 8065 * 8066 * @return the resource that was read 8067 * 8068 * @throws CmsException if the resource could not be read for any reason 8069 * 8070 * @see CmsObject#restoreResourceVersion(CmsUUID, int) 8071 * @see CmsObject#readResource(CmsUUID, int) 8072 */ 8073 public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException { 8074 8075 Iterator<I_CmsHistoryResource> itVersions = getHistoryDriver(dbc).readAllAvailableVersions( 8076 dbc, 8077 resource.getStructureId()).iterator(); 8078 while (itVersions.hasNext()) { 8079 I_CmsHistoryResource histRes = itVersions.next(); 8080 if (histRes.getVersion() == version) { 8081 return histRes; 8082 } 8083 } 8084 throw new CmsVfsResourceNotFoundException( 8085 org.opencms.db.generic.Messages.get().container( 8086 org.opencms.db.generic.Messages.ERR_HISTORY_FILE_NOT_FOUND_1, 8087 resource.getStructureId())); 8088 } 8089 8090 /** 8091 * Reads a resource from the VFS, using the specified resource filter.<p> 8092 * 8093 * @param dbc the current database context 8094 * @param structureID the structure id of the resource to read 8095 * @param filter the resource filter to use while reading 8096 * 8097 * @return the resource that was read 8098 * 8099 * @throws CmsDataAccessException if something goes wrong 8100 * 8101 * @see CmsObject#readResource(CmsUUID, CmsResourceFilter) 8102 * @see CmsObject#readResource(CmsUUID) 8103 */ 8104 public CmsResource readResource(CmsDbContext dbc, CmsUUID structureID, CmsResourceFilter filter) 8105 throws CmsDataAccessException { 8106 8107 CmsUUID projectId = getProjectIdForContext(dbc); 8108 // please note: the filter will be applied in the security manager later 8109 CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, structureID, filter.includeDeleted()); 8110 8111 // context dates need to be updated 8112 updateContextDates(dbc, resource); 8113 8114 // return the resource 8115 return resource; 8116 } 8117 8118 /** 8119 * Reads a resource from the VFS, using the specified resource filter.<p> 8120 * 8121 * @param dbc the current database context 8122 * @param resourcePath the name of the resource to read (full path) 8123 * @param filter the resource filter to use while reading 8124 * 8125 * @return the resource that was read 8126 * 8127 * @throws CmsDataAccessException if something goes wrong 8128 * 8129 * @see CmsObject#readResource(String, CmsResourceFilter) 8130 * @see CmsObject#readResource(String) 8131 * @see CmsObject#readFile(CmsResource) 8132 */ 8133 public CmsResource readResource(CmsDbContext dbc, String resourcePath, CmsResourceFilter filter) 8134 throws CmsDataAccessException { 8135 8136 CmsUUID projectId = getProjectIdForContext(dbc); 8137 // please note: the filter will be applied in the security manager later 8138 CmsResource resource = getVfsDriver(dbc).readResource(dbc, projectId, resourcePath, filter.includeDeleted()); 8139 8140 // context dates need to be updated 8141 updateContextDates(dbc, resource); 8142 8143 // return the resource 8144 return resource; 8145 } 8146 8147 /** 8148 * Reads all resources below the given path matching the filter criteria, 8149 * including the full tree below the path only in case the <code>readTree</code> 8150 * parameter is <code>true</code>.<p> 8151 * 8152 * @param dbc the current database context 8153 * @param parent the parent path to read the resources from 8154 * @param filter the filter 8155 * @param readTree <code>true</code> to read all subresources 8156 * 8157 * @return a list of <code>{@link CmsResource}</code> objects matching the filter criteria 8158 * 8159 * @throws CmsDataAccessException if the bare reading of the resources fails 8160 * @throws CmsException if security and permission checks for the resources read fail 8161 */ 8162 public List<CmsResource> readResources( 8163 CmsDbContext dbc, 8164 CmsResource parent, 8165 CmsResourceFilter filter, 8166 boolean readTree) 8167 throws CmsException, CmsDataAccessException { 8168 8169 // try to get the sub resources from the cache 8170 String cacheKey = getCacheKey( 8171 new String[] {dbc.currentUser().getName(), filter.getCacheId(), readTree ? "+" : "-", parent.getRootPath()}, 8172 dbc); 8173 8174 List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey); 8175 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8176 // read the result from the database 8177 resourceList = getVfsDriver(dbc).readResourceTree( 8178 dbc, 8179 dbc.currentProject().getUuid(), 8180 (readTree ? parent.getRootPath() : parent.getStructureId().toString()), 8181 filter.getType(), 8182 filter.getState(), 8183 filter.getModifiedAfter(), 8184 filter.getModifiedBefore(), 8185 filter.getReleaseAfter(), 8186 filter.getReleaseBefore(), 8187 filter.getExpireAfter(), 8188 filter.getExpireBefore(), 8189 (readTree ? CmsDriverManager.READMODE_INCLUDE_TREE : CmsDriverManager.READMODE_EXCLUDE_TREE) 8190 | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0) 8191 | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0) 8192 | ((filter.getOnlyFolders() != null) 8193 ? (filter.getOnlyFolders().booleanValue() 8194 ? CmsDriverManager.READMODE_ONLY_FOLDERS 8195 : CmsDriverManager.READMODE_ONLY_FILES) 8196 : 0)); 8197 8198 // HACK: do not take care of permissions if reading organizational units 8199 if (!parent.getRootPath().startsWith("/system/orgunits/")) { 8200 // apply permission filter 8201 resourceList = filterPermissions(dbc, resourceList, filter); 8202 } 8203 // store the result in the resourceList cache 8204 if (dbc.getProjectId().isNullUUID()) { 8205 m_monitor.cacheResourceList(cacheKey, resourceList); 8206 } 8207 } 8208 // we must always apply the result filter and update the context dates 8209 return updateContextDates(dbc, resourceList, filter); 8210 } 8211 8212 /** 8213 * Returns the resources that were visited by a user set in the filter.<p> 8214 * 8215 * @param dbc the database context 8216 * @param poolName the name of the database pool to use 8217 * @param filter the filter that is used to get the visited resources 8218 * 8219 * @return the resources that were visited by a user set in the filter 8220 * 8221 * @throws CmsException if something goes wrong 8222 */ 8223 public List<CmsResource> readResourcesVisitedBy(CmsDbContext dbc, String poolName, CmsVisitedByFilter filter) 8224 throws CmsException { 8225 8226 List<CmsResource> result = getSubscriptionDriver().readResourcesVisitedBy(dbc, poolName, filter); 8227 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 8228 return result; 8229 } 8230 8231 /** 8232 * Reads all resources that have a value (containing the given value string) set 8233 * for the specified property (definition) in the given path.<p> 8234 * 8235 * Both individual and shared properties of a resource are checked.<p> 8236 * 8237 * If the <code>value</code> parameter is <code>null</code>, all resources having the 8238 * given property set are returned.<p> 8239 * 8240 * @param dbc the current database context 8241 * @param folder the folder to get the resources with the property from 8242 * @param propertyDefinition the name of the property (definition) to check for 8243 * @param value the string to search in the value of the property 8244 * @param filter the resource filter to apply to the result set 8245 * 8246 * @return a list of all <code>{@link CmsResource}</code> objects 8247 * that have a value set for the specified property. 8248 * 8249 * @throws CmsException if something goes wrong 8250 */ 8251 public List<CmsResource> readResourcesWithProperty( 8252 CmsDbContext dbc, 8253 CmsResource folder, 8254 String propertyDefinition, 8255 String value, 8256 CmsResourceFilter filter) 8257 throws CmsException { 8258 8259 String cacheKey; 8260 if (value == null) { 8261 cacheKey = getCacheKey( 8262 new String[] { 8263 dbc.currentUser().getName(), 8264 folder.getRootPath(), 8265 propertyDefinition, 8266 filter.getCacheId()}, 8267 dbc); 8268 } else { 8269 cacheKey = getCacheKey( 8270 new String[] { 8271 dbc.currentUser().getName(), 8272 folder.getRootPath(), 8273 propertyDefinition, 8274 value, 8275 filter.getCacheId()}, 8276 dbc); 8277 } 8278 List<CmsResource> resourceList = m_monitor.getCachedResourceList(cacheKey); 8279 if ((resourceList == null) || !dbc.getProjectId().isNullUUID()) { 8280 8281 CmsPropertyDefinition propDef = null; 8282 try { 8283 // first read the property definition 8284 propDef = readPropertyDefinition(dbc, propertyDefinition); 8285 } catch (CmsDbEntryNotFoundException e) { 8286 LOG.debug(e.getLocalizedMessage(), e); 8287 } 8288 if (propDef != null) { 8289 // now read the list of resources that have a value set for the property definition 8290 resourceList = getVfsDriver(dbc).readResourcesWithProperty( 8291 dbc, 8292 dbc.currentProject().getUuid(), 8293 propDef.getId(), 8294 folder.getRootPath(), 8295 value); 8296 // apply permission filter 8297 resourceList = filterPermissions(dbc, resourceList, filter); 8298 } else { 8299 resourceList = new ArrayList<>(); 8300 } 8301 // store the result in the resourceList cache 8302 if (dbc.getProjectId().isNullUUID()) { 8303 m_monitor.cacheResourceList(cacheKey, resourceList); 8304 } 8305 } 8306 // we must always apply the result filter and update the context dates 8307 return updateContextDates(dbc, resourceList, filter); 8308 } 8309 8310 /** 8311 * Returns the set of users that are responsible for a specific resource.<p> 8312 * 8313 * @param dbc the current database context 8314 * @param resource the resource to get the responsible users from 8315 * 8316 * @return the set of users that are responsible for a specific resource 8317 * 8318 * @throws CmsException if something goes wrong 8319 */ 8320 public Set<I_CmsPrincipal> readResponsiblePrincipals(CmsDbContext dbc, CmsResource resource) throws CmsException { 8321 8322 Set<I_CmsPrincipal> result = new HashSet<I_CmsPrincipal>(); 8323 Iterator<CmsAccessControlEntry> aces = getAccessControlEntries(dbc, resource, true).iterator(); 8324 while (aces.hasNext()) { 8325 CmsAccessControlEntry ace = aces.next(); 8326 if (ace.isResponsible()) { 8327 I_CmsPrincipal p = lookupPrincipal(dbc, ace.getPrincipal()); 8328 if (p != null) { 8329 result.add(p); 8330 } 8331 } 8332 } 8333 return result; 8334 } 8335 8336 /** 8337 * Returns the set of users that are responsible for a specific resource.<p> 8338 * 8339 * @param dbc the current database context 8340 * @param resource the resource to get the responsible users from 8341 * 8342 * @return the set of users that are responsible for a specific resource 8343 * 8344 * @throws CmsException if something goes wrong 8345 */ 8346 public Set<CmsUser> readResponsibleUsers(CmsDbContext dbc, CmsResource resource) throws CmsException { 8347 8348 Set<CmsUser> result = new HashSet<CmsUser>(); 8349 Iterator<I_CmsPrincipal> principals = readResponsiblePrincipals(dbc, resource).iterator(); 8350 while (principals.hasNext()) { 8351 I_CmsPrincipal principal = principals.next(); 8352 if (principal.isGroup()) { 8353 try { 8354 result.addAll(getUsersOfGroup(dbc, principal.getName(), true, false, false)); 8355 } catch (CmsException e) { 8356 if (LOG.isInfoEnabled()) { 8357 LOG.info(e.getLocalizedMessage(), e); 8358 } 8359 } 8360 } else { 8361 result.add((CmsUser)principal); 8362 } 8363 } 8364 return result; 8365 } 8366 8367 /** 8368 * Returns a List of all siblings of the specified resource, 8369 * the specified resource being always part of the result set.<p> 8370 * 8371 * The result is a list of <code>{@link CmsResource}</code> objects.<p> 8372 * 8373 * @param dbc the current database context 8374 * @param resource the resource to read the siblings for 8375 * @param filter a filter object 8376 * 8377 * @return a list of <code>{@link CmsResource}</code> Objects that 8378 * are siblings to the specified resource, 8379 * including the specified resource itself 8380 * 8381 * @throws CmsException if something goes wrong 8382 */ 8383 public List<CmsResource> readSiblings(CmsDbContext dbc, CmsResource resource, CmsResourceFilter filter) 8384 throws CmsException { 8385 8386 List<CmsResource> siblings = getVfsDriver( 8387 dbc).readSiblings(dbc, dbc.currentProject().getUuid(), resource, filter.includeDeleted()); 8388 8389 // important: there is no permission check done on the returned list of siblings 8390 // this is because of possible issues with the "publish all siblings" option, 8391 // moreover the user has read permission for the content through 8392 // the selected sibling anyway 8393 return updateContextDates(dbc, siblings, filter); 8394 } 8395 8396 /** 8397 * Returns the parameters of a resource in the table of all published template resources.<p> 8398 * 8399 * @param dbc the current database context 8400 * @param rfsName the rfs name of the resource 8401 * 8402 * @return the parameter string of the requested resource 8403 * 8404 * @throws CmsException if something goes wrong 8405 */ 8406 public String readStaticExportPublishedResourceParameters(CmsDbContext dbc, String rfsName) throws CmsException { 8407 8408 return getProjectDriver(dbc).readStaticExportPublishedResourceParameters(dbc, rfsName); 8409 } 8410 8411 /** 8412 * Returns a list of all template resources which must be processed during a static export.<p> 8413 * 8414 * @param dbc the current database context 8415 * @param parameterResources flag for reading resources with parameters (1) or without (0) 8416 * @param timestamp for reading the data from the db 8417 * 8418 * @return a list of template resources as <code>{@link String}</code> objects 8419 * 8420 * @throws CmsException if something goes wrong 8421 */ 8422 public List<String> readStaticExportResources(CmsDbContext dbc, int parameterResources, long timestamp) 8423 throws CmsException { 8424 8425 return getProjectDriver(dbc).readStaticExportResources(dbc, parameterResources, timestamp); 8426 } 8427 8428 /** 8429 * Returns the subscribed history resources that were deleted.<p> 8430 * 8431 * @param dbc the database context 8432 * @param poolName the name of the database pool to use 8433 * @param user the user that subscribed to the resource 8434 * @param groups the groups to check subscribed resources for 8435 * @param parent the parent resource (folder) of the deleted resources, if <code>null</code> all deleted resources will be returned 8436 * @param includeSubFolders indicates if the sub folders of the specified folder path should be considered, too 8437 * @param deletedFrom the time stamp from which the resources should have been deleted 8438 * 8439 * @return the subscribed history resources that were deleted 8440 * 8441 * @throws CmsException if something goes wrong 8442 */ 8443 public List<I_CmsHistoryResource> readSubscribedDeletedResources( 8444 CmsDbContext dbc, 8445 String poolName, 8446 CmsUser user, 8447 List<CmsGroup> groups, 8448 CmsResource parent, 8449 boolean includeSubFolders, 8450 long deletedFrom) 8451 throws CmsException { 8452 8453 List<I_CmsHistoryResource> result = getSubscriptionDriver().readSubscribedDeletedResources( 8454 dbc, 8455 poolName, 8456 user, 8457 groups, 8458 parent, 8459 includeSubFolders, 8460 deletedFrom); 8461 8462 return result; 8463 } 8464 8465 /** 8466 * Returns the resources that were subscribed by a user or group set in the filter.<p> 8467 * 8468 * @param dbc the database context 8469 * @param poolName the name of the database pool to use 8470 * @param filter the filter that is used to get the subscribed resources 8471 * 8472 * @return the resources that were subscribed by a user or group set in the filter 8473 * 8474 * @throws CmsException if something goes wrong 8475 */ 8476 public List<CmsResource> readSubscribedResources(CmsDbContext dbc, String poolName, CmsSubscriptionFilter filter) 8477 throws CmsException { 8478 8479 List<CmsResource> result = getSubscriptionDriver().readSubscribedResources(dbc, poolName, filter); 8480 8481 result = filterPermissions(dbc, result, CmsResourceFilter.DEFAULT); 8482 return result; 8483 } 8484 8485 /** 8486 * Reads URL name mapping entries which match the given filter.<p> 8487 * 8488 * @param dbc the database context 8489 * @param online if true, read online URL name mappings, else offline ones 8490 * @param filter the filter for matching the URL name entries 8491 * 8492 * @return the list of URL name mapping entries which match the given filter 8493 * 8494 * @throws CmsDataAccessException if something goes wrong 8495 */ 8496 public List<CmsUrlNameMappingEntry> readUrlNameMappingEntries( 8497 CmsDbContext dbc, 8498 boolean online, 8499 CmsUrlNameMappingFilter filter) 8500 throws CmsDataAccessException { 8501 8502 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 8503 return vfsDriver.readUrlNameMappingEntries(dbc, online, filter); 8504 } 8505 8506 /** 8507 * Reads the URL name mappings matching the given filter.<p> 8508 * 8509 * @param dbc the DB context to use 8510 * @param filter the filter used to select the mapping entries 8511 * @return the entries matching the given filter 8512 * 8513 * @throws CmsDataAccessException if something goes wrong 8514 */ 8515 public List<CmsUrlNameMappingEntry> readUrlNameMappings(CmsDbContext dbc, CmsUrlNameMappingFilter filter) 8516 throws CmsDataAccessException { 8517 8518 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 8519 dbc, 8520 dbc.currentProject().isOnlineProject(), 8521 filter); 8522 return entries; 8523 } 8524 8525 /** 8526 * Reads the newest URL names of a resource for all locales.<p> 8527 * 8528 * @param dbc the database context 8529 * @param id the resource's structure id 8530 * 8531 * @return the url names for the locales 8532 * 8533 * @throws CmsDataAccessException if the database operation failed 8534 */ 8535 public List<String> readUrlNamesForAllLocales(CmsDbContext dbc, CmsUUID id) throws CmsDataAccessException { 8536 8537 List<String> result = new ArrayList<String>(); 8538 List<CmsUrlNameMappingEntry> entries = getVfsDriver(dbc).readUrlNameMappingEntries( 8539 dbc, 8540 dbc.currentProject().isOnlineProject(), 8541 CmsUrlNameMappingFilter.ALL.filterStructureId(id)); 8542 ArrayListMultimap<String, CmsUrlNameMappingEntry> entriesByLocale = ArrayListMultimap.create(); 8543 for (CmsUrlNameMappingEntry entry : entries) { 8544 String localeKey = entry.getLocale(); 8545 entriesByLocale.put(localeKey, entry); 8546 } 8547 8548 for (String localeKey : entriesByLocale.keySet()) { 8549 List<CmsUrlNameMappingEntry> entrs = entriesByLocale.get(localeKey); 8550 CmsUrlNameMappingEntry maxEntryForLocale = Collections.max(entrs, new UrlNameMappingComparator()); 8551 result.add(maxEntryForLocale.getName()); 8552 } 8553 return result; 8554 } 8555 8556 /** 8557 * Returns a user object based on the id of a user.<p> 8558 * 8559 * @param dbc the current database context 8560 * @param id the id of the user to read 8561 * 8562 * @return the user read 8563 * 8564 * @throws CmsException if something goes wrong 8565 */ 8566 public CmsUser readUser(CmsDbContext dbc, CmsUUID id) throws CmsException { 8567 8568 CmsUser user = m_monitor.getCachedUser(id.toString()); 8569 if (user == null) { 8570 user = getUserDriver(dbc).readUser(dbc, id); 8571 m_monitor.cacheUser(user); 8572 } 8573 // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects 8574 return user.clone(); 8575 } 8576 8577 /** 8578 * Returns a user object.<p> 8579 * 8580 * @param dbc the current database context 8581 * @param username the name of the user that is to be read 8582 * 8583 * @return user read 8584 * 8585 * @throws CmsDataAccessException if operation was not successful 8586 */ 8587 public CmsUser readUser(CmsDbContext dbc, String username) throws CmsDataAccessException { 8588 8589 CmsUser user = m_monitor.getCachedUser(username); 8590 if (user == null) { 8591 user = getUserDriver(dbc).readUser(dbc, username); 8592 m_monitor.cacheUser(user); 8593 } 8594 // important: do not return the cached user object, but a clone to avoid unwanted changes on cached objects 8595 return user.clone(); 8596 } 8597 8598 /** 8599 * Returns a user object if the password for the user is correct.<p> 8600 * 8601 * If the user/pwd pair is not valid a <code>{@link CmsException}</code> is thrown.<p> 8602 * 8603 * @param dbc the current database context 8604 * @param username the username of the user that is to be read 8605 * @param password the password of the user that is to be read 8606 * 8607 * @return user read 8608 * 8609 * @throws CmsException if operation was not successful 8610 */ 8611 public CmsUser readUser(CmsDbContext dbc, String username, String password) throws CmsException { 8612 8613 // don't read user from cache here because password may have changed 8614 CmsUser user = getUserDriver(dbc).readUser(dbc, username, password, null); 8615 m_monitor.cacheUser(user); 8616 return user; 8617 } 8618 8619 /** 8620 * Removes an access control entry for a given resource and principal.<p> 8621 * 8622 * @param dbc the current database context 8623 * @param resource the resource 8624 * @param principal the id of the principal to remove the the access control entry for 8625 * 8626 * @throws CmsException if something goes wrong 8627 */ 8628 public void removeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsUUID principal) 8629 throws CmsException { 8630 8631 // remove the ace 8632 getUserDriver(dbc).removeAccessControlEntry(dbc, dbc.currentProject(), resource.getResourceId(), principal); 8633 8634 // log it 8635 log( 8636 dbc, 8637 new CmsLogEntry( 8638 dbc, 8639 resource.getStructureId(), 8640 CmsLogEntryType.RESOURCE_PERMISSIONS, 8641 new String[] {resource.getRootPath()}), 8642 false); 8643 8644 // update the "last modified" information 8645 setDateLastModified(dbc, resource, resource.getDateLastModified()); 8646 8647 // clear the cache 8648 m_monitor.clearAccessControlListCache(); 8649 8650 // fire a resource modification event 8651 Map<String, Object> data = new HashMap<String, Object>(2); 8652 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 8653 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 8654 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 8655 } 8656 8657 /** 8658 * Removes a resource from the given organizational unit.<p> 8659 * 8660 * @param dbc the current db context 8661 * @param orgUnit the organizational unit to remove the resource from 8662 * @param resource the resource that is to be removed from the organizational unit 8663 * 8664 * @throws CmsException if something goes wrong 8665 * 8666 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 8667 * @see org.opencms.security.CmsOrgUnitManager#addResourceToOrgUnit(CmsObject, String, String) 8668 */ 8669 public void removeResourceFromOrgUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsResource resource) 8670 throws CmsException { 8671 8672 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 8673 getUserDriver(dbc).removeResourceFromOrganizationalUnit(dbc, orgUnit, resource); 8674 } 8675 8676 /** 8677 * Removes a resource from the current project of the user.<p> 8678 * 8679 * @param dbc the current database context 8680 * @param resource the resource to apply this operation to 8681 * 8682 * @throws CmsException if something goes wrong 8683 * 8684 * @see CmsObject#copyResourceToProject(String) 8685 * @see I_CmsResourceType#copyResourceToProject(CmsObject, CmsSecurityManager, CmsResource) 8686 */ 8687 public void removeResourceFromProject(CmsDbContext dbc, CmsResource resource) throws CmsException { 8688 8689 // remove the resource to the project only if the resource is already in the project 8690 if (isInsideCurrentProject(dbc, resource.getRootPath())) { 8691 // check if there are already any subfolders of this resource 8692 I_CmsProjectDriver projectDriver = getProjectDriver(dbc); 8693 if (resource.isFolder()) { 8694 List<String> projectResources = projectDriver.readProjectResources(dbc, dbc.currentProject()); 8695 for (int i = 0; i < projectResources.size(); i++) { 8696 String resname = projectResources.get(i); 8697 if (resname.startsWith(resource.getRootPath())) { 8698 // delete the existing project resource first 8699 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resname); 8700 } 8701 } 8702 } 8703 try { 8704 projectDriver.deleteProjectResource(dbc, dbc.currentProject().getUuid(), resource.getRootPath()); 8705 } catch (CmsException exc) { 8706 // if the subfolder exists already - all is ok 8707 } finally { 8708 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT_RESOURCES); 8709 8710 OpenCms.fireCmsEvent( 8711 new CmsEvent( 8712 I_CmsEventListener.EVENT_PROJECT_MODIFIED, 8713 Collections.<String, Object> singletonMap("project", dbc.currentProject()))); 8714 } 8715 } 8716 } 8717 8718 /** 8719 * Removes the given resource to the given user's publish list.<p> 8720 * 8721 * @param dbc the database context 8722 * @param userId the user's id 8723 * @param structureIds the collection of structure IDs to remove 8724 * 8725 * @throws CmsDataAccessException if something goes wrong 8726 */ 8727 public void removeResourceFromUsersPubList(CmsDbContext dbc, CmsUUID userId, Collection<CmsUUID> structureIds) 8728 throws CmsDataAccessException { 8729 8730 for (CmsUUID structureId : structureIds) { 8731 CmsLogEntry entry = new CmsLogEntry( 8732 userId, 8733 System.currentTimeMillis(), 8734 structureId, 8735 CmsLogEntryType.RESOURCE_HIDDEN, 8736 new String[] {readResource(dbc, structureId, CmsResourceFilter.ALL).getRootPath()}); 8737 log(dbc, entry, true); 8738 } 8739 } 8740 8741 /** 8742 * Removes a user from a group.<p> 8743 * 8744 * @param dbc the current database context 8745 * @param username the name of the user that is to be removed from the group 8746 * @param groupname the name of the group 8747 * @param readRoles if to read roles or groups 8748 * 8749 * @throws CmsException if operation was not successful 8750 * @throws CmsIllegalArgumentException if the given user was not member in the given group 8751 * @throws CmsDbEntryNotFoundException if the given group was not found 8752 * @throws CmsSecurityException if the given user was <b>read as 'null' from the database</b> 8753 * 8754 * @see #addUserToGroup(CmsDbContext, String, String, boolean) 8755 */ 8756 public void removeUserFromGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 8757 throws CmsException, CmsIllegalArgumentException, CmsDbEntryNotFoundException, CmsSecurityException { 8758 8759 CmsGroup group = readGroup(dbc, groupname); 8760 //check if group exists 8761 if (group == null) { 8762 // the group does not exists 8763 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 8764 } 8765 if (group.isVirtual() && !readRoles) { 8766 // if removing a user from a virtual role treat it as removing the user from the role 8767 removeUserFromGroup(dbc, username, CmsRole.valueOf(group).getGroupName(), true); 8768 return; 8769 } 8770 if (group.isVirtual()) { 8771 // this is an hack so to prevent a unlimited recursive calls 8772 readRoles = false; 8773 } 8774 if ((readRoles && !group.isRole()) || (!readRoles && group.isRole())) { 8775 // we want a role but we got a group, or the other way 8776 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 8777 } 8778 8779 boolean skipRemove = false; 8780 // test if this user is existing in the group 8781 if (!userInGroup(dbc, username, groupname, readRoles)) { 8782 if (readRoles) { 8783 // Sometimes users can end up with the default groups corresponding to roles (Administrators, Users) without the actual roles. 8784 // 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 8785 // want to throw an exception then, because it would prevent the code that actually removes the user from the group from running. 8786 LOG.warn( 8787 "Trying to remove user from role that they are not a member of (user: " 8788 + username 8789 + ", group: " 8790 + groupname 8791 + ")"); 8792 skipRemove = true; 8793 } else { 8794 // user is not in the group, throw exception 8795 throw new CmsIllegalArgumentException( 8796 Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname)); 8797 } 8798 } 8799 8800 CmsUser user = readUser(dbc, username); 8801 //check if the user exists 8802 if (user == null) { 8803 // the user does not exists 8804 throw new CmsIllegalArgumentException( 8805 Messages.get().container(Messages.ERR_USER_NOT_IN_GROUP_2, username, groupname)); 8806 } 8807 8808 if (readRoles) { 8809 CmsRole role = CmsRole.valueOf(group); 8810 // update virtual groups 8811 Iterator<CmsGroup> it = getVirtualGroupsForRole(dbc, role).iterator(); 8812 while (it.hasNext()) { 8813 CmsGroup virtualGroup = it.next(); 8814 if (userInGroup(dbc, username, virtualGroup.getName(), false)) { 8815 // here we say readroles = true, to prevent an unlimited recursive calls 8816 removeUserFromGroup(dbc, username, virtualGroup.getName(), true); 8817 } 8818 } 8819 } 8820 if (!skipRemove) { 8821 getUserDriver(dbc).deleteUserInGroup(dbc, user.getId(), group.getId()); 8822 } 8823 8824 // flush relevant caches 8825 if (readRoles) { 8826 m_monitor.flushCache(CmsMemoryMonitor.CacheType.HAS_ROLE, CmsMemoryMonitor.CacheType.ROLE_LIST); 8827 } 8828 m_monitor.flushUserGroups(user.getId()); 8829 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 8830 8831 if (!dbc.getProjectId().isNullUUID()) { 8832 // user modified event is not needed 8833 return; 8834 } 8835 // fire user modified event 8836 Map<String, Object> eventData = new HashMap<String, Object>(); 8837 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 8838 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 8839 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 8840 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, group.getName()); 8841 eventData.put( 8842 I_CmsEventListener.KEY_USER_ACTION, 8843 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_REMOVE_USER_FROM_GROUP); 8844 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 8845 8846 } 8847 8848 /** 8849 * Repairs broken categories.<p> 8850 * 8851 * @param dbc the database context 8852 * @param projectId the project id 8853 * @param resource the resource to repair the categories for 8854 * 8855 * @throws CmsException if something goes wrong 8856 */ 8857 public void repairCategories(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsException { 8858 8859 CmsObject cms = OpenCms.initCmsObject(new CmsObject(getSecurityManager(), dbc.getRequestContext())); 8860 cms.getRequestContext().setSiteRoot(""); 8861 cms.getRequestContext().setCurrentProject(readProject(dbc, projectId)); 8862 CmsCategoryService.getInstance().repairRelations(cms, resource); 8863 } 8864 8865 /** 8866 * Replaces the content, type and properties of a resource.<p> 8867 * 8868 * @param dbc the current database context 8869 * @param resource the name of the resource to apply this operation to 8870 * @param type the new type of the resource 8871 * @param content the new content of the resource 8872 * @param properties the new properties of the resource 8873 * 8874 * @throws CmsException if something goes wrong 8875 * 8876 * @see CmsObject#replaceResource(String, int, byte[], List) 8877 * @see I_CmsResourceType#replaceResource(CmsObject, CmsSecurityManager, CmsResource, int, byte[], List) 8878 */ 8879 @SuppressWarnings("javadoc") 8880 public void replaceResource( 8881 CmsDbContext dbc, 8882 CmsResource resource, 8883 int type, 8884 byte[] content, 8885 List<CmsProperty> properties) 8886 throws CmsException { 8887 8888 // replace the existing with the new file content 8889 getVfsDriver(dbc).replaceResource(dbc, resource, content, type); 8890 8891 if ((properties != null) && !properties.isEmpty()) { 8892 // write the properties 8893 getVfsDriver(dbc).writePropertyObjects(dbc, dbc.currentProject(), resource, properties); 8894 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 8895 } 8896 8897 // update the resource state 8898 if (resource.getState().isUnchanged()) { 8899 resource.setState(CmsResource.STATE_CHANGED); 8900 } 8901 resource.setUserLastModified(dbc.currentUser().getId()); 8902 8903 // log it 8904 log( 8905 dbc, 8906 new CmsLogEntry( 8907 dbc, 8908 resource.getStructureId(), 8909 CmsLogEntryType.RESOURCE_CONTENT_MODIFIED, 8910 new String[] {resource.getRootPath()}), 8911 false); 8912 8913 setDateLastModified(dbc, resource, System.currentTimeMillis()); 8914 8915 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false); 8916 8917 deleteRelationsWithSiblings(dbc, resource); 8918 8919 // clear the cache 8920 m_monitor.clearResourceCache(); 8921 8922 if ((properties != null) && !properties.isEmpty()) { 8923 // resource and properties were modified 8924 OpenCms.fireCmsEvent( 8925 new CmsEvent( 8926 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 8927 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource))); 8928 } else { 8929 // only the resource was modified 8930 Map<String, Object> data = new HashMap<String, Object>(2); 8931 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 8932 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 8933 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 8934 } 8935 } 8936 8937 /** 8938 * Resets the password for a specified user.<p> 8939 * 8940 * @param dbc the current database context 8941 * @param username the name of the user 8942 * @param oldPassword the old password 8943 * @param secondFactor the second factor data used for 2FA 8944 * @param newPassword the new password 8945 * 8946 * @throws CmsException if the user data could not be read from the database 8947 * @throws CmsSecurityException if the specified username and old password could not be verified 8948 */ 8949 public void resetPassword( 8950 CmsDbContext dbc, 8951 String username, 8952 String oldPassword, 8953 CmsSecondFactorInfo secondFactor, 8954 String newPassword) 8955 throws CmsException, CmsSecurityException { 8956 8957 if ((oldPassword != null) && (newPassword != null)) { 8958 8959 CmsUser user = null; 8960 8961 if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) { 8962 validatePassword(newPassword); 8963 } 8964 8965 // read the user as a system user to verify that the specified old password is correct 8966 try { 8967 user = getUserDriver(dbc).readUser(dbc, username, oldPassword, null); 8968 } catch (CmsDbEntryNotFoundException e) { 8969 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), e); 8970 } 8971 8972 if ((user == null) || user.isManaged()) { 8973 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username)); 8974 } 8975 8976 CmsTwoFactorAuthenticationHandler twoFactorHandler = OpenCms.getTwoFactorAuthenticationHandler(); 8977 if (twoFactorHandler.needsTwoFactorAuthentication(user) && twoFactorHandler.hasSecondFactor(user)) { 8978 if (!twoFactorHandler.verifySecondFactor(user, secondFactor)) { 8979 throw new CmsDataAccessException( 8980 Messages.get().container(Messages.ERR_RESET_PASSWORD_1, username), 8981 new RuntimeException("Verification code mismatch")); 8982 } 8983 } 8984 8985 getUserDriver(dbc).writePassword(dbc, username, oldPassword, newPassword); 8986 user.getAdditionalInfo().put( 8987 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 8988 "" + System.currentTimeMillis()); 8989 user.deleteAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_PASSWORD_RESET); 8990 getUserDriver(dbc).writeUser(dbc, user); 8991 8992 if (!dbc.getProjectId().isNullUUID()) { 8993 // user modified event is not needed 8994 return; 8995 } 8996 // fire user modified event 8997 Map<String, Object> eventData = new HashMap<String, Object>(); 8998 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 8999 eventData.put( 9000 I_CmsEventListener.KEY_USER_ACTION, 9001 I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD); 9002 eventData.put( 9003 I_CmsEventListener.KEY_USER_CHANGES, 9004 Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA)); 9005 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9006 9007 } else if (CmsStringUtil.isEmpty(oldPassword)) { 9008 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_OLD_MISSING_0)); 9009 } else if (CmsStringUtil.isEmpty(newPassword)) { 9010 throw new CmsDataAccessException(Messages.get().container(Messages.ERR_PWD_NEW_MISSING_0)); 9011 } 9012 } 9013 9014 /** 9015 * Restores a deleted resource identified by its structure id from the historical archive.<p> 9016 * 9017 * @param dbc the current database context 9018 * @param structureId the structure id of the resource to restore 9019 * 9020 * @throws CmsException if something goes wrong 9021 * 9022 * @see CmsObject#restoreDeletedResource(CmsUUID) 9023 */ 9024 public CmsResource restoreDeletedResource(CmsDbContext dbc, CmsUUID structureId) throws CmsException { 9025 9026 // get the last version, which should be the deleted one 9027 int version = getHistoryDriver(dbc).readLastVersion(dbc, structureId); 9028 // get that version 9029 I_CmsHistoryResource histRes = getHistoryDriver(dbc).readResource(dbc, structureId, version); 9030 9031 // check the parent path 9032 CmsResource parent; 9033 try { 9034 // try to read the parent resource by id 9035 parent = getVfsDriver(dbc).readResource(dbc, dbc.currentProject().getUuid(), histRes.getParentId(), true); 9036 } catch (CmsVfsResourceNotFoundException e) { 9037 // if not found try to read the parent resource by name 9038 try { 9039 // try to read the parent resource by id 9040 parent = getVfsDriver(dbc).readResource( 9041 dbc, 9042 dbc.currentProject().getUuid(), 9043 CmsResource.getParentFolder(histRes.getRootPath()), 9044 true); 9045 } catch (CmsVfsResourceNotFoundException e1) { 9046 // if not found try to restore the parent resource 9047 restoreDeletedResource(dbc, histRes.getParentId()); 9048 parent = readResource(dbc, histRes.getParentId(), CmsResourceFilter.IGNORE_EXPIRATION); 9049 } 9050 } 9051 // check write permissions 9052 m_securityManager.checkPermissions( 9053 dbc, 9054 parent, 9055 CmsPermissionSet.ACCESS_WRITE, 9056 false, 9057 CmsResourceFilter.IGNORE_EXPIRATION); 9058 9059 // check the name 9060 String path = parent.getRootPath(); 9061 String resName = CmsResource.getName(histRes.getRootPath()); // name 9062 String ext = ""; 9063 if (resName.charAt(resName.length() - 1) == '/') { 9064 resName = resName.substring(0, resName.length() - 1); 9065 } else { 9066 ext = CmsFileUtil.getExtension(resName); // extension 9067 } 9068 String nameWOExt = resName.substring(0, resName.length() - ext.length()); // name without extension 9069 for (int i = 1; true; i++) { 9070 try { 9071 readResource(dbc, path + resName, CmsResourceFilter.ALL); 9072 resName = nameWOExt + "_" + i + ext; 9073 // try the next resource name with following schema: path/name_{i}.ext 9074 } catch (CmsVfsResourceNotFoundException e) { 9075 // ok, we found a not used resource name 9076 break; 9077 } 9078 } 9079 9080 // check structure id 9081 CmsUUID id = structureId; 9082 if (getVfsDriver(dbc).validateStructureIdExists(dbc, dbc.currentProject().getUuid(), structureId)) { 9083 // should never happen, but if already exists create a new one 9084 id = new CmsUUID(); 9085 } 9086 9087 byte[] contents = null; 9088 boolean isFolder = true; 9089 9090 // do we need the contents? 9091 if (histRes instanceof CmsFile) { 9092 contents = ((CmsFile)histRes).getContents(); 9093 if ((contents == null) || (contents.length == 0)) { 9094 contents = getHistoryDriver(dbc).readContent(dbc, histRes.getResourceId(), histRes.getPublishTag()); 9095 } 9096 isFolder = false; 9097 } 9098 9099 // now read the historical properties 9100 List<CmsProperty> properties = getHistoryDriver(dbc).readProperties(dbc, histRes); 9101 9102 // create the object to create 9103 CmsResource newResource = new CmsResource( 9104 id, 9105 histRes.getResourceId(), 9106 path + resName, 9107 histRes.getTypeId(), 9108 isFolder, 9109 histRes.getFlags(), 9110 dbc.currentProject().getUuid(), 9111 CmsResource.STATE_NEW, 9112 histRes.getDateCreated(), 9113 histRes.getUserCreated(), 9114 histRes.getDateLastModified(), 9115 dbc.currentUser().getId(), 9116 histRes.getDateReleased(), 9117 histRes.getDateExpired(), 9118 histRes.getSiblingCount(), 9119 histRes.getLength(), 9120 histRes.getDateContent(), 9121 histRes.getVersion()); 9122 9123 // log it 9124 log( 9125 dbc, 9126 new CmsLogEntry( 9127 dbc, 9128 newResource.getStructureId(), 9129 CmsLogEntryType.RESOURCE_RESTORE_DELETED, 9130 new String[] {newResource.getRootPath()}), 9131 false); 9132 9133 // prevent the date last modified is set to the current time 9134 newResource.setDateLastModified(newResource.getDateLastModified()); 9135 // restore the resource! 9136 CmsResource resource = createResource(dbc, path + resName, newResource, contents, properties, true); 9137 // set resource state to changed 9138 newResource.setState(CmsResource.STATE_CHANGED); 9139 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), newResource, UPDATE_RESOURCE_STATE, false); 9140 newResource.setState(CmsResource.STATE_NEW); 9141 // fire the event 9142 Map<String, Object> data = new HashMap<String, Object>(2); 9143 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9144 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9145 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9146 return newResource; 9147 } 9148 9149 /** 9150 * Restores a resource in the current project with a version from the historical archive.<p> 9151 * 9152 * @param dbc the current database context 9153 * @param resource the resource to restore from the archive 9154 * @param version the version number to restore from the archive 9155 * 9156 * @throws CmsException if something goes wrong 9157 * 9158 * @see CmsObject#restoreResourceVersion(CmsUUID, int) 9159 * @see I_CmsResourceType#restoreResource(CmsObject, CmsSecurityManager, CmsResource, int) 9160 */ 9161 public CmsResource restoreResource(CmsDbContext dbc, CmsResource resource, int version) throws CmsException { 9162 9163 I_CmsHistoryResource historyResource = readResource(dbc, resource, version); 9164 CmsResourceState state = CmsResource.STATE_CHANGED; 9165 if (resource.getState().isNew()) { 9166 state = CmsResource.STATE_NEW; 9167 } 9168 int newVersion = resource.getVersion(); 9169 if (resource.getState().isUnchanged()) { 9170 newVersion++; 9171 } 9172 CmsResource newResource = null; 9173 // is the resource a file? 9174 if (historyResource instanceof CmsFile) { 9175 // get the historical up flags 9176 int flags = historyResource.getFlags(); 9177 if (resource.isLabeled()) { 9178 // set the flag for labeled links on the restored file 9179 flags |= CmsResource.FLAG_LABELED; 9180 } 9181 CmsFile newFile = new CmsFile( 9182 resource.getStructureId(), 9183 resource.getResourceId(), 9184 resource.getRootPath(), 9185 historyResource.getTypeId(), 9186 flags, 9187 dbc.currentProject().getUuid(), 9188 state, 9189 resource.getDateCreated(), 9190 historyResource.getUserCreated(), 9191 resource.getDateLastModified(), 9192 dbc.currentUser().getId(), 9193 historyResource.getDateReleased(), 9194 historyResource.getDateExpired(), 9195 resource.getSiblingCount(), 9196 historyResource.getLength(), 9197 historyResource.getDateContent(), 9198 newVersion, 9199 readFile(dbc, (CmsHistoryFile)historyResource).getContents()); 9200 9201 // log it 9202 log( 9203 dbc, 9204 new CmsLogEntry( 9205 dbc, 9206 newFile.getStructureId(), 9207 CmsLogEntryType.RESOURCE_HISTORY, 9208 new String[] {newFile.getRootPath()}), 9209 false); 9210 9211 newResource = writeFile(dbc, newFile); 9212 } else { 9213 // it is a folder! 9214 newResource = new CmsFolder( 9215 resource.getStructureId(), 9216 resource.getResourceId(), 9217 resource.getRootPath(), 9218 historyResource.getTypeId(), 9219 historyResource.getFlags(), 9220 dbc.currentProject().getUuid(), 9221 state, 9222 resource.getDateCreated(), 9223 historyResource.getUserCreated(), 9224 resource.getDateLastModified(), 9225 dbc.currentUser().getId(), 9226 historyResource.getDateReleased(), 9227 historyResource.getDateExpired(), 9228 newVersion); 9229 9230 // log it 9231 log( 9232 dbc, 9233 new CmsLogEntry( 9234 dbc, 9235 newResource.getStructureId(), 9236 CmsLogEntryType.RESOURCE_HISTORY, 9237 new String[] {newResource.getRootPath()}), 9238 false); 9239 9240 writeResource(dbc, newResource); 9241 } 9242 if (newResource != null) { 9243 // now read the historical properties 9244 List<CmsProperty> historyProperties = getHistoryDriver(dbc).readProperties(dbc, historyResource); 9245 // remove all properties 9246 deleteAllProperties(dbc, newResource.getRootPath()); 9247 // write them to the restored resource 9248 writePropertyObjects(dbc, newResource, historyProperties, false); 9249 9250 m_monitor.clearResourceCache(); 9251 } 9252 9253 Map<String, Object> data = new HashMap<String, Object>(2); 9254 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9255 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE | CHANGED_CONTENT)); 9256 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9257 return newResource; 9258 } 9259 9260 /** 9261 * Saves a list of aliases for the same structure id, replacing any aliases for the same structure id.<p> 9262 * 9263 * @param dbc the current database context 9264 * @param project the current project 9265 * @param structureId the structure id for which the aliases should be saved 9266 * @param aliases the list of aliases to save 9267 * 9268 * @throws CmsException if something goes wrong 9269 */ 9270 public void saveAliases(CmsDbContext dbc, CmsProject project, CmsUUID structureId, List<CmsAlias> aliases) 9271 throws CmsException { 9272 9273 for (CmsAlias alias : aliases) { 9274 if (!structureId.equals(alias.getStructureId())) { 9275 throw new IllegalArgumentException("Aliases to replace must have the same structure id!"); 9276 } 9277 } 9278 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 9279 vfsDriver.deleteAliases(dbc, project, new CmsAliasFilter(null, null, structureId)); 9280 for (CmsAlias alias : aliases) { 9281 String aliasPath = alias.getAliasPath(); 9282 if (CmsAlias.ALIAS_PATTERN.matcher(aliasPath).matches()) { 9283 vfsDriver.insertAlias(dbc, project, alias); 9284 } else { 9285 LOG.error("Invalid alias path: " + aliasPath); 9286 } 9287 } 9288 } 9289 9290 /** 9291 * Replaces the complete list of rewrite aliases for a given site root.<p> 9292 * 9293 * @param dbc the current database context 9294 * @param siteRoot the site root for which the rewrite aliases should be replaced 9295 * @param newAliases the new aliases for the given site root 9296 * @throws CmsException if something goes wrong 9297 */ 9298 public void saveRewriteAliases(CmsDbContext dbc, String siteRoot, List<CmsRewriteAlias> newAliases) 9299 throws CmsException { 9300 9301 CmsRewriteAliasFilter filter = new CmsRewriteAliasFilter().setSiteRoot(siteRoot); 9302 getVfsDriver(dbc).deleteRewriteAliases(dbc, filter); 9303 getVfsDriver(dbc).insertRewriteAliases(dbc, newAliases); 9304 } 9305 9306 /** 9307 * Searches for users which fit the given criteria.<p> 9308 * 9309 * @param dbc the database context 9310 * @param searchParams the search criteria 9311 * 9312 * @return the users which fit the search criteria 9313 * 9314 * @throws CmsDataAccessException if something goes wrong 9315 */ 9316 public List<CmsUser> searchUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams 9317 9318 ) throws CmsDataAccessException { 9319 9320 return getUserDriver(dbc).searchUsers(dbc, searchParams); 9321 } 9322 9323 /** 9324 * Changes the "expire" date of a resource.<p> 9325 * 9326 * @param dbc the current database context 9327 * @param resource the resource to touch 9328 * @param dateExpired the new expire date of the resource 9329 * 9330 * @throws CmsDataAccessException if something goes wrong 9331 * 9332 * @see CmsObject#setDateExpired(String, long, boolean) 9333 * @see I_CmsResourceType#setDateExpired(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9334 */ 9335 public void setDateExpired(CmsDbContext dbc, CmsResource resource, long dateExpired) throws CmsDataAccessException { 9336 9337 resource.setDateExpired(dateExpired); 9338 if (resource.getState().isUnchanged()) { 9339 resource.setState(CmsResource.STATE_CHANGED); 9340 } 9341 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false); 9342 9343 // modify the last modified project reference 9344 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false); 9345 // log 9346 log( 9347 dbc, 9348 new CmsLogEntry( 9349 dbc, 9350 resource.getStructureId(), 9351 CmsLogEntryType.RESOURCE_DATE_EXPIRED, 9352 new String[] {resource.getRootPath()}), 9353 false); 9354 9355 // clear the cache 9356 m_monitor.clearResourceCache(); 9357 9358 // fire the event 9359 Map<String, Object> data = new HashMap<String, Object>(2); 9360 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9361 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_TIMEFRAME)); 9362 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9363 } 9364 9365 /** 9366 * Changes the "last modified" timestamp of a resource.<p> 9367 * 9368 * @param dbc the current database context 9369 * @param resource the resource to touch 9370 * @param dateLastModified the new last modified date of the resource 9371 * 9372 * @throws CmsDataAccessException if something goes wrong 9373 * 9374 * @see CmsObject#setDateLastModified(String, long, boolean) 9375 * @see I_CmsResourceType#setDateLastModified(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9376 */ 9377 public void setDateLastModified(CmsDbContext dbc, CmsResource resource, long dateLastModified) 9378 throws CmsDataAccessException { 9379 9380 // modify the last modification date 9381 resource.setDateLastModified(dateLastModified); 9382 if (resource.getState().isUnchanged()) { 9383 resource.setState(CmsResource.STATE_CHANGED); 9384 } else if (resource.getState().isNew() && (resource.getSiblingCount() > 1)) { 9385 // in case of new resources with siblings make sure the state is correct 9386 resource.setState(CmsResource.STATE_CHANGED); 9387 } 9388 resource.setUserLastModified(dbc.currentUser().getId()); 9389 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE, false); 9390 9391 log( 9392 dbc, 9393 new CmsLogEntry( 9394 dbc, 9395 resource.getStructureId(), 9396 CmsLogEntryType.RESOURCE_TOUCHED, 9397 new String[] {resource.getRootPath()}), 9398 false); 9399 9400 // clear the cache 9401 m_monitor.clearResourceCache(); 9402 9403 // fire the event 9404 Map<String, Object> data = new HashMap<String, Object>(2); 9405 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9406 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_LASTMODIFIED)); 9407 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9408 } 9409 9410 /** 9411 * Changes the "release" date of a resource.<p> 9412 * 9413 * @param dbc the current database context 9414 * @param resource the resource to touch 9415 * @param dateReleased the new release date of the resource 9416 * 9417 * @throws CmsDataAccessException if something goes wrong 9418 * 9419 * @see CmsObject#setDateReleased(String, long, boolean) 9420 * @see I_CmsResourceType#setDateReleased(CmsObject, CmsSecurityManager, CmsResource, long, boolean) 9421 */ 9422 public void setDateReleased(CmsDbContext dbc, CmsResource resource, long dateReleased) 9423 throws CmsDataAccessException { 9424 9425 // modify the last modification date 9426 resource.setDateReleased(dateReleased); 9427 if (resource.getState().isUnchanged()) { 9428 resource.setState(CmsResource.STATE_CHANGED); 9429 } 9430 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_STRUCTURE, false); 9431 9432 // modify the last modified project reference 9433 getVfsDriver(dbc).writeResourceState(dbc, dbc.currentProject(), resource, UPDATE_RESOURCE_PROJECT, false); 9434 // log it 9435 log( 9436 dbc, 9437 new CmsLogEntry( 9438 dbc, 9439 resource.getStructureId(), 9440 CmsLogEntryType.RESOURCE_DATE_RELEASED, 9441 new String[] {resource.getRootPath()}), 9442 false); 9443 9444 // clear the cache 9445 m_monitor.clearResourceCache(); 9446 9447 // fire the event 9448 Map<String, Object> data = new HashMap<String, Object>(2); 9449 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9450 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_TIMEFRAME)); 9451 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9452 } 9453 9454 /** 9455 * Sets a new parent group for an already existing group.<p> 9456 * 9457 * @param dbc the current database context 9458 * @param groupName the name of the group that should be written 9459 * @param parentGroupName the name of the parent group to set, 9460 * or <code>null</code> if the parent 9461 * group should be deleted. 9462 * 9463 * @throws CmsException if operation was not successful 9464 * @throws CmsDataAccessException if the group with <code>groupName</code> could not be read from VFS 9465 */ 9466 public void setParentGroup(CmsDbContext dbc, String groupName, String parentGroupName) 9467 throws CmsException, CmsDataAccessException { 9468 9469 CmsGroup group = readGroup(dbc, groupName); 9470 CmsUUID parentGroupId = CmsUUID.getNullUUID(); 9471 9472 // if the group exists, use its id, else set to unknown. 9473 if (parentGroupName != null) { 9474 parentGroupId = readGroup(dbc, parentGroupName).getId(); 9475 } 9476 9477 group.setParentId(parentGroupId); 9478 9479 // write the changes to the cms 9480 writeGroup(dbc, group); 9481 } 9482 9483 /** 9484 * Sets the password for a user.<p> 9485 * 9486 * @param dbc the current database context 9487 * @param username the name of the user 9488 * @param newPassword the new password 9489 * 9490 * @throws CmsException if operation was not successful 9491 * @throws CmsIllegalArgumentException if the user with the <code>username</code> was not found 9492 */ 9493 public void setPassword(CmsDbContext dbc, String username, String newPassword) 9494 throws CmsException, CmsIllegalArgumentException { 9495 9496 if (dbc.getRequestContext().getAttribute(CmsUserDriver.REQ_ATTR_DONT_DIGEST_PASSWORD) == null) { 9497 validatePassword(newPassword); 9498 } 9499 9500 // read the user as a system user to verify that the specified old password is correct 9501 CmsUser user = getUserDriver(dbc).readUser(dbc, username); 9502 // only continue if not found and read user from web might succeed 9503 getUserDriver(dbc).writePassword(dbc, username, null, newPassword); 9504 user.getAdditionalInfo().put( 9505 CmsUserSettings.ADDITIONAL_INFO_LAST_PASSWORD_CHANGE, 9506 "" + System.currentTimeMillis()); 9507 getUserDriver(dbc).writeUser(dbc, user); 9508 9509 // fire user modified event 9510 Map<String, Object> eventData = new HashMap<String, Object>(); 9511 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9512 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_RESET_PASSWORD); 9513 eventData.put( 9514 I_CmsEventListener.KEY_USER_CHANGES, 9515 Integer.valueOf(CmsUser.FLAG_CORE_DATA | CmsUser.FLAG_CORE_DATA)); 9516 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9517 } 9518 9519 /** 9520 * Marks a subscribed resource as deleted.<p> 9521 * 9522 * @param dbc the database context 9523 * @param poolName the name of the database pool to use 9524 * @param resource the subscribed resource to mark as deleted 9525 * 9526 * @throws CmsException if something goes wrong 9527 */ 9528 public void setSubscribedResourceAsDeleted(CmsDbContext dbc, String poolName, CmsResource resource) 9529 throws CmsException { 9530 9531 getSubscriptionDriver().setSubscribedResourceAsDeleted(dbc, poolName, resource); 9532 } 9533 9534 /** 9535 * Moves an user to the given organizational unit.<p> 9536 * 9537 * @param dbc the current db context 9538 * @param orgUnit the organizational unit to add the resource to 9539 * @param user the user that is to be moved to the organizational unit 9540 * 9541 * @throws CmsException if something goes wrong 9542 * 9543 * @see org.opencms.security.CmsOrgUnitManager#setUsersOrganizationalUnit(CmsObject, String, String) 9544 */ 9545 public void setUsersOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit orgUnit, CmsUser user) 9546 throws CmsException { 9547 9548 if (!getGroupsOfUser(dbc, user.getName(), false).isEmpty()) { 9549 throw new CmsDbConsistencyException( 9550 Messages.get().container(Messages.ERR_ORGUNIT_MOVE_USER_2, orgUnit.getName(), user.getName())); 9551 } 9552 9553 // move the principal 9554 getUserDriver(dbc).setUsersOrganizationalUnit(dbc, orgUnit, user); 9555 // remove the principal from cache 9556 m_monitor.clearUserCache(user); 9557 9558 if (!dbc.getProjectId().isNullUUID()) { 9559 // user modified event is not needed 9560 return; 9561 } 9562 // fire user modified event 9563 Map<String, Object> eventData = new HashMap<String, Object>(); 9564 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9565 eventData.put(I_CmsEventListener.KEY_OU_NAME, user.getOuFqn()); 9566 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_SET_OU); 9567 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9568 } 9569 9570 /** 9571 * Subscribes the user or group to the resource.<p> 9572 * 9573 * @param dbc the database context 9574 * @param poolName the name of the database pool to use 9575 * @param principal the principal that subscribes to the resource 9576 * @param resource the resource to subscribe to 9577 * 9578 * @throws CmsException if something goes wrong 9579 */ 9580 public void subscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource) 9581 throws CmsException { 9582 9583 getSubscriptionDriver().subscribeResourceFor(dbc, poolName, principal, resource); 9584 } 9585 9586 /** 9587 * Undelete the resource.<p> 9588 * 9589 * @param dbc the current database context 9590 * @param resource the name of the resource to apply this operation to 9591 * 9592 * @throws CmsException if something goes wrong 9593 * 9594 * @see CmsObject#undeleteResource(String, boolean) 9595 * @see I_CmsResourceType#undelete(CmsObject, CmsSecurityManager, CmsResource, boolean) 9596 */ 9597 public void undelete(CmsDbContext dbc, CmsResource resource) throws CmsException { 9598 9599 if (!resource.getState().isDeleted()) { 9600 throw new CmsVfsException( 9601 Messages.get().container( 9602 Messages.ERR_UNDELETE_FOR_RESOURCE_DELETED_1, 9603 dbc.removeSiteRoot(resource.getRootPath()))); 9604 } 9605 9606 // set the state to changed 9607 resource.setState(CmsResourceState.STATE_CHANGED); 9608 // perform the changes 9609 updateState(dbc, resource, false); 9610 // log it 9611 log( 9612 dbc, 9613 new CmsLogEntry( 9614 dbc, 9615 resource.getStructureId(), 9616 CmsLogEntryType.RESOURCE_UNDELETED, 9617 new String[] {resource.getRootPath()}), 9618 false); 9619 // clear the cache 9620 m_monitor.clearResourceCache(); 9621 9622 // fire change event 9623 Map<String, Object> data = new HashMap<String, Object>(2); 9624 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9625 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE)); 9626 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9627 } 9628 9629 /** 9630 * Undos all changes in the resource by restoring the version from the 9631 * online project to the current offline project.<p> 9632 * 9633 * @param dbc the current database context 9634 * @param resource the name of the resource to apply this operation to 9635 * @param mode the undo mode, one of the <code>{@link org.opencms.file.CmsResource.CmsResourceUndoMode}#UNDO_XXX</code> constants 9636 * please note that the recursive flag is ignored at this level 9637 * 9638 * @throws CmsException if something goes wrong 9639 * 9640 * @see CmsObject#undoChanges(String, CmsResource.CmsResourceUndoMode) 9641 * @see I_CmsResourceType#undoChanges(CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode) 9642 */ 9643 public void undoChanges(CmsDbContext dbc, CmsResource resource, CmsResource.CmsResourceUndoMode mode) 9644 throws CmsException { 9645 9646 if (resource.getState().isNew()) { 9647 // undo changes is impossible on a new resource 9648 throw new CmsVfsException(Messages.get().container(Messages.ERR_UNDO_CHANGES_FOR_RESOURCE_NEW_0)); 9649 } 9650 9651 // we need this for later use 9652 CmsProject onlineProject = readProject(dbc, CmsProject.ONLINE_PROJECT_ID); 9653 // read the resource from the online project 9654 CmsResource onlineResource = getVfsDriver( 9655 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getStructureId(), true); 9656 9657 CmsResource onlineResourceByPath = null; 9658 try { 9659 // this is needed to figure out if a moved resource overwrote a deleted one 9660 onlineResourceByPath = getVfsDriver( 9661 dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, resource.getRootPath(), true); 9662 9663 // force undo move operation if needed 9664 if (!mode.isUndoMove() && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) { 9665 mode = mode.includeMove(); 9666 } 9667 } catch (Exception e) { 9668 // ok 9669 } 9670 9671 boolean moved = !onlineResource.getRootPath().equals(resource.getRootPath()); 9672 // undo move operation if required 9673 if (moved && mode.isUndoMove()) { 9674 moveResource(dbc, resource, onlineResource.getRootPath(), true); 9675 if ((onlineResourceByPath != null) 9676 && !onlineResourceByPath.getRootPath().equals(onlineResource.getRootPath())) { 9677 // was moved over deleted, so the deleted file has to be undone 9678 undoContentChanges(dbc, onlineProject, null, onlineResourceByPath, CmsResource.STATE_UNCHANGED, true); 9679 } 9680 } 9681 // undo content changes 9682 CmsResourceState newState = CmsResource.STATE_UNCHANGED; 9683 if (moved && !mode.isUndoMove()) { 9684 newState = CmsResource.STATE_CHANGED; 9685 } 9686 undoContentChanges(dbc, onlineProject, resource, onlineResource, newState, moved && mode.isUndoMove()); 9687 // because undoContentChanges deletes the offline resource internally, we have 9688 // to write an entry to the log table to prevent the resource from appearing in the 9689 // user's publish list. 9690 log( 9691 dbc, 9692 new CmsLogEntry( 9693 dbc, 9694 resource.getStructureId(), 9695 CmsLogEntryType.RESOURCE_CHANGES_UNDONE, 9696 new String[] {resource.getRootPath()}), 9697 true); 9698 9699 } 9700 9701 /** 9702 * Unlocks all resources in the given project.<p> 9703 * 9704 * @param project the project to unlock the resources in 9705 */ 9706 public void unlockProject(CmsProject project) { 9707 9708 // unlock all resources in the project 9709 m_lockManager.removeResourcesInProject(project.getUuid(), false); 9710 m_monitor.clearResourceCache(); 9711 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROJECT, CmsMemoryMonitor.CacheType.PERMISSION); 9712 } 9713 9714 /** 9715 * Unlocks a resource.<p> 9716 * 9717 * @param dbc the current database context 9718 * @param resource the resource to unlock 9719 * @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 9720 * @param removeSystemLock <code>true</code>, if you also want to remove system locks 9721 * 9722 * @throws CmsException if something goes wrong 9723 * 9724 * @see CmsObject#unlockResource(String) 9725 * @see I_CmsResourceType#unlockResource(CmsObject, CmsSecurityManager, CmsResource) 9726 */ 9727 public void unlockResource(CmsDbContext dbc, CmsResource resource, boolean force, boolean removeSystemLock) 9728 throws CmsException { 9729 9730 // update the resource cache 9731 m_monitor.clearResourceCache(); 9732 9733 // now update lock status 9734 m_lockManager.removeResource(dbc, resource, force, removeSystemLock); 9735 9736 // we must also clear the permission cache 9737 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PERMISSION); 9738 9739 // fire resource modification event 9740 Map<String, Object> data = new HashMap<String, Object>(2); 9741 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 9742 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(NOTHING_CHANGED)); 9743 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 9744 } 9745 9746 /** 9747 * Unsubscribes all deleted resources that were deleted before the specified time stamp.<p> 9748 * 9749 * @param dbc the database context 9750 * @param poolName the name of the database pool to use 9751 * @param deletedTo the time stamp to which the resources have been deleted 9752 * 9753 * @throws CmsException if something goes wrong 9754 */ 9755 public void unsubscribeAllDeletedResources(CmsDbContext dbc, String poolName, long deletedTo) throws CmsException { 9756 9757 getSubscriptionDriver().unsubscribeAllDeletedResources(dbc, poolName, deletedTo); 9758 } 9759 9760 /** 9761 * Unsubscribes the principal from all resources.<p> 9762 * 9763 * @param dbc the database context 9764 * @param poolName the name of the database pool to use 9765 * @param principal the principal that unsubscribes from all resources 9766 * 9767 * @throws CmsException if something goes wrong 9768 */ 9769 public void unsubscribeAllResourcesFor(CmsDbContext dbc, String poolName, CmsPrincipal principal) 9770 throws CmsException { 9771 9772 getSubscriptionDriver().unsubscribeAllResourcesFor(dbc, poolName, principal); 9773 9774 } 9775 9776 /** 9777 * Unsubscribes the principal from the resource.<p> 9778 * 9779 * @param dbc the database context 9780 * @param poolName the name of the database pool to use 9781 * @param principal the principal that unsubscribes from the resource 9782 * @param resource the resource to unsubscribe from 9783 * 9784 * @throws CmsException if something goes wrong 9785 */ 9786 public void unsubscribeResourceFor(CmsDbContext dbc, String poolName, CmsPrincipal principal, CmsResource resource) 9787 throws CmsException { 9788 9789 getSubscriptionDriver().unsubscribeResourceFor(dbc, poolName, principal, resource); 9790 } 9791 9792 /** 9793 * Unsubscribes all groups and users from the resource.<p> 9794 * 9795 * @param dbc the database context 9796 * @param poolName the name of the database pool to use 9797 * @param resource the resource to unsubscribe all groups and users from 9798 * 9799 * @throws CmsException if something goes wrong 9800 */ 9801 public void unsubscribeResourceForAll(CmsDbContext dbc, String poolName, CmsResource resource) throws CmsException { 9802 9803 getSubscriptionDriver().unsubscribeResourceForAll(dbc, poolName, resource); 9804 } 9805 9806 /** 9807 * Update the export points.<p> 9808 * 9809 * All files and folders "inside" an export point are written.<p> 9810 * 9811 * @param dbc the current database context 9812 */ 9813 public void updateExportPoints(CmsDbContext dbc) { 9814 9815 try { 9816 // read the export points and return immediately if there are no export points at all 9817 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 9818 exportPoints.addAll(OpenCms.getExportPoints()); 9819 exportPoints.addAll(OpenCms.getModuleManager().getExportPoints()); 9820 if (exportPoints.size() == 0) { 9821 if (LOG.isWarnEnabled()) { 9822 LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0)); 9823 } 9824 return; 9825 } 9826 9827 // create the driver to write the export points 9828 I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver( 9829 exportPoints); 9830 9831 // the export point hash table contains RFS export paths keyed by their internal VFS paths 9832 Iterator<String> i = exportPointDriver.getExportPointPaths().iterator(); 9833 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 9834 while (i.hasNext()) { 9835 String currentExportPoint = i.next(); 9836 9837 // print some report messages 9838 if (LOG.isInfoEnabled()) { 9839 LOG.info(Messages.get().getBundle().key(Messages.LOG_WRITE_EXPORT_POINT_1, currentExportPoint)); 9840 } 9841 9842 try { 9843 CmsResourceFilter filter = CmsResourceFilter.DEFAULT; 9844 List<CmsResource> resources = vfsDriver.readResourceTree( 9845 dbc, 9846 CmsProject.ONLINE_PROJECT_ID, 9847 currentExportPoint, 9848 filter.getType(), 9849 filter.getState(), 9850 filter.getModifiedAfter(), 9851 filter.getModifiedBefore(), 9852 filter.getReleaseAfter(), 9853 filter.getReleaseBefore(), 9854 filter.getExpireAfter(), 9855 filter.getExpireBefore(), 9856 CmsDriverManager.READMODE_INCLUDE_TREE 9857 | (filter.excludeType() ? CmsDriverManager.READMODE_EXCLUDE_TYPE : 0) 9858 | (filter.excludeState() ? CmsDriverManager.READMODE_EXCLUDE_STATE : 0)); 9859 9860 Iterator<CmsResource> j = resources.iterator(); 9861 while (j.hasNext()) { 9862 CmsResource currentResource = j.next(); 9863 9864 if (currentResource.isFolder()) { 9865 // export the folder 9866 exportPointDriver.createFolder(currentResource.getRootPath(), currentExportPoint); 9867 } else { 9868 // try to create the exportpoint folder 9869 exportPointDriver.createFolder(currentExportPoint, currentExportPoint); 9870 byte[] onlineContent = vfsDriver.readContent( 9871 dbc, 9872 CmsProject.ONLINE_PROJECT_ID, 9873 currentResource.getResourceId()); 9874 // export the file content online 9875 exportPointDriver.writeFile( 9876 currentResource.getRootPath(), 9877 currentExportPoint, 9878 onlineContent); 9879 } 9880 } 9881 } catch (CmsException e) { 9882 // there might exist export points without corresponding resources in the VFS 9883 // -> ignore exceptions which are not "resource not found" exception quiet here 9884 if (e instanceof CmsVfsResourceNotFoundException) { 9885 if (LOG.isErrorEnabled()) { 9886 LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e); 9887 } 9888 } 9889 } 9890 } 9891 } catch (Exception e) { 9892 if (LOG.isErrorEnabled()) { 9893 LOG.error(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXORT_POINTS_ERROR_0), e); 9894 } 9895 } 9896 } 9897 9898 /** 9899 * Updates the last login date on the given user to the current time.<p> 9900 * 9901 * @param dbc the current database context 9902 * @param user the user to be updated 9903 * 9904 * @throws CmsException if operation was not successful 9905 */ 9906 public void updateLastLoginDate(CmsDbContext dbc, CmsUser user) throws CmsException { 9907 9908 m_monitor.clearUserCache(user); 9909 // set the last login time to the current time 9910 user.setLastlogin(System.currentTimeMillis()); 9911 dbc.setAttribute(ATTRIBUTE_LOGIN, user.getName()); 9912 getUserDriver(dbc).writeUser(dbc, user); 9913 // update cache 9914 m_monitor.cacheUser(user); 9915 9916 // invalidate all user dependent caches 9917 m_monitor.flushCache( 9918 CmsMemoryMonitor.CacheType.ACL, 9919 CmsMemoryMonitor.CacheType.GROUP, 9920 CmsMemoryMonitor.CacheType.ORG_UNIT, 9921 CmsMemoryMonitor.CacheType.USER_LIST, 9922 CmsMemoryMonitor.CacheType.PERMISSION, 9923 CmsMemoryMonitor.CacheType.RESOURCE_LIST); 9924 9925 // fire user modified event 9926 Map<String, Object> eventData = new HashMap<String, Object>(); 9927 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 9928 eventData.put(I_CmsEventListener.KEY_USER_NAME, user.getName()); 9929 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 9930 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(CmsUser.FLAG_LAST_LOGIN)); 9931 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 9932 } 9933 9934 /** 9935 * Logs everything that has not been written to DB jet.<p> 9936 * 9937 * @param dbc the current db context 9938 * 9939 * @throws CmsDataAccessException if something goes wrong 9940 */ 9941 public void updateLog(CmsDbContext dbc) throws CmsDataAccessException { 9942 9943 synchronized (m_publishListUpdateLock) { 9944 9945 if (m_log.isEmpty()) { 9946 return; 9947 } 9948 9949 List<CmsLogEntry> log = new ArrayList<CmsLogEntry>(m_log); 9950 m_log.clear(); 9951 String logTableEnabledStr = (String)OpenCms.getRuntimeProperty(PARAM_LOG_TABLE_ENABLED); 9952 if (Boolean.parseBoolean(logTableEnabledStr)) { // defaults to 'false' if value not set 9953 m_projectDriver.log(dbc, log); 9954 } 9955 A_CmsLogPublishListConverter converter = null; 9956 switch (OpenCms.getPublishManager().getPublishListRemoveMode()) { 9957 case currentUser: 9958 converter = new CmsLogPublishListConverterCurrentUser(); 9959 break; 9960 case allUsers: 9961 default: 9962 converter = new CmsLogPublishListConverterAllUsers(); 9963 break; 9964 } 9965 for (CmsLogEntry entry : log) { 9966 converter.add(entry); 9967 } 9968 converter.writeChangesToDatabase(dbc, m_projectDriver); 9969 } 9970 } 9971 9972 /** 9973 * Updates/Creates the given relations for the given resource.<p> 9974 * 9975 * @param dbc the db context 9976 * @param resource the resource to update the relations for 9977 * @param links the links to consider for updating 9978 * @param updateSiblingState if true, sets the state of siblings whose relations have changed to 'changed' (unless they are new or deleted) 9979 * 9980 * @throws CmsException if something goes wrong 9981 * 9982 * @see CmsSecurityManager#updateRelationsForResource(CmsRequestContext, CmsResource, List) 9983 */ 9984 public void updateRelationsForResource( 9985 CmsDbContext dbc, 9986 CmsResource resource, 9987 List<CmsLink> links, 9988 boolean updateSiblingState) 9989 throws CmsException { 9990 9991 if (links == null) { 9992 links = new ArrayList<>(); 9993 } 9994 9995 // create new relation information 9996 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 9997 Iterator<CmsLink> itLinks = links.iterator(); 9998 Set<CmsRelation> relationsForOriginalResource = new HashSet<>(); 9999 while (itLinks.hasNext()) { 10000 CmsLink link = itLinks.next(); 10001 if (link.isInternal()) { // only update internal links 10002 if (CmsStringUtil.isEmptyOrWhitespaceOnly(link.getTarget())) { 10003 // only an anchor 10004 continue; 10005 } 10006 CmsUUID targetId = link.getStructureId(); 10007 String destPath = link.getTarget(); 10008 10009 if (targetId != null) { 10010 // the link target may not be a VFS path even if the link id is a structure id, 10011 // so if possible, we read the resource for the id and set the relation target to its 10012 // real root path. 10013 try { 10014 CmsResource destRes = readResource(dbc, targetId, CmsResourceFilter.ALL); 10015 destPath = destRes.getRootPath(); 10016 } catch (CmsVfsResourceNotFoundException e) { 10017 // ignore 10018 } 10019 } 10020 10021 CmsRelation originalRelation = new CmsRelation( 10022 resource.getStructureId(), 10023 resource.getRootPath(), 10024 link.getStructureId(), 10025 destPath, 10026 link.getType()); 10027 relationsForOriginalResource.add(originalRelation); 10028 } 10029 } 10030 List<CmsResource> siblings = resource.getSiblingCount() == 1 10031 ? Arrays.asList(resource) 10032 : readSiblings(dbc, resource, CmsResourceFilter.ALL); 10033 10034 for (CmsResource sibling : siblings) { 10035 // For each sibling, we determine which 'defined in content' relations it SHOULD have, 10036 // and only update the relations if that set differs from the ones it actually has. 10037 // If the updateSiblingState flag is set, then for siblings, we update the structure 10038 // state to changed (unless the state was 'deleted' or 'new'). 10039 // This is so that even if the user later publishes only one sibling, the other sibling will still 10040 // show up as changed in the GUI, so the user can publish it separately (with its updated relations). 10041 Set<CmsRelation> relationsForSibling = relationsForOriginalResource.stream().map( 10042 relation -> new CmsRelation( 10043 sibling.getStructureId(), 10044 sibling.getRootPath(), 10045 relation.getTargetId(), 10046 relation.getTargetPath(), 10047 relation.getType())).collect(Collectors.toSet()); 10048 Set<CmsRelation> existingRelations = new HashSet<>( 10049 vfsDriver.readRelations( 10050 dbc, 10051 dbc.currentProject().getUuid(), 10052 sibling, 10053 CmsRelationFilter.TARGETS.filterDefinedInContent())); 10054 if (!existingRelations.equals(relationsForSibling)) { 10055 vfsDriver.deleteRelations( 10056 dbc, 10057 dbc.currentProject().getUuid(), 10058 sibling, 10059 CmsRelationFilter.TARGETS.filterDefinedInContent()); 10060 for (CmsRelation relation : relationsForSibling) { 10061 vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation); 10062 } 10063 if (!sibling.getState().isDeleted() 10064 && !sibling.getState().isNew() 10065 && (siblings.size() > 1) 10066 && updateSiblingState) { 10067 sibling.setState(CmsResource.STATE_CHANGED); 10068 vfsDriver.writeResourceState( 10069 dbc, 10070 dbc.currentProject(), 10071 sibling, 10072 CmsDriverManager.UPDATE_STRUCTURE_STATE, 10073 false); 10074 } 10075 } 10076 } 10077 } 10078 10079 /** 10080 * Returns <code>true</code> if a user is member of the given group.<p> 10081 * 10082 * @param dbc the current database context 10083 * @param username the name of the user to check 10084 * @param groupname the name of the group to check 10085 * @param readRoles if to read roles or groups 10086 * 10087 * @return <code>true</code>, if the user is in the group, <code>false</code> otherwise 10088 * 10089 * @throws CmsException if something goes wrong 10090 */ 10091 public boolean userInGroup(CmsDbContext dbc, String username, String groupname, boolean readRoles) 10092 throws CmsException { 10093 10094 List<CmsGroup> groups = getGroupsOfUser(dbc, username, readRoles); 10095 for (int i = 0; i < groups.size(); i++) { 10096 CmsGroup group = groups.get(i); 10097 if (groupname.equals(group.getName()) || groupname.substring(1).equals(group.getName())) { 10098 return true; 10099 } 10100 } 10101 return false; 10102 } 10103 10104 /** 10105 * This method checks if a new password follows the rules for 10106 * new passwords, which are defined by a Class implementing the 10107 * <code>{@link org.opencms.security.I_CmsPasswordHandler}</code> 10108 * interface and configured in the opencms.properties file.<p> 10109 * 10110 * If this method throws no exception the password is valid.<p> 10111 * 10112 * @param password the new password that has to be checked 10113 * 10114 * @throws CmsSecurityException if the password is not valid 10115 */ 10116 public void validatePassword(String password) throws CmsSecurityException { 10117 10118 OpenCms.getPasswordHandler().validatePassword(password); 10119 } 10120 10121 /** 10122 * Validates the relations for the given resources.<p> 10123 * 10124 * @param dbc the database context 10125 * @param publishList the resources to validate during publishing 10126 * @param report a report to write the messages to 10127 * 10128 * @return a map with lists of invalid links 10129 * (<code>{@link org.opencms.relations.CmsRelation}}</code> objects) 10130 * keyed by root paths 10131 * 10132 * @throws Exception if something goes wrong 10133 */ 10134 public Map<String, List<CmsRelation>> validateRelations( 10135 CmsDbContext dbc, 10136 CmsPublishList publishList, 10137 I_CmsReport report) 10138 throws Exception { 10139 10140 return m_htmlLinkValidator.validateResources(dbc, publishList, report); 10141 } 10142 10143 /** 10144 * Writes an access control entries to a given resource.<p> 10145 * 10146 * @param dbc the current database context 10147 * @param resource the resource 10148 * @param ace the entry to write 10149 * 10150 * @throws CmsException if something goes wrong 10151 */ 10152 public void writeAccessControlEntry(CmsDbContext dbc, CmsResource resource, CmsAccessControlEntry ace) 10153 throws CmsException { 10154 10155 // write the new ace 10156 getUserDriver(dbc).writeAccessControlEntry(dbc, dbc.currentProject(), ace); 10157 10158 // log it 10159 log( 10160 dbc, 10161 new CmsLogEntry( 10162 dbc, 10163 resource.getStructureId(), 10164 CmsLogEntryType.RESOURCE_PERMISSIONS, 10165 new String[] {resource.getRootPath()}), 10166 false); 10167 10168 // update the "last modified" information 10169 setDateLastModified(dbc, resource, resource.getDateLastModified()); 10170 10171 // clear the cache 10172 m_monitor.clearAccessControlListCache(); 10173 10174 // fire a resource modification event 10175 Map<String, Object> data = new HashMap<String, Object>(2); 10176 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10177 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_ACCESSCONTROL)); 10178 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10179 } 10180 10181 /** 10182 * Writes all export points into the file system for the publish task 10183 * specified by trhe given publish history ID.<p> 10184 * 10185 * @param dbc the current database context 10186 * @param report an I_CmsReport instance to print output message, or null to write messages to the log file 10187 * @param publishHistoryId ID to identify the publish task in the publish history 10188 */ 10189 public void writeExportPoints(CmsDbContext dbc, I_CmsReport report, CmsUUID publishHistoryId) { 10190 10191 boolean printReportHeaders = false; 10192 List<CmsPublishedResource> publishedResources = null; 10193 try { 10194 // read the "published resources" for the specified publish history ID 10195 publishedResources = getProjectDriver(dbc).readPublishedResources(dbc, publishHistoryId); 10196 } catch (CmsException e) { 10197 if (LOG.isErrorEnabled()) { 10198 LOG.error( 10199 Messages.get().getBundle().key(Messages.ERR_READ_PUBLISHED_RESOURCES_FOR_ID_1, publishHistoryId), 10200 e); 10201 } 10202 } 10203 if ((publishedResources == null) || publishedResources.isEmpty()) { 10204 if (LOG.isWarnEnabled()) { 10205 LOG.warn(Messages.get().getBundle().key(Messages.LOG_EMPTY_PUBLISH_HISTORY_1, publishHistoryId)); 10206 } 10207 return; 10208 } 10209 10210 // read the export points and return immediately if there are no export points at all 10211 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 10212 exportPoints.addAll(OpenCms.getExportPoints()); 10213 exportPoints.addAll(OpenCms.getModuleManager().getExportPoints()); 10214 if (exportPoints.size() == 0) { 10215 if (LOG.isWarnEnabled()) { 10216 LOG.warn(Messages.get().getBundle().key(Messages.LOG_NO_EXPORT_POINTS_CONFIGURED_0)); 10217 } 10218 return; 10219 } 10220 10221 // create the driver to write the export points 10222 I_CmsExportPointDriver exportPointDriver = OpenCms.getImportExportManager().createExportPointDriver( 10223 exportPoints); 10224 10225 // the report may be null if the export point write was started by an event 10226 if (report == null) { 10227 if (dbc.getRequestContext() != null) { 10228 report = new CmsLogReport(dbc.getRequestContext().getLocale(), getClass()); 10229 } else { 10230 report = new CmsLogReport(CmsLocaleManager.getDefaultLocale(), getClass()); 10231 } 10232 } 10233 10234 // iterate over all published resources to export them 10235 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10236 Iterator<CmsPublishedResource> i = publishedResources.iterator(); 10237 while (i.hasNext()) { 10238 CmsPublishedResource currentPublishedResource = i.next(); 10239 String currentExportPoint = exportPointDriver.getExportPoint(currentPublishedResource.getRootPath()); 10240 10241 if (currentExportPoint != null) { 10242 if (!printReportHeaders) { 10243 report.println( 10244 Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_BEGIN_0), 10245 I_CmsReport.FORMAT_HEADLINE); 10246 printReportHeaders = true; 10247 } 10248 10249 // print report message 10250 if (currentPublishedResource.getState().isDeleted()) { 10251 report.print( 10252 Messages.get().container(Messages.RPT_EXPORT_POINTS_DELETE_0), 10253 I_CmsReport.FORMAT_NOTE); 10254 } else { 10255 report.print(Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_0), I_CmsReport.FORMAT_NOTE); 10256 } 10257 report.print( 10258 org.opencms.report.Messages.get().container( 10259 org.opencms.report.Messages.RPT_ARGUMENT_1, 10260 currentPublishedResource.getRootPath())); 10261 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 10262 10263 if (currentPublishedResource.isFolder()) { 10264 // export the folder 10265 if (currentPublishedResource.getState().isDeleted()) { 10266 exportPointDriver.deleteResource(currentPublishedResource.getRootPath(), currentExportPoint); 10267 } else { 10268 exportPointDriver.createFolder(currentPublishedResource.getRootPath(), currentExportPoint); 10269 } 10270 report.println( 10271 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 10272 I_CmsReport.FORMAT_OK); 10273 } else { 10274 // export the file 10275 try { 10276 if (currentPublishedResource.getState().isDeleted()) { 10277 exportPointDriver.deleteResource( 10278 currentPublishedResource.getRootPath(), 10279 currentExportPoint); 10280 } else { 10281 // read the file content online 10282 byte[] onlineContent = vfsDriver.readContent( 10283 dbc, 10284 CmsProject.ONLINE_PROJECT_ID, 10285 currentPublishedResource.getResourceId()); 10286 exportPointDriver.writeFile( 10287 currentPublishedResource.getRootPath(), 10288 currentExportPoint, 10289 onlineContent); 10290 } 10291 report.println( 10292 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 10293 I_CmsReport.FORMAT_OK); 10294 } catch (CmsException e) { 10295 if (LOG.isErrorEnabled()) { 10296 LOG.error( 10297 Messages.get().getBundle().key( 10298 Messages.LOG_WRITE_EXPORT_POINT_ERROR_1, 10299 currentPublishedResource.getRootPath()), 10300 e); 10301 } 10302 report.println( 10303 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 10304 I_CmsReport.FORMAT_ERROR); 10305 } 10306 } 10307 } 10308 } 10309 if (printReportHeaders) { 10310 report.println( 10311 Messages.get().container(Messages.RPT_EXPORT_POINTS_WRITE_END_0), 10312 I_CmsReport.FORMAT_HEADLINE); 10313 } 10314 } 10315 10316 /** 10317 * Writes a resource to the OpenCms VFS, including it's content.<p> 10318 * 10319 * Applies only to resources of type <code>{@link CmsFile}</code> 10320 * i.e. resources that have a binary content attached.<p> 10321 * 10322 * Certain resource types might apply content validation or transformation rules 10323 * before the resource is actually written to the VFS. The returned result 10324 * might therefore be a modified version from the provided original.<p> 10325 * 10326 * @param dbc the current database context 10327 * @param resource the resource to apply this operation to 10328 * 10329 * @return the written resource (may have been modified) 10330 * 10331 * @throws CmsException if something goes wrong 10332 * 10333 * @see CmsObject#writeFile(CmsFile) 10334 * @see I_CmsResourceType#writeFile(CmsObject, CmsSecurityManager, CmsFile) 10335 */ 10336 public CmsFile writeFile(CmsDbContext dbc, CmsFile resource) throws CmsException { 10337 10338 resource.setUserLastModified(dbc.currentUser().getId()); 10339 resource.setContents(resource.getContents()); // to be sure the content date is updated 10340 10341 getVfsDriver(dbc).writeResource(dbc, dbc.currentProject().getUuid(), resource, UPDATE_RESOURCE_STATE); 10342 10343 byte[] contents = resource.getContents(); 10344 getVfsDriver(dbc).writeContent(dbc, resource.getResourceId(), contents); 10345 // log it 10346 log( 10347 dbc, 10348 new CmsLogEntry( 10349 dbc, 10350 resource.getStructureId(), 10351 CmsLogEntryType.RESOURCE_CONTENT_MODIFIED, 10352 new String[] {resource.getRootPath()}), 10353 false); 10354 10355 // read the file back from db 10356 resource = new CmsFile(readResource(dbc, resource.getStructureId(), CmsResourceFilter.ALL)); 10357 resource.setContents(contents); 10358 10359 deleteRelationsWithSiblings(dbc, resource); 10360 10361 // update the cache 10362 m_monitor.clearResourceCache(); 10363 10364 Map<String, Object> data = new HashMap<String, Object>(2); 10365 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10366 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_CONTENT)); 10367 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10368 10369 return resource; 10370 } 10371 10372 /** 10373 * Writes an already existing group.<p> 10374 * 10375 * The group id has to be a valid OpenCms group id.<br> 10376 * 10377 * The group with the given id will be completely overridden 10378 * by the given data.<p> 10379 * 10380 * @param dbc the current database context 10381 * @param group the group that should be written 10382 * 10383 * @throws CmsException if operation was not successful 10384 */ 10385 public void writeGroup(CmsDbContext dbc, CmsGroup group) throws CmsException { 10386 10387 CmsGroup oldGroup = readGroup(dbc, group.getName()); 10388 m_monitor.uncacheGroup(oldGroup); 10389 getUserDriver(dbc).writeGroup(dbc, group); 10390 m_monitor.cacheGroup(group); 10391 10392 if (!dbc.getProjectId().isNullUUID()) { 10393 // group modified event is not needed 10394 return; 10395 } 10396 // fire group modified event 10397 Map<String, Object> eventData = new HashMap<String, Object>(); 10398 eventData.put(I_CmsEventListener.KEY_GROUP_ID, group.getId().toString()); 10399 eventData.put(I_CmsEventListener.KEY_GROUP_NAME, oldGroup.getName()); 10400 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_GROUP_MODIFIED_ACTION_WRITE); 10401 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_GROUP_MODIFIED, eventData)); 10402 } 10403 10404 /** 10405 * Creates an historical entry of the current project.<p> 10406 * 10407 * @param dbc the current database context 10408 * @param publishTag the version 10409 * @param publishDate the date of publishing 10410 * 10411 * @throws CmsDataAccessException if operation was not successful 10412 */ 10413 public void writeHistoryProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException { 10414 10415 getHistoryDriver(dbc).writeProject(dbc, publishTag, publishDate); 10416 } 10417 10418 /** 10419 * Writes the locks that are currently stored in-memory to the database to allow restoring them 10420 * in future server startups.<p> 10421 * 10422 * This overwrites the locks previously stored in the underlying database table.<p> 10423 * 10424 * @param dbc the current database context 10425 * 10426 * @throws CmsException if something goes wrong 10427 */ 10428 public void writeLocks(CmsDbContext dbc) throws CmsException { 10429 10430 m_lockManager.writeLocks(dbc); 10431 } 10432 10433 /** 10434 * Writes an already existing organizational unit.<p> 10435 * 10436 * The organizational unit id has to be a valid OpenCms organizational unit id.<br> 10437 * 10438 * The organizational unit with the given id will be completely overridden 10439 * by the given data.<p> 10440 * 10441 * @param dbc the current db context 10442 * @param organizationalUnit the organizational unit that should be written 10443 * 10444 * @throws CmsException if operation was not successful 10445 * 10446 * @see org.opencms.security.CmsOrgUnitManager#writeOrganizationalUnit(CmsObject, CmsOrganizationalUnit) 10447 */ 10448 public void writeOrganizationalUnit(CmsDbContext dbc, CmsOrganizationalUnit organizationalUnit) 10449 throws CmsException { 10450 10451 m_monitor.uncacheOrgUnit(organizationalUnit); 10452 getUserDriver(dbc).writeOrganizationalUnit(dbc, organizationalUnit); 10453 10454 // create a publish list for the 'virtual' publish event 10455 CmsResource ouRes = readResource(dbc, organizationalUnit.getId(), CmsResourceFilter.DEFAULT); 10456 CmsPublishList pl = new CmsPublishList(ouRes, false); 10457 pl.add(ouRes, false); 10458 10459 getProjectDriver(dbc).writePublishHistory( 10460 dbc, 10461 pl.getPublishHistoryId(), 10462 new CmsPublishedResource(ouRes, -1, CmsResourceState.STATE_NEW)); 10463 10464 // fire the 'virtual' publish event 10465 Map<String, Object> eventData = new HashMap<String, Object>(); 10466 eventData.put(I_CmsEventListener.KEY_PUBLISHID, pl.getPublishHistoryId().toString()); 10467 eventData.put(I_CmsEventListener.KEY_PROJECTID, dbc.currentProject().getUuid()); 10468 eventData.put(I_CmsEventListener.KEY_DBCONTEXT, dbc); 10469 CmsEvent afterPublishEvent = new CmsEvent(I_CmsEventListener.EVENT_PUBLISH_PROJECT, eventData); 10470 OpenCms.fireCmsEvent(afterPublishEvent); 10471 10472 m_monitor.cacheOrgUnit(organizationalUnit); 10473 } 10474 10475 /** 10476 * Writes an already existing project.<p> 10477 * 10478 * The project id has to be a valid OpenCms project id.<br> 10479 * 10480 * The project with the given id will be completely overridden 10481 * by the given data.<p> 10482 * 10483 * @param dbc the current database context 10484 * @param project the project that should be written 10485 * 10486 * @throws CmsException if operation was not successful 10487 */ 10488 public void writeProject(CmsDbContext dbc, CmsProject project) throws CmsException { 10489 10490 m_monitor.uncacheProject(project); 10491 getProjectDriver(dbc).writeProject(dbc, project); 10492 m_monitor.cacheProject(project); 10493 } 10494 10495 /** 10496 * Writes a new project into the PROJECT_LASTMODIFIED field of a resource record.<p> 10497 * 10498 * @param dbc the current database context 10499 * @param resource the resource which should be modified 10500 * @param projectId the project id to write 10501 * 10502 * @throws CmsDataAccessException if the database access fails 10503 */ 10504 public void writeProjectLastModified(CmsDbContext dbc, CmsResource resource, CmsUUID projectId) 10505 throws CmsDataAccessException { 10506 10507 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10508 vfsDriver.writeLastModifiedProjectId(dbc, dbc.currentProject(), projectId, resource); 10509 } 10510 10511 /** 10512 * Writes a property for a specified resource.<p> 10513 * 10514 * @param dbc the current database context 10515 * @param resource the resource to write the property for 10516 * @param property the property to write 10517 * 10518 * @throws CmsException if something goes wrong 10519 * 10520 * @see CmsObject#writePropertyObject(String, CmsProperty) 10521 * @see I_CmsResourceType#writePropertyObject(CmsObject, CmsSecurityManager, CmsResource, CmsProperty) 10522 */ 10523 public void writePropertyObject(CmsDbContext dbc, CmsResource resource, CmsProperty property) throws CmsException { 10524 10525 try { 10526 if (property == CmsProperty.getNullProperty()) { 10527 // skip empty or null properties 10528 return; 10529 } 10530 10531 // test if and what state should be updated 10532 // 0: none, 1: structure, 2: resource 10533 int updateState = getUpdateState(dbc, resource, Collections.singletonList(property)); 10534 10535 // write the property 10536 getVfsDriver(dbc).writePropertyObject(dbc, dbc.currentProject(), resource, property); 10537 10538 if (updateState > 0) { 10539 updateState(dbc, resource, updateState == 2); 10540 } 10541 // log it 10542 log( 10543 dbc, 10544 new CmsLogEntry( 10545 dbc, 10546 resource.getStructureId(), 10547 CmsLogEntryType.RESOURCE_PROPERTIES, 10548 new String[] {resource.getRootPath()}), 10549 false); 10550 10551 } finally { 10552 // update the driver manager cache 10553 m_monitor.clearResourceCache(); 10554 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 10555 10556 // fire an event that a property of a resource has been modified 10557 Map<String, Object> data = new HashMap<String, Object>(); 10558 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10559 data.put("property", property); 10560 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_PROPERTY_MODIFIED, data)); 10561 } 10562 } 10563 10564 /** 10565 * Writes a list of properties for a specified resource.<p> 10566 * 10567 * Code calling this method has to ensure that the no properties 10568 * <code>a, b</code> are contained in the specified list so that <code>a.equals(b)</code>, 10569 * otherwise an exception is thrown.<p> 10570 * 10571 * @param dbc the current database context 10572 * @param resource the resource to write the properties for 10573 * @param properties the list of properties to write 10574 * @param updateState if <code>true</code> the state of the resource will be updated 10575 * 10576 * @throws CmsException if something goes wrong 10577 * 10578 * @see CmsObject#writePropertyObjects(String, List) 10579 * @see I_CmsResourceType#writePropertyObjects(CmsObject, CmsSecurityManager, CmsResource, List) 10580 */ 10581 public void writePropertyObjects( 10582 CmsDbContext dbc, 10583 CmsResource resource, 10584 List<CmsProperty> properties, 10585 boolean updateState) 10586 throws CmsException { 10587 10588 if ((properties == null) || (properties.size() == 0)) { 10589 // skip empty or null lists 10590 return; 10591 } 10592 10593 try { 10594 // the specified list must not contain two or more equal property objects 10595 for (int i = 0, n = properties.size(); i < n; i++) { 10596 Set<String> keyValidationSet = new HashSet<String>(); 10597 CmsProperty property = properties.get(i); 10598 if (!keyValidationSet.contains(property.getName())) { 10599 keyValidationSet.add(property.getName()); 10600 } else { 10601 throw new CmsVfsException( 10602 Messages.get().container(Messages.ERR_VFS_INVALID_PROPERTY_LIST_1, property.getName())); 10603 } 10604 } 10605 10606 // test if and what state should be updated 10607 // 0: none, 1: structure, 2: resource 10608 int updateStateValue = 0; 10609 if (updateState) { 10610 updateStateValue = getUpdateState(dbc, resource, properties); 10611 } 10612 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10613 for (int i = 0; i < properties.size(); i++) { 10614 // write the property 10615 CmsProperty property = properties.get(i); 10616 vfsDriver.writePropertyObject(dbc, dbc.currentProject(), resource, property); 10617 } 10618 10619 if (updateStateValue > 0) { 10620 // update state 10621 updateState(dbc, resource, (updateStateValue == 2)); 10622 } 10623 10624 if (updateState) { 10625 // log it 10626 log( 10627 dbc, 10628 new CmsLogEntry( 10629 dbc, 10630 resource.getStructureId(), 10631 CmsLogEntryType.RESOURCE_PROPERTIES, 10632 new String[] {resource.getRootPath()}), 10633 false); 10634 } 10635 } finally { 10636 // update the driver manager cache 10637 m_monitor.clearResourceCache(); 10638 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 10639 10640 // fire an event that the properties of a resource have been modified 10641 OpenCms.fireCmsEvent( 10642 new CmsEvent( 10643 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 10644 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, resource))); 10645 } 10646 } 10647 10648 /** 10649 * Updates a publish job.<p> 10650 * 10651 * @param dbc the current database context 10652 * @param publishJob the publish job to update 10653 * 10654 * @throws CmsException if something goes wrong 10655 */ 10656 public void writePublishJob(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 10657 10658 getProjectDriver(dbc).writePublishJob(dbc, publishJob); 10659 } 10660 10661 /** 10662 * Writes the publish report for a publish job.<p> 10663 * 10664 * @param dbc the current database context 10665 * @param publishJob the publish job 10666 * @throws CmsException if something goes wrong 10667 */ 10668 public void writePublishReport(CmsDbContext dbc, CmsPublishJobInfoBean publishJob) throws CmsException { 10669 10670 CmsPublishReport report = (CmsPublishReport)publishJob.removePublishReport(); 10671 10672 if (report != null) { 10673 getProjectDriver(dbc).writePublishReport(dbc, publishJob.getPublishHistoryId(), report.getContents()); 10674 } 10675 } 10676 10677 /** 10678 * Writes a resource to the OpenCms VFS.<p> 10679 * 10680 * @param dbc the current database context 10681 * @param resource the resource to write 10682 * 10683 * @throws CmsException if something goes wrong 10684 */ 10685 public void writeResource(CmsDbContext dbc, CmsResource resource) throws CmsException { 10686 10687 // access was granted - write the resource 10688 resource.setUserLastModified(dbc.currentUser().getId()); 10689 CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) 10690 ? dbc.currentProject().getUuid() 10691 : dbc.getProjectId(); 10692 10693 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE); 10694 10695 // make sure the written resource has the state correctly set 10696 if (resource.getState().isUnchanged()) { 10697 resource.setState(CmsResource.STATE_CHANGED); 10698 } 10699 10700 // delete in content relations if the new type is not parseable 10701 if (!(OpenCms.getResourceManager().getResourceType(resource.getTypeId()) instanceof I_CmsLinkParseable)) { 10702 deleteRelationsWithSiblings(dbc, resource); 10703 } 10704 10705 // update the cache 10706 m_monitor.clearResourceCache(); 10707 Map<String, Object> data = new HashMap<String, Object>(2); 10708 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 10709 data.put(I_CmsEventListener.KEY_CHANGE, Integer.valueOf(CHANGED_RESOURCE)); 10710 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 10711 } 10712 10713 /** 10714 * Inserts an entry in the published resource table.<p> 10715 * 10716 * This is done during static export.<p> 10717 * 10718 * @param dbc the current database context 10719 * @param resourceName The name of the resource to be added to the static export 10720 * @param linkType the type of resource exported (0= non-parameter, 1=parameter) 10721 * @param linkParameter the parameters added to the resource 10722 * @param timestamp a time stamp for writing the data into the db 10723 * 10724 * @throws CmsException if something goes wrong 10725 */ 10726 public void writeStaticExportPublishedResource( 10727 CmsDbContext dbc, 10728 String resourceName, 10729 int linkType, 10730 String linkParameter, 10731 long timestamp) 10732 throws CmsException { 10733 10734 getProjectDriver(dbc).writeStaticExportPublishedResource(dbc, resourceName, linkType, linkParameter, timestamp); 10735 } 10736 10737 /** 10738 * Adds a new url name mapping for a structure id.<p> 10739 * 10740 * Instead of taking the name directly, this method takes an iterator of strings 10741 * which generates candidate URL names on-the-fly. The first generated name which is 10742 * not already mapped to another structure id will be chosen for the new URL name mapping. 10743 * 10744 * @param dbc the current database context 10745 * @param nameSeq the sequence of URL name candidates 10746 * @param structureId the structure id to which the url name should be mapped 10747 * @param locale the locale for which the mapping should be written 10748 * @param replaceOnPublish name mappings for which this is set will replace all other mappings for the same resource on publishing 10749 * 10750 * @return the actual name which was mapped to the structure id 10751 * 10752 * @throws CmsDataAccessException if something goes wrong 10753 */ 10754 public String writeUrlNameMapping( 10755 CmsDbContext dbc, 10756 Iterator<String> nameSeq, 10757 CmsUUID structureId, 10758 String locale, 10759 boolean replaceOnPublish) 10760 throws CmsDataAccessException { 10761 10762 String bestName = findBestNameForUrlNameMapping(dbc, nameSeq, structureId, locale); 10763 addOrReplaceUrlNameMapping(dbc, bestName, structureId, locale, replaceOnPublish); 10764 return bestName; 10765 } 10766 10767 /** 10768 * Updates the user information. <p> 10769 * 10770 * The user id has to be a valid OpenCms user id.<br> 10771 * 10772 * The user with the given id will be completely overridden 10773 * by the given data.<p> 10774 * 10775 * @param dbc the current database context 10776 * @param user the user to be updated 10777 * 10778 * @throws CmsException if operation was not successful 10779 */ 10780 public void writeUser(CmsDbContext dbc, CmsUser user) throws CmsException { 10781 10782 CmsUser oldUser = readUser(dbc, user.getId()); 10783 m_monitor.clearUserCache(oldUser); 10784 getUserDriver(dbc).writeUser(dbc, user); 10785 m_monitor.flushCache(CmsMemoryMonitor.CacheType.USER_LIST); 10786 10787 if (!dbc.getProjectId().isNullUUID()) { 10788 // user modified event is not needed 10789 return; 10790 } 10791 // fire user modified event 10792 Map<String, Object> eventData = new HashMap<String, Object>(); 10793 eventData.put(I_CmsEventListener.KEY_USER_ID, user.getId().toString()); 10794 eventData.put(I_CmsEventListener.KEY_USER_NAME, oldUser.getName()); 10795 eventData.put(I_CmsEventListener.KEY_USER_ACTION, I_CmsEventListener.VALUE_USER_MODIFIED_ACTION_WRITE_USER); 10796 eventData.put(I_CmsEventListener.KEY_USER_CHANGES, Integer.valueOf(user.getChanges(oldUser))); 10797 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_USER_MODIFIED, eventData)); 10798 OpenCms.getTwoFactorAuthenticationHandler().trackUserChange(dbc.getRequestContext(), oldUser, user); 10799 } 10800 10801 /** 10802 * Adds or replaces a new url name mapping in the offline project.<p> 10803 * 10804 * @param dbc the current database context 10805 * @param name the URL name of the mapping 10806 * @param structureId the structure id of the mapping 10807 * @param locale the locale of the mapping 10808 * @param replaceOnPublish if the mapping shoudl replace previous URL name mappings when published 10809 * 10810 * @throws CmsDataAccessException if something goes wrong 10811 */ 10812 protected void addOrReplaceUrlNameMapping( 10813 CmsDbContext dbc, 10814 String name, 10815 CmsUUID structureId, 10816 String locale, 10817 boolean replaceOnPublish) 10818 throws CmsDataAccessException { 10819 10820 getVfsDriver(dbc).deleteUrlNameMappingEntries( 10821 dbc, 10822 false, 10823 CmsUrlNameMappingFilter.ALL.filterStructureId(structureId).filterLocale(locale).filterStates( 10824 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 10825 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 10826 CmsUrlNameMappingEntry newEntry = new CmsUrlNameMappingEntry( 10827 name, 10828 structureId, 10829 replaceOnPublish 10830 ? CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH 10831 : CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 10832 System.currentTimeMillis(), 10833 locale); 10834 getVfsDriver(dbc).addUrlNameMappingEntry(dbc, false, newEntry); 10835 } 10836 10837 /** 10838 * Converts a resource to a folder (if possible).<p> 10839 * 10840 * @param resource the resource to convert 10841 * @return the converted resource 10842 * 10843 * @throws CmsVfsResourceNotFoundException if the resource is not a folder 10844 */ 10845 protected CmsFolder convertResourceToFolder(CmsResource resource) throws CmsVfsResourceNotFoundException { 10846 10847 if (resource.isFolder()) { 10848 return new CmsFolder(resource); 10849 } 10850 10851 throw new CmsVfsResourceNotFoundException( 10852 Messages.get().container(Messages.ERR_ACCESS_FILE_AS_FOLDER_1, resource.getRootPath())); 10853 } 10854 10855 /** 10856 * Helper method for creating a driver from configuration data.<p> 10857 * 10858 * @param dbc the db context 10859 * @param configManager the configuration manager 10860 * @param config the configuration 10861 * @param driverChainKey the configuration key under which the driver chain is stored 10862 * @param suffix the suffix to append to a driver chain entry to get the key for the driver class 10863 * 10864 * @return the newly created driver 10865 */ 10866 protected Object createDriver( 10867 CmsDbContext dbc, 10868 CmsConfigurationManager configManager, 10869 CmsParameterConfiguration config, 10870 String driverChainKey, 10871 String suffix) { 10872 10873 // read the vfs driver class properties and initialize a new instance 10874 List<String> drivers = config.getList(driverChainKey); 10875 String driverKey = drivers.get(0) + suffix; 10876 String driverName = config.get(driverKey); 10877 drivers = (drivers.size() > 1) ? drivers.subList(1, drivers.size()) : null; 10878 if (driverName == null) { 10879 CmsLog.INIT.error(Messages.get().getBundle().key(Messages.INIT_DRIVER_FAILED_1, driverKey)); 10880 } 10881 Object result = newDriverInstance(dbc, configManager, driverName, drivers); 10882 if ("true".equalsIgnoreCase(System.getProperty("opencms.profile.drivers"))) { 10883 result = wrapDriverInProfilingProxy(result); 10884 } 10885 return result; 10886 } 10887 10888 /** 10889 * Deletes all relations for the given resource and all its siblings.<p> 10890 * 10891 * @param dbc the current database context 10892 * @param resource the resource to delete the resource for 10893 * 10894 * @throws CmsException if something goes wrong 10895 */ 10896 protected void deleteRelationsWithSiblings(CmsDbContext dbc, CmsResource resource) throws CmsException { 10897 10898 // get all siblings 10899 List<CmsResource> siblings; 10900 if (resource.getSiblingCount() > 1) { 10901 siblings = readSiblings(dbc, resource, CmsResourceFilter.ALL); 10902 } else { 10903 siblings = new ArrayList<CmsResource>(); 10904 siblings.add(resource); 10905 } 10906 // clean the relations in content for all siblings 10907 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 10908 Iterator<CmsResource> it = siblings.iterator(); 10909 while (it.hasNext()) { 10910 CmsResource sibling = it.next(); 10911 // clean the relation information for this sibling 10912 vfsDriver.deleteRelations( 10913 dbc, 10914 dbc.currentProject().getUuid(), 10915 sibling, 10916 CmsRelationFilter.TARGETS.filterDefinedInContent()); 10917 } 10918 } 10919 10920 /** 10921 * Tries to add sub-resources of moved folders to the publish list and throws an exception if the publish list still does 10922 * not contain some sub-resources of the moved folders.<p> 10923 * 10924 * @param cms the current CMS context 10925 * @param dbc the current database context 10926 * @param pubList the publish list 10927 * @throws CmsException if something goes wrong 10928 */ 10929 protected void ensureSubResourcesOfMovedFoldersPublished(CmsObject cms, CmsDbContext dbc, CmsPublishList pubList) 10930 throws CmsException { 10931 10932 List<CmsResource> topMovedFolders = pubList.getTopMovedFolders(cms); 10933 Iterator<CmsResource> folderIt = topMovedFolders.iterator(); 10934 while (folderIt.hasNext()) { 10935 CmsResource folder = folderIt.next(); 10936 addSubResources(dbc, pubList, folder, resource -> !resource.getState().isNew()); 10937 } 10938 List<CmsResource> missingSubResources = pubList.getMissingSubResources(cms, topMovedFolders); 10939 if (missingSubResources.isEmpty()) { 10940 return; 10941 } 10942 10943 StringBuffer pathBuffer = new StringBuffer(); 10944 10945 for (CmsResource missing : missingSubResources) { 10946 pathBuffer.append(missing.getRootPath()); 10947 pathBuffer.append(" "); 10948 } 10949 throw new CmsVfsException( 10950 Messages.get().container(Messages.RPT_CHILDREN_OF_MOVED_FOLDER_NOT_PUBLISHED_1, pathBuffer.toString())); 10951 10952 } 10953 10954 /** 10955 * Tries to find the best name for an URL name mapping for the given structure id.<p> 10956 * 10957 * @param dbc the database context 10958 * @param nameSeq the sequence of name candidates 10959 * @param structureId the structure id to which an URL name should be mapped 10960 * @param locale the locale for which the URL name should be mapped 10961 * 10962 * @return the selected URL name candidate 10963 * 10964 * @throws CmsDataAccessException if something goes wrong 10965 */ 10966 protected String findBestNameForUrlNameMapping( 10967 CmsDbContext dbc, 10968 Iterator<String> nameSeq, 10969 CmsUUID structureId, 10970 String locale) 10971 throws CmsDataAccessException { 10972 10973 String newName; 10974 boolean alreadyInUse; 10975 do { 10976 newName = nameSeq.next(); 10977 alreadyInUse = false; 10978 CmsUrlNameMappingFilter filter = CmsUrlNameMappingFilter.ALL.filterName(newName); 10979 List<CmsUrlNameMappingEntry> entriesWithSameName = getVfsDriver(dbc).readUrlNameMappingEntries( 10980 dbc, 10981 false, 10982 filter); 10983 for (CmsUrlNameMappingEntry entry : entriesWithSameName) { 10984 boolean sameId = entry.getStructureId().equals(structureId); 10985 if (!sameId) { 10986 // name already used for other resource, or for different locale of the same resource 10987 alreadyInUse = true; 10988 break; 10989 } 10990 } 10991 } while (alreadyInUse); 10992 return newName; 10993 } 10994 10995 /** 10996 * Helper method for finding the 'best' URL name to use for a new URL name mapping.<p> 10997 * 10998 * Since the name given as a parameter may be already used, this method will try to append numeric suffixes 10999 * to the name to find a mapping name which is not used.<p> 11000 * 11001 * @param dbc the current database context 11002 * @param name the name of the mapping 11003 * @param structureId the structure id to which the name is mapped 11004 * 11005 * @return the best name which was found for the new mapping 11006 * 11007 * @throws CmsDataAccessException if something goes wrong 11008 */ 11009 protected String findBestNameForUrlNameMapping(CmsDbContext dbc, String name, CmsUUID structureId) 11010 throws CmsDataAccessException { 11011 11012 List<CmsUrlNameMappingEntry> entriesStartingWithName = getVfsDriver(dbc).readUrlNameMappingEntries( 11013 dbc, 11014 false, 11015 CmsUrlNameMappingFilter.ALL.filterNamePattern(name + "%").filterRejectStructureId(structureId)); 11016 Set<String> usedNames = new HashSet<String>(); 11017 for (CmsUrlNameMappingEntry entry : entriesStartingWithName) { 11018 usedNames.add(entry.getName()); 11019 } 11020 int counter = 0; 11021 String numberedName; 11022 do { 11023 numberedName = getNumberedName(name, counter); 11024 counter += 1; 11025 } while (usedNames.contains(numberedName)); 11026 return numberedName; 11027 } 11028 11029 /** 11030 * Returns the lock manager instance.<p> 11031 * 11032 * @return the lock manager instance 11033 */ 11034 protected CmsLockManager getLockManager() { 11035 11036 return m_lockManager; 11037 } 11038 11039 /** 11040 * Adds a numeric suffix to the end of a string, unless the number passed as a parameter is 0.<p> 11041 * 11042 * @param name the base name 11043 * @param number the number from which to form the suffix 11044 * 11045 * @return the concatenation of the base name and possibly the numeric suffix 11046 */ 11047 protected String getNumberedName(String name, int number) { 11048 11049 if (number == 0) { 11050 return name; 11051 } 11052 PrintfFormat fmt = new PrintfFormat("%0.6d"); 11053 return name + "_" + fmt.sprintf(number); 11054 } 11055 11056 /** 11057 * Resets the resources in a project to their online state.<p> 11058 * 11059 * @param dbc the database context 11060 * @param projectId the project id 11061 * @param modifiedFiles the modified files 11062 * @param modifiedFolders the modified folders 11063 * @throws CmsException if something goes wrong 11064 * @throws CmsSecurityException if we don't have the permissions 11065 * @throws CmsDataAccessException if something goes wrong with the database 11066 */ 11067 protected void resetResourcesInProject( 11068 CmsDbContext dbc, 11069 CmsUUID projectId, 11070 List<CmsResource> modifiedFiles, 11071 List<CmsResource> modifiedFolders) 11072 throws CmsException, CmsSecurityException, CmsDataAccessException { 11073 11074 // all resources inside the project have to be be reset to their online state. 11075 // 1. step: delete all new files 11076 for (int i = 0; i < modifiedFiles.size(); i++) { 11077 CmsResource currentFile = modifiedFiles.get(i); 11078 if (currentFile.getState().isNew()) { 11079 CmsLock lock = getLock(dbc, currentFile); 11080 if (lock.isNullLock()) { 11081 // lock the resource 11082 lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE); 11083 } else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) { 11084 changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE); 11085 } 11086 // delete the properties 11087 getVfsDriver(dbc).deletePropertyObjects( 11088 dbc, 11089 projectId, 11090 currentFile, 11091 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 11092 // delete the file 11093 getVfsDriver(dbc).removeFile(dbc, dbc.currentProject().getUuid(), currentFile); 11094 // remove the access control entries 11095 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFile.getResourceId()); 11096 // fire the corresponding event 11097 OpenCms.fireCmsEvent( 11098 new CmsEvent( 11099 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11100 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile))); 11101 } 11102 } 11103 11104 // 2. step: delete all new folders 11105 for (int i = 0; i < modifiedFolders.size(); i++) { 11106 CmsResource currentFolder = modifiedFolders.get(i); 11107 if (currentFolder.getState().isNew()) { 11108 // delete the properties 11109 getVfsDriver(dbc).deletePropertyObjects( 11110 dbc, 11111 projectId, 11112 currentFolder, 11113 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 11114 // delete the folder 11115 getVfsDriver(dbc).removeFolder(dbc, dbc.currentProject(), currentFolder); 11116 // remove the access control entries 11117 getUserDriver(dbc).removeAccessControlEntries(dbc, dbc.currentProject(), currentFolder.getResourceId()); 11118 // fire the corresponding event 11119 OpenCms.fireCmsEvent( 11120 new CmsEvent( 11121 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11122 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder))); 11123 } 11124 } 11125 11126 // 3. step: undo changes on all changed or deleted folders 11127 for (int i = 0; i < modifiedFolders.size(); i++) { 11128 CmsResource currentFolder = modifiedFolders.get(i); 11129 if ((currentFolder.getState().isChanged()) || (currentFolder.getState().isDeleted())) { 11130 CmsLock lock = getLock(dbc, currentFolder); 11131 if (lock.isNullLock()) { 11132 // lock the resource 11133 lockResource(dbc, currentFolder, CmsLockType.EXCLUSIVE); 11134 } else if (!lock.isOwnedBy(dbc.currentUser()) || !lock.isInProject(dbc.currentProject())) { 11135 changeLock(dbc, currentFolder, CmsLockType.EXCLUSIVE); 11136 } 11137 // undo all changes in the folder 11138 undoChanges(dbc, currentFolder, CmsResource.UNDO_CONTENT); 11139 // fire the corresponding event 11140 OpenCms.fireCmsEvent( 11141 new CmsEvent( 11142 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11143 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFolder))); 11144 } 11145 } 11146 11147 // 4. step: undo changes on all changed or deleted files 11148 for (int i = 0; i < modifiedFiles.size(); i++) { 11149 CmsResource currentFile = modifiedFiles.get(i); 11150 if (currentFile.getState().isChanged() || currentFile.getState().isDeleted()) { 11151 CmsLock lock = getLock(dbc, currentFile); 11152 if (lock.isNullLock()) { 11153 // lock the resource 11154 lockResource(dbc, currentFile, CmsLockType.EXCLUSIVE); 11155 } else if (!lock.isOwnedInProjectBy(dbc.currentUser(), dbc.currentProject())) { 11156 if (lock.isLockableBy(dbc.currentUser())) { 11157 changeLock(dbc, currentFile, CmsLockType.EXCLUSIVE); 11158 } 11159 } 11160 // undo all changes in the file 11161 undoChanges(dbc, currentFile, CmsResource.UNDO_CONTENT); 11162 // fire the corresponding event 11163 OpenCms.fireCmsEvent( 11164 new CmsEvent( 11165 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 11166 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, currentFile))); 11167 } 11168 } 11169 } 11170 11171 /** 11172 * Counts the total number of users which fit the given criteria.<p> 11173 * 11174 * @param dbc the database context 11175 * @param searchParams the user search criteria 11176 * 11177 * @return the total number of users matching the criteria 11178 * 11179 * @throws CmsDataAccessException if something goes wrong 11180 */ 11181 long countUsers(CmsDbContext dbc, CmsUserSearchParameters searchParams) throws CmsDataAccessException { 11182 11183 return getUserDriver(dbc).countUsers(dbc, searchParams); 11184 } 11185 11186 /** 11187 * Adds a pool to the static pool map.<p> 11188 * 11189 * @param pool the pool to add 11190 */ 11191 private void addPool(CmsDbPoolV11 pool) { 11192 11193 m_pools.put(pool.getPoolUrl(), pool); 11194 } 11195 11196 /** 11197 * Adds all sub-resources of the given resource to the publish list.<p> 11198 * 11199 * @param dbc the database context 11200 * @param publishList the publish list 11201 * @param directPublishResource the resource to get the sub-resources for 11202 * @param additionalFilter an additional test for resources to pass before they are added to the publish list 11203 * 11204 * @throws CmsDataAccessException if something goes wrong accessing the database 11205 */ 11206 private void addSubResources( 11207 CmsDbContext dbc, 11208 CmsPublishList publishList, 11209 CmsResource directPublishResource, 11210 Predicate<CmsResource> additionalFilter) 11211 throws CmsDataAccessException { 11212 11213 int flags = CmsDriverManager.READMODE_INCLUDE_TREE | CmsDriverManager.READMODE_EXCLUDE_STATE; 11214 if (!directPublishResource.getState().isDeleted()) { 11215 // fix for org.opencms.file.TestPublishIssues#testPublishFolderWithDeletedFileFromOtherProject 11216 flags = flags | CmsDriverManager.READMODE_INCLUDE_PROJECT; 11217 } 11218 11219 // add all sub resources of the folder 11220 List<CmsResource> folderList = getVfsDriver(dbc).readResourceTree( 11221 dbc, 11222 dbc.currentProject().getUuid(), 11223 directPublishResource.getRootPath(), 11224 CmsDriverManager.READ_IGNORE_TYPE, 11225 CmsResource.STATE_UNCHANGED, 11226 CmsDriverManager.READ_IGNORE_TIME, 11227 CmsDriverManager.READ_IGNORE_TIME, 11228 CmsDriverManager.READ_IGNORE_TIME, 11229 CmsDriverManager.READ_IGNORE_TIME, 11230 CmsDriverManager.READ_IGNORE_TIME, 11231 CmsDriverManager.READ_IGNORE_TIME, 11232 flags | CmsDriverManager.READMODE_ONLY_FOLDERS); 11233 11234 publishList.addAll( 11235 filterResources(dbc, publishList, folderList).stream().filter(additionalFilter).collect( 11236 Collectors.toList()), 11237 true); 11238 11239 List<CmsResource> fileList = getVfsDriver(dbc).readResourceTree( 11240 dbc, 11241 dbc.currentProject().getUuid(), 11242 directPublishResource.getRootPath(), 11243 CmsDriverManager.READ_IGNORE_TYPE, 11244 CmsResource.STATE_UNCHANGED, 11245 CmsDriverManager.READ_IGNORE_TIME, 11246 CmsDriverManager.READ_IGNORE_TIME, 11247 CmsDriverManager.READ_IGNORE_TIME, 11248 CmsDriverManager.READ_IGNORE_TIME, 11249 CmsDriverManager.READ_IGNORE_TIME, 11250 CmsDriverManager.READ_IGNORE_TIME, 11251 flags | CmsDriverManager.READMODE_ONLY_FILES); 11252 11253 publishList.addAll( 11254 filterResources(dbc, publishList, fileList).stream().filter(additionalFilter).collect(Collectors.toList()), 11255 true); 11256 } 11257 11258 /** 11259 * Helper method to check whether we should bother with reading the group for a given role in a given OU.<p> 11260 * 11261 * 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. 11262 * 11263 * @param ou the OU 11264 * @param role the role 11265 * @return true if we should read the role in the OU 11266 */ 11267 private boolean canReadRoleInOu(CmsOrganizationalUnit ou, CmsRole role) { 11268 11269 if (ou.hasFlagWebuser() && !role.getRoleName().equals(CmsRole.ACCOUNT_MANAGER.getRoleName())) { 11270 return false; 11271 } 11272 return true; 11273 } 11274 11275 /** 11276 * Checks the parent of a resource during publishing.<p> 11277 * 11278 * @param dbc the current database context 11279 * @param deletedFolders a list of deleted folders 11280 * @param res a resource to check the parent for 11281 * 11282 * @return <code>true</code> if the parent resource will be deleted during publishing 11283 */ 11284 private boolean checkDeletedParentFolder(CmsDbContext dbc, List<CmsResource> deletedFolders, CmsResource res) { 11285 11286 String parentPath = CmsResource.getParentFolder(res.getRootPath()); 11287 11288 if (parentPath == null) { 11289 // resource has no parent 11290 return false; 11291 } 11292 11293 CmsResource parent; 11294 try { 11295 parent = readResource(dbc, parentPath, CmsResourceFilter.ALL); 11296 } catch (Exception e) { 11297 // failure: if we cannot read the parent, we should not publish the resource 11298 return false; 11299 } 11300 11301 if (!parent.getState().isDeleted()) { 11302 // parent is not deleted 11303 return false; 11304 } 11305 11306 for (int j = 0; j < deletedFolders.size(); j++) { 11307 if ((deletedFolders.get(j)).getStructureId().equals(parent.getStructureId())) { 11308 // parent is deleted, and it will get published 11309 return true; 11310 } 11311 } 11312 11313 // parent is new, but it will not get published 11314 return false; 11315 } 11316 11317 /** 11318 * Checks that no one of the resources to be published has a 'new' parent (that has not been published yet).<p> 11319 * 11320 * @param dbc the db context 11321 * @param publishList the publish list to check 11322 * 11323 * @throws CmsVfsException if there is a resource to be published with a 'new' parent 11324 */ 11325 private void checkParentFolders(CmsDbContext dbc, CmsPublishList publishList) throws CmsVfsException { 11326 11327 boolean directPublish = publishList.isDirectPublish(); 11328 // if we direct publish a file, check if all parent folders are already published 11329 if (directPublish) { 11330 // first get the names of all parent folders 11331 Iterator<CmsResource> it = publishList.getDirectPublishResources().iterator(); 11332 List<String> parentFolderNames = new ArrayList<String>(); 11333 while (it.hasNext()) { 11334 CmsResource res = it.next(); 11335 String parentFolderName = CmsResource.getParentFolder(res.getRootPath()); 11336 if (parentFolderName != null) { 11337 parentFolderNames.add(parentFolderName); 11338 } 11339 } 11340 // remove duplicate parent folder names 11341 parentFolderNames = CmsFileUtil.removeRedundancies(parentFolderNames); 11342 String parentFolderName = null; 11343 try { 11344 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 11345 // now check all folders if they exist in the online project 11346 Iterator<String> parentIt = parentFolderNames.iterator(); 11347 while (parentIt.hasNext()) { 11348 parentFolderName = parentIt.next(); 11349 vfsDriver.readFolder(dbc, CmsProject.ONLINE_PROJECT_ID, parentFolderName); 11350 } 11351 } catch (CmsException e) { 11352 throw new CmsVfsException( 11353 Messages.get().container(Messages.RPT_PARENT_FOLDER_NOT_PUBLISHED_1, parentFolderName)); 11354 } 11355 } 11356 } 11357 11358 /** 11359 * Checks the parent of a resource during publishing.<p> 11360 * 11361 * @param dbc the current database context 11362 * @param folderList a list of folders 11363 * @param res a resource to check the parent for 11364 * 11365 * @return true if the resource should be published 11366 */ 11367 private boolean checkParentResource(CmsDbContext dbc, List<CmsResource> folderList, CmsResource res) { 11368 11369 String parentPath = CmsResource.getParentFolder(res.getRootPath()); 11370 11371 if (parentPath == null) { 11372 // resource has no parent 11373 return true; 11374 } 11375 11376 CmsResource parent; 11377 try { 11378 parent = readResource(dbc, parentPath, CmsResourceFilter.ALL); 11379 } catch (Exception e) { 11380 // failure: if we cannot read the parent, we should not publish the resource 11381 return false; 11382 } 11383 11384 if (!parent.getState().isNew()) { 11385 // parent is already published 11386 return true; 11387 } 11388 11389 for (int j = 0; j < folderList.size(); j++) { 11390 if (folderList.get(j).getStructureId().equals(parent.getStructureId())) { 11391 // parent is new, but it will get published 11392 return true; 11393 } 11394 } 11395 11396 // parent is new, but it will not get published 11397 return false; 11398 } 11399 11400 /** 11401 * Copies all relations from the source resource to the target resource.<p> 11402 * 11403 * @param dbc the database context 11404 * @param source the source 11405 * @param target the target 11406 * 11407 * @throws CmsException if something goes wrong 11408 */ 11409 private void copyRelations(CmsDbContext dbc, CmsResource source, CmsResource target) throws CmsException { 11410 11411 // copy relations all relations 11412 CmsObject cms = new CmsObject(getSecurityManager(), dbc.getRequestContext()); 11413 Iterator<CmsRelation> itRelations = getRelationsForResource( 11414 dbc, 11415 source, 11416 CmsRelationFilter.TARGETS.filterNotDefinedInContent()).iterator(); 11417 while (itRelations.hasNext()) { 11418 CmsRelation relation = itRelations.next(); 11419 if (relation.getType().getCopyBehavior() == CopyBehavior.copy) { 11420 try { 11421 CmsResource relTarget = relation.getTarget(cms, CmsResourceFilter.ALL); 11422 addRelationToResource(dbc, target, relTarget, relation.getType(), true); 11423 } catch (CmsVfsResourceNotFoundException e) { 11424 // ignore this broken relation 11425 if (LOG.isWarnEnabled()) { 11426 LOG.warn(e.getLocalizedMessage(), e); 11427 } 11428 } 11429 } 11430 } 11431 // repair categories 11432 repairCategories(dbc, getProjectIdForContext(dbc), target); 11433 } 11434 11435 /** 11436 * Filters the given list of resources, removes all resources where the current user 11437 * does not have READ permissions, plus the filter is applied.<p> 11438 * 11439 * @param dbc the current database context 11440 * @param resourceList a list of CmsResources 11441 * @param filter the resource filter to use 11442 * 11443 * @return the filtered list of resources 11444 * 11445 * @throws CmsException in case errors testing the permissions 11446 */ 11447 private List<CmsResource> filterPermissions( 11448 CmsDbContext dbc, 11449 List<CmsResource> resourceList, 11450 CmsResourceFilter filter) 11451 throws CmsException { 11452 11453 if (filter.requireTimerange()) { 11454 // never check time range here - this must be done later in #updateContextDates(...) 11455 filter = filter.addExcludeTimerange(); 11456 } 11457 ResourceListWithCacheability result = new ResourceListWithCacheability(); 11458 boolean nocacheWasSet = false; 11459 if (null == dbc.getAttribute(ATTR_PERMISSION_NOCACHE)) { 11460 // The attribute will be used by the permission handler to tell us that lists containing the resource 11461 // should not be cached. 11462 dbc.setAttribute(ATTR_PERMISSION_NOCACHE, new boolean[] {false}); 11463 // insurance against potential indirect recursive calls introduced by future code changes: 11464 // make sure we only remove the attribute later if we were the one who set it 11465 nocacheWasSet = true; 11466 } 11467 try { 11468 for (int i = 0; i < resourceList.size(); i++) { 11469 // check the permission of all resources 11470 CmsResource currentResource = resourceList.get(i); 11471 if (m_securityManager.hasPermissions( 11472 dbc, 11473 currentResource, 11474 CmsPermissionSet.ACCESS_READ, 11475 LockCheck.yes, 11476 filter).isAllowed()) { 11477 // only return resources where permission was granted 11478 result.add(currentResource); 11479 } 11480 } 11481 } finally { 11482 if (nocacheWasSet) { 11483 boolean[] nocache = (boolean[])dbc.getAttribute(ATTR_PERMISSION_NOCACHE); 11484 if (nocache != null) { 11485 dbc.removeAttribute(ATTR_PERMISSION_NOCACHE); 11486 if (nocache[0]) { 11487 result.setCacheable(false); 11488 } 11489 } 11490 } 11491 } 11492 // return the result 11493 return result; 11494 } 11495 11496 /** 11497 * Returns a filtered list of resources for publishing.<p> 11498 * Contains all resources, which are not locked 11499 * and which have a parent folder that is already published or will be published, too.<p> 11500 * 11501 * @param dbc the current database context 11502 * @param publishList the filling publish list 11503 * @param resourceList the list of resources to filter 11504 * 11505 * @return a filtered list of resources 11506 */ 11507 private List<CmsResource> filterResources( 11508 CmsDbContext dbc, 11509 CmsPublishList publishList, 11510 List<CmsResource> resourceList) { 11511 11512 List<CmsResource> result = new ArrayList<CmsResource>(); 11513 11514 // local folder list for adding new publishing subfolders 11515 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioD} problem. 11516 List<CmsResource> newFolderList = new ArrayList<CmsResource>( 11517 publishList == null ? resourceList : publishList.getFolderList()); 11518 11519 for (int i = 0; i < resourceList.size(); i++) { 11520 CmsResource res = resourceList.get(i); 11521 try { 11522 CmsLock lock = getLock(dbc, res); 11523 if (lock.isPublish()) { 11524 // if already enqueued 11525 continue; 11526 } 11527 if (!lock.isLockableBy(dbc.currentUser())) { 11528 // checks if there is a shared lock and if the resource is deleted 11529 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem. 11530 if (lock.isShared() && (publishList != null)) { 11531 if (!res.getState().isDeleted() 11532 || !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) { 11533 continue; 11534 } 11535 } else { 11536 // don't add locked resources 11537 continue; 11538 } 11539 } 11540 if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, newFolderList, res)) { 11541 continue; 11542 } 11543 // check permissions 11544 try { 11545 m_securityManager.checkPermissions( 11546 dbc, 11547 res, 11548 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 11549 false, 11550 CmsResourceFilter.ALL); 11551 } catch (CmsException e) { 11552 // skip if not enough permissions 11553 continue; 11554 } 11555 if (res.isFolder()) { 11556 newFolderList.add(res); 11557 } 11558 result.add(res); 11559 } catch (Exception e) { 11560 // should never happen 11561 LOG.error(e.getLocalizedMessage(), e); 11562 } 11563 } 11564 return result; 11565 } 11566 11567 /** 11568 * Returns a filtered list of sibling resources for publishing.<p> 11569 * 11570 * Contains all siblings of the given resources, which are not locked 11571 * and which have a parent folder that is already published or will be published, too.<p> 11572 * 11573 * @param dbc the current database context 11574 * @param publishList the unfinished publish list 11575 * @param resourceList the list of siblings to filter 11576 * 11577 * @return a filtered list of sibling resources for publishing 11578 */ 11579 private List<CmsResource> filterSiblings( 11580 CmsDbContext dbc, 11581 CmsPublishList publishList, 11582 Collection<CmsResource> resourceList) { 11583 11584 List<CmsResource> result = new ArrayList<CmsResource>(); 11585 11586 // removed internal extendible folder list, since iterated (sibling) resources are files in any case, never folders 11587 11588 for (CmsResource res : resourceList) { 11589 try { 11590 CmsLock lock = getLock(dbc, res); 11591 if (lock.isPublish()) { 11592 // if already enqueued 11593 continue; 11594 } 11595 if (!lock.isLockableBy(dbc.currentUser())) { 11596 // checks if there is a shared lock and if the resource is deleted 11597 // this solves the {@link org.opencms.file.TestPublishIssues#testPublishScenarioE} problem. 11598 if (lock.isShared() && (publishList != null)) { 11599 if (!res.getState().isDeleted() 11600 || !checkDeletedParentFolder(dbc, publishList.getDeletedFolderList(), res)) { 11601 continue; 11602 } 11603 } else { 11604 // don't add locked resources 11605 continue; 11606 } 11607 } 11608 if (!"/".equals(res.getRootPath()) && !checkParentResource(dbc, publishList.getFolderList(), res)) { 11609 // don't add resources that have no parent in the online project 11610 continue; 11611 } 11612 // check permissions 11613 try { 11614 m_securityManager.checkPermissions( 11615 dbc, 11616 res, 11617 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 11618 false, 11619 CmsResourceFilter.ALL); 11620 } catch (CmsException e) { 11621 // skip if not enough permissions 11622 continue; 11623 } 11624 result.add(res); 11625 } catch (Exception e) { 11626 // should never happen 11627 LOG.error(e.getLocalizedMessage(), e); 11628 } 11629 } 11630 return result; 11631 } 11632 11633 /** 11634 * Returns the access control list of a given resource.<p> 11635 * 11636 * @param dbc the current database context 11637 * @param resource the resource 11638 * @param forFolder should be true if resource is a folder 11639 * @param depth the depth to include non-inherited access entries, also 11640 * @param inheritedOnly flag indicates to collect inherited permissions only 11641 * 11642 * @return the access control list of the resource 11643 * 11644 * @throws CmsException if something goes wrong 11645 */ 11646 private CmsAccessControlList getAccessControlList( 11647 CmsDbContext dbc, 11648 CmsResource resource, 11649 boolean inheritedOnly, 11650 boolean forFolder, 11651 int depth) 11652 throws CmsException { 11653 11654 String cacheKey = getCacheKey( 11655 new String[] { 11656 inheritedOnly ? "+" : "-", 11657 forFolder ? "+" : "-", 11658 Integer.toString(depth), 11659 resource.getStructureId().toString()}, 11660 dbc); 11661 11662 CmsAccessControlList acl = m_monitor.getCachedACL(cacheKey); 11663 11664 // return the cached acl if already available 11665 if ((acl != null) && dbc.getProjectId().isNullUUID()) { 11666 return acl; 11667 } 11668 11669 List<CmsAccessControlEntry> aces = getUserDriver(dbc).readAccessControlEntries( 11670 dbc, 11671 dbc.currentProject(), 11672 resource.getResourceId(), 11673 (depth > 1) || ((depth > 0) && forFolder)); 11674 11675 // sort the list of aces 11676 boolean overwriteAll = sortAceList(aces); 11677 11678 // if no 'overwrite all' ace was found 11679 if (!overwriteAll) { 11680 // get the acl of the parent 11681 CmsResource parentResource = null; 11682 try { 11683 // try to recurse over the id 11684 parentResource = getVfsDriver(dbc).readParentFolder( 11685 dbc, 11686 dbc.currentProject().getUuid(), 11687 resource.getStructureId()); 11688 } catch (CmsVfsResourceNotFoundException e) { 11689 // should never happen, but try with the path 11690 String parentPath = CmsResource.getParentFolder(resource.getRootPath()); 11691 if (parentPath != null) { 11692 parentResource = getVfsDriver(dbc).readFolder(dbc, dbc.currentProject().getUuid(), parentPath); 11693 } 11694 } 11695 if (parentResource != null) { 11696 acl = (CmsAccessControlList)getAccessControlList( 11697 dbc, 11698 parentResource, 11699 inheritedOnly, 11700 forFolder, 11701 depth + 1).clone(); 11702 } 11703 } 11704 if (acl == null) { 11705 acl = new CmsAccessControlList(); 11706 } 11707 11708 Set<CmsUUID> exclusiveAccessPrincipals = new HashSet<>(); 11709 if (!((depth == 0) && inheritedOnly)) { 11710 Iterator<CmsAccessControlEntry> itAces = aces.iterator(); 11711 while (itAces.hasNext()) { 11712 CmsAccessControlEntry acEntry = itAces.next(); 11713 if (depth > 0) { 11714 acEntry.setFlags(CmsAccessControlEntry.ACCESS_FLAGS_INHERITED); 11715 } 11716 if ((depth == 0) 11717 && resource.isFile() 11718 && (0 != (acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE))) { 11719 11720 // 'responsible' flag is only interpreted as exclusive access if it's not inherited and set directly on a file 11721 exclusiveAccessPrincipals.add(acEntry.getPrincipal()); 11722 } 11723 11724 acl.add(acEntry); 11725 11726 // if the overwrite flag is set, reset the allowed permissions to the permissions of this entry 11727 // denied permissions are kept or extended 11728 if ((acEntry.getFlags() & CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE) > 0) { 11729 acl.setAllowedPermissions(acEntry); 11730 } 11731 } 11732 } 11733 if (exclusiveAccessPrincipals.size() > 0) { 11734 acl.setExclusiveAccessPrincipals(exclusiveAccessPrincipals); 11735 } 11736 11737 if (dbc.getProjectId().isNullUUID()) { 11738 m_monitor.cacheACL(cacheKey, acl); 11739 } 11740 return acl; 11741 } 11742 11743 /** 11744 * Return a cache key build from the provided information.<p> 11745 * 11746 * @param prefix a prefix for the key 11747 * @param flag a boolean flag for the key (only used if prefix is not null) 11748 * @param projectId the project for which to generate the key 11749 * @param resource the resource for which to generate the key 11750 * 11751 * @return String a cache key build from the provided information 11752 */ 11753 private String getCacheKey(String prefix, boolean flag, CmsUUID projectId, String resource) { 11754 11755 StringBuffer b = new StringBuffer(64); 11756 if (prefix != null) { 11757 b.append(prefix); 11758 b.append(flag ? '+' : '-'); 11759 } 11760 b.append(CmsProject.isOnlineProject(projectId) ? '+' : '-'); 11761 return b.append(resource).toString(); 11762 } 11763 11764 /** 11765 * Return a cache key build from the provided information.<p> 11766 * 11767 * @param keys an array of keys to generate the cache key from 11768 * @param dbc the database context for which to generate the key 11769 * 11770 * @return String a cache key build from the provided information 11771 */ 11772 private String getCacheKey(String[] keys, CmsDbContext dbc) { 11773 11774 if (!dbc.getProjectId().isNullUUID()) { 11775 return ""; 11776 } 11777 StringBuffer b = new StringBuffer(64); 11778 int len = keys.length; 11779 if (len > 0) { 11780 for (int i = 0; i < len; i++) { 11781 b.append(keys[i]); 11782 b.append('_'); 11783 } 11784 } 11785 if (dbc.currentProject().isOnlineProject()) { 11786 b.append("+"); 11787 } else { 11788 b.append("-"); 11789 } 11790 return b.toString(); 11791 } 11792 11793 /** 11794 * Gets the correct driver interface to use for proxying a specific driver instance.<p> 11795 * 11796 * @param obj the driver instance 11797 * @return the interface to use for proxying 11798 */ 11799 private Class<?> getDriverInterfaceForProxy(Object obj) { 11800 11801 for (Class<?> interfaceClass : new Class[] { 11802 I_CmsUserDriver.class, 11803 I_CmsVfsDriver.class, 11804 I_CmsProjectDriver.class, 11805 I_CmsHistoryDriver.class, 11806 I_CmsSubscriptionDriver.class}) { 11807 if (interfaceClass.isAssignableFrom(obj.getClass())) { 11808 return interfaceClass; 11809 } 11810 } 11811 return null; 11812 } 11813 11814 /** 11815 * Returns the correct project id.<p> 11816 * 11817 * @param dbc the database context 11818 * 11819 * @return the correct project id 11820 */ 11821 private CmsUUID getProjectIdForContext(CmsDbContext dbc) { 11822 11823 CmsUUID projectId = dbc.getProjectId(); 11824 if (projectId.isNullUUID()) { 11825 projectId = dbc.currentProject().getUuid(); 11826 } 11827 return projectId; 11828 } 11829 11830 /** 11831 * Returns if and what state needs to be updated.<p> 11832 * 11833 * @param dbc the db context 11834 * @param resource the resource 11835 * @param properties the properties to check 11836 * 11837 * @return 0: none, 1: structure, 2: resource 11838 * 11839 * @throws CmsDataAccessException if something goes wrong 11840 */ 11841 private int getUpdateState(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties) 11842 throws CmsDataAccessException { 11843 11844 int updateState = 0; 11845 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 11846 Iterator<CmsProperty> it = properties.iterator(); 11847 while (it.hasNext() && (updateState < 2)) { 11848 CmsProperty property = it.next(); 11849 11850 // read existing property 11851 CmsProperty existingProperty = vfsDriver.readPropertyObject( 11852 dbc, 11853 property.getName(), 11854 dbc.currentProject(), 11855 resource); 11856 11857 // check the shared property 11858 if (property.getResourceValue() != null) { 11859 if (property.isDeleteResourceValue()) { 11860 if (existingProperty.getResourceValue() != null) { 11861 updateState = 2; // deleted 11862 } 11863 } else { 11864 if (existingProperty.getResourceValue() == null) { 11865 updateState = 2; // created 11866 } else { 11867 if (!property.getResourceValue().equals(existingProperty.getResourceValue())) { 11868 updateState = 2; // updated 11869 } 11870 } 11871 } 11872 } 11873 if (updateState == 0) { 11874 // check the individual property only if needed 11875 if (property.getStructureValue() != null) { 11876 if (property.isDeleteStructureValue()) { 11877 if (existingProperty.getStructureValue() != null) { 11878 updateState = 1; // deleted 11879 } 11880 } else { 11881 if (existingProperty.getStructureValue() == null) { 11882 updateState = 1; // created 11883 } else { 11884 if (!property.getStructureValue().equals(existingProperty.getStructureValue())) { 11885 updateState = 1; // updated 11886 } 11887 } 11888 } 11889 } 11890 } 11891 } 11892 return updateState; 11893 } 11894 11895 /** 11896 * Returns all groups that are virtualizing the given role in the given ou.<p> 11897 * 11898 * @param dbc the database context 11899 * @param role the role 11900 * 11901 * @return all groups that are virtualizing the given role (or a child of it) 11902 * 11903 * @throws CmsException if something goes wrong 11904 */ 11905 private List<CmsGroup> getVirtualGroupsForRole(CmsDbContext dbc, CmsRole role) throws CmsException { 11906 11907 Set<Integer> roleFlags = new HashSet<Integer>(); 11908 // add role flag 11909 Integer flags = Integer.valueOf(role.getVirtualGroupFlags()); 11910 roleFlags.add(flags); 11911 // collect all child role flags 11912 Iterator<CmsRole> itChildRoles = role.getChildren(true).iterator(); 11913 while (itChildRoles.hasNext()) { 11914 CmsRole child = itChildRoles.next(); 11915 flags = Integer.valueOf(child.getVirtualGroupFlags()); 11916 roleFlags.add(flags); 11917 } 11918 // iterate all groups matching the flags 11919 List<CmsGroup> groups = new ArrayList<CmsGroup>(); 11920 Iterator<CmsGroup> it = getGroups(dbc, readOrganizationalUnit(dbc, role.getOuFqn()), false, false).iterator(); 11921 while (it.hasNext()) { 11922 CmsGroup group = it.next(); 11923 if (group.isVirtual()) { 11924 CmsRole r = CmsRole.valueOf(group); 11925 if (roleFlags.contains(Integer.valueOf(r.getVirtualGroupFlags()))) { 11926 groups.add(group); 11927 } 11928 } 11929 } 11930 return groups; 11931 } 11932 11933 /** 11934 * Returns a list of users in a group.<p> 11935 * 11936 * @param dbc the current database context 11937 * @param ouFqn the organizational unit to get the users from 11938 * @param groupname the name of the group to list users from 11939 * @param includeOtherOuUsers include users of other organizational units 11940 * @param directUsersOnly if set only the direct assigned users will be returned, 11941 * if not also indirect users, ie. members of parent roles, 11942 * this parameter only works with roles 11943 * @param readRoles if to read roles or groups 11944 * 11945 * @return all <code>{@link CmsUser}</code> objects in the group 11946 * 11947 * @throws CmsException if operation was not successful 11948 */ 11949 private List<CmsUser> internalUsersOfGroup( 11950 CmsDbContext dbc, 11951 String ouFqn, 11952 String groupname, 11953 boolean includeOtherOuUsers, 11954 boolean directUsersOnly, 11955 boolean readRoles) 11956 throws CmsException { 11957 11958 CmsGroup group = readGroup(dbc, groupname); // check that the group really exists 11959 if ((group == null) || (!((!readRoles && !group.isRole()) || (readRoles && group.isRole())))) { 11960 throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_UNKNOWN_GROUP_1, groupname)); 11961 } 11962 11963 String prefix = "_" + includeOtherOuUsers + "_" + directUsersOnly + "_" + ouFqn; 11964 String cacheKey = m_keyGenerator.getCacheKeyForGroupUsers(prefix, dbc, group); 11965 List<CmsUser> allUsers = m_monitor.getCachedUserList(cacheKey); 11966 if (allUsers == null) { 11967 Set<CmsUser> users = new HashSet<CmsUser>( 11968 getUserDriver(dbc).readUsersOfGroup(dbc, groupname, includeOtherOuUsers)); 11969 if (readRoles && !directUsersOnly) { 11970 CmsRole role = CmsRole.valueOf(group); 11971 if (role.getParentRole() != null) { 11972 try { 11973 String parentGroup = role.getParentRole().getGroupName(); 11974 readGroup(dbc, parentGroup); 11975 // iterate the parent roles 11976 users.addAll( 11977 internalUsersOfGroup( 11978 dbc, 11979 ouFqn, 11980 parentGroup, 11981 includeOtherOuUsers, 11982 directUsersOnly, 11983 readRoles)); 11984 } catch (CmsDbEntryNotFoundException e) { 11985 // ignore, this may happen while deleting an orgunit 11986 if (LOG.isDebugEnabled()) { 11987 LOG.debug(e.getLocalizedMessage(), e); 11988 } 11989 } 11990 } 11991 String parentOu = CmsOrganizationalUnit.getParentFqn(group.getOuFqn()); 11992 if (parentOu != null) { 11993 // iterate the parent ou's 11994 users.addAll( 11995 internalUsersOfGroup( 11996 dbc, 11997 ouFqn, 11998 parentOu + group.getSimpleName(), 11999 includeOtherOuUsers, 12000 directUsersOnly, 12001 readRoles)); 12002 } 12003 } else if (!readRoles && !directUsersOnly) { 12004 List<CmsGroup> groups = getChildren(dbc, group, false); 12005 for (CmsGroup parentGroup : groups) { 12006 try { 12007 // iterate the parent groups 12008 users.addAll( 12009 internalUsersOfGroup( 12010 dbc, 12011 ouFqn, 12012 parentGroup.getName(), 12013 includeOtherOuUsers, 12014 directUsersOnly, 12015 readRoles)); 12016 } catch (CmsDbEntryNotFoundException e) { 12017 // ignore, this may happen while deleting an orgunit 12018 if (LOG.isDebugEnabled()) { 12019 LOG.debug(e.getLocalizedMessage(), e); 12020 } 12021 } 12022 } 12023 } 12024 // filter users from other ous 12025 if (!includeOtherOuUsers) { 12026 Iterator<CmsUser> itUsers = users.iterator(); 12027 while (itUsers.hasNext()) { 12028 CmsUser user = itUsers.next(); 12029 if (!user.getOuFqn().equals(ouFqn)) { 12030 itUsers.remove(); 12031 } 12032 } 12033 } 12034 12035 // make user list unmodifiable for caching 12036 allUsers = Collections.unmodifiableList(new ArrayList<CmsUser>(users)); 12037 if (dbc.getProjectId().isNullUUID()) { 12038 m_monitor.cacheUserList(cacheKey, allUsers); 12039 } 12040 } 12041 return allUsers; 12042 } 12043 12044 /** 12045 * Reads all resources that are inside and changed in a specified project.<p> 12046 * 12047 * @param dbc the current database context 12048 * @param projectId the ID of the project 12049 * @param mode one of the {@link CmsReadChangedProjectResourceMode} constants 12050 * 12051 * @return a List with all resources inside the specified project 12052 * 12053 * @throws CmsException if something goes wrong 12054 */ 12055 private List<CmsResource> readChangedResourcesInsideProject( 12056 CmsDbContext dbc, 12057 CmsUUID projectId, 12058 CmsReadChangedProjectResourceMode mode) 12059 throws CmsException { 12060 12061 String cacheKey = projectId + "_" + mode.toString(); 12062 List<CmsResource> result = m_monitor.getCachedProjectResources(cacheKey); 12063 if (result != null) { 12064 return result; 12065 } 12066 List<String> projectResources = readProjectResources(dbc, readProject(dbc, projectId)); 12067 result = new ArrayList<CmsResource>(); 12068 String currentProjectResource = null; 12069 List<CmsResource> resources = new ArrayList<CmsResource>(); 12070 CmsResource currentResource = null; 12071 CmsLock currentLock = null; 12072 12073 for (int i = 0; i < projectResources.size(); i++) { 12074 // read all resources that are inside the project by visiting each project resource 12075 currentProjectResource = projectResources.get(i); 12076 12077 try { 12078 currentResource = readResource(dbc, currentProjectResource, CmsResourceFilter.ALL); 12079 12080 if (currentResource.isFolder()) { 12081 resources.addAll(readResources(dbc, currentResource, CmsResourceFilter.ALL, true)); 12082 } else { 12083 resources.add(currentResource); 12084 } 12085 } catch (CmsException e) { 12086 // the project resource probably doesn't exist (anymore)... 12087 if (!(e instanceof CmsVfsResourceNotFoundException)) { 12088 throw e; 12089 } 12090 } 12091 } 12092 12093 for (int j = 0; j < resources.size(); j++) { 12094 currentResource = resources.get(j); 12095 currentLock = getLock(dbc, currentResource).getEditionLock(); 12096 12097 if (!currentResource.getState().isUnchanged()) { 12098 if ((currentLock.isNullLock() && (currentResource.getProjectLastModified().equals(projectId))) 12099 || (currentLock.isOwnedBy(dbc.currentUser()) && (currentLock.getProjectId().equals(projectId)))) { 12100 // add only resources that are 12101 // - inside the project, 12102 // - changed in the project, 12103 // - either unlocked, or locked for the current user in the project 12104 if ((mode == RCPRM_FILES_AND_FOLDERS_MODE) 12105 || (currentResource.isFolder() && (mode == RCPRM_FOLDERS_ONLY_MODE)) 12106 || (currentResource.isFile() && (mode == RCPRM_FILES_ONLY_MODE))) { 12107 result.add(currentResource); 12108 } 12109 } 12110 } 12111 } 12112 12113 resources.clear(); 12114 resources = null; 12115 12116 m_monitor.cacheProjectResources(cacheKey, result); 12117 return result; 12118 } 12119 12120 /** 12121 * Sorts the given list of {@link CmsAccessControlEntry} objects.<p> 12122 * 12123 * The the 'all others' ace in first place, the 'overwrite all' ace in second.<p> 12124 * 12125 * @param aces the list of ACEs to sort 12126 * 12127 * @return <code>true</code> if the list contains the 'overwrite all' ace 12128 */ 12129 private boolean sortAceList(List<CmsAccessControlEntry> aces) { 12130 12131 // sort the list of entries 12132 Collections.sort(aces, CmsAccessControlEntry.COMPARATOR_ACE); 12133 // after sorting just the first 2 positions come in question 12134 for (int i = 0; i < Math.min(aces.size(), 2); i++) { 12135 CmsAccessControlEntry acEntry = aces.get(i); 12136 if (acEntry.getPrincipal().equals(CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_ID)) { 12137 return true; 12138 } 12139 } 12140 return false; 12141 } 12142 12143 /** 12144 * All permissions and resources attributes of the principal 12145 * are transfered to a replacement principal.<p> 12146 * 12147 * @param dbc the current database context 12148 * @param project the current project 12149 * @param principalId the id of the principal to be replaced 12150 * @param replacementId the user to be transfered 12151 * @param withACEs flag to signal if the ACEs should also be transfered or just deleted 12152 * 12153 * @throws CmsException if operation was not successful 12154 */ 12155 private void transferPrincipalResources( 12156 CmsDbContext dbc, 12157 CmsProject project, 12158 CmsUUID principalId, 12159 CmsUUID replacementId, 12160 boolean withACEs) 12161 throws CmsException { 12162 12163 // get all resources for the given user including resources associated by ACEs or attributes 12164 I_CmsUserDriver userDriver = getUserDriver(dbc); 12165 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12166 Set<CmsResource> resources = getResourcesForPrincipal(dbc, project, principalId, null, true); 12167 Iterator<CmsResource> it = resources.iterator(); 12168 while (it.hasNext()) { 12169 CmsResource resource = it.next(); 12170 // check resource attributes 12171 boolean attrModified = false; 12172 CmsUUID createdUser = null; 12173 if (resource.getUserCreated().equals(principalId)) { 12174 createdUser = replacementId; 12175 attrModified = true; 12176 } 12177 CmsUUID lastModUser = null; 12178 if (resource.getUserLastModified().equals(principalId)) { 12179 lastModUser = replacementId; 12180 attrModified = true; 12181 } 12182 if (attrModified) { 12183 vfsDriver.transferResource(dbc, project, resource, createdUser, lastModUser); 12184 // clear the cache 12185 m_monitor.clearResourceCache(); 12186 } 12187 boolean aceModified = false; 12188 // check aces 12189 if (withACEs) { 12190 Iterator<CmsAccessControlEntry> itAces = userDriver.readAccessControlEntries( 12191 dbc, 12192 project, 12193 resource.getResourceId(), 12194 false).iterator(); 12195 while (itAces.hasNext()) { 12196 CmsAccessControlEntry ace = itAces.next(); 12197 if (ace.getPrincipal().equals(principalId)) { 12198 CmsAccessControlEntry newAce = new CmsAccessControlEntry( 12199 ace.getResource(), 12200 replacementId, 12201 ace.getAllowedPermissions(), 12202 ace.getDeniedPermissions(), 12203 ace.getFlags()); 12204 // write the new ace 12205 userDriver.writeAccessControlEntry(dbc, project, newAce); 12206 aceModified = true; 12207 } 12208 } 12209 if (aceModified) { 12210 // clear the cache 12211 m_monitor.clearAccessControlListCache(); 12212 } 12213 } 12214 if (attrModified || aceModified) { 12215 // fire the event 12216 Map<String, Object> data = new HashMap<String, Object>(2); 12217 data.put(I_CmsEventListener.KEY_RESOURCE, resource); 12218 data.put( 12219 I_CmsEventListener.KEY_CHANGE, 12220 Integer.valueOf( 12221 ((attrModified) ? CHANGED_RESOURCE : 0) | ((aceModified) ? CHANGED_ACCESSCONTROL : 0))); 12222 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_RESOURCE_MODIFIED, data)); 12223 } 12224 } 12225 } 12226 12227 /** 12228 * Undoes all content changes of a resource.<p> 12229 * 12230 * @param dbc the database context 12231 * @param onlineProject the online project 12232 * @param offlineResource the offline resource, or <code>null</code> if deleted 12233 * @param onlineResource the online resource 12234 * @param newState the new resource state 12235 * @param moveUndone is a move operation on the same resource has been made 12236 * 12237 * @throws CmsException if something goes wrong 12238 */ 12239 private void undoContentChanges( 12240 CmsDbContext dbc, 12241 CmsProject onlineProject, 12242 CmsResource offlineResource, 12243 CmsResource onlineResource, 12244 CmsResourceState newState, 12245 boolean moveUndone) 12246 throws CmsException { 12247 12248 String path = ((moveUndone || (offlineResource == null)) 12249 ? onlineResource.getRootPath() 12250 : offlineResource.getRootPath()); 12251 12252 // change folder or file? 12253 I_CmsUserDriver userDriver = getUserDriver(dbc); 12254 I_CmsVfsDriver vfsDriver = getVfsDriver(dbc); 12255 if (onlineResource.isFolder()) { 12256 CmsFolder restoredFolder = new CmsFolder( 12257 onlineResource.getStructureId(), 12258 onlineResource.getResourceId(), 12259 path, 12260 onlineResource.getTypeId(), 12261 onlineResource.getFlags(), 12262 dbc.currentProject().getUuid(), 12263 newState, 12264 onlineResource.getDateCreated(), 12265 onlineResource.getUserCreated(), 12266 onlineResource.getDateLastModified(), 12267 onlineResource.getUserLastModified(), 12268 onlineResource.getDateReleased(), 12269 onlineResource.getDateExpired(), 12270 onlineResource.getVersion()); // version number does not matter since it will be computed later 12271 12272 // write the folder in the offline project 12273 // this sets a flag so that the folder date is not set to the current time 12274 restoredFolder.setDateLastModified(onlineResource.getDateLastModified()); 12275 12276 // write the folder 12277 vfsDriver.writeResource(dbc, dbc.currentProject().getUuid(), restoredFolder, NOTHING_CHANGED); 12278 12279 // restore the properties from the online project 12280 vfsDriver.deletePropertyObjects( 12281 dbc, 12282 dbc.currentProject().getUuid(), 12283 restoredFolder, 12284 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 12285 12286 List<CmsProperty> propertyInfos = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource); 12287 vfsDriver.writePropertyObjects(dbc, dbc.currentProject(), restoredFolder, propertyInfos); 12288 12289 // restore the access control entries from the online project 12290 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId()); 12291 ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries( 12292 dbc, 12293 onlineProject, 12294 onlineResource.getResourceId(), 12295 false).listIterator(); 12296 12297 while (aceList.hasNext()) { 12298 CmsAccessControlEntry ace = aceList.next(); 12299 userDriver.createAccessControlEntry( 12300 dbc, 12301 dbc.currentProject(), 12302 onlineResource.getResourceId(), 12303 ace.getPrincipal(), 12304 ace.getPermissions().getAllowedPermissions(), 12305 ace.getPermissions().getDeniedPermissions(), 12306 ace.getFlags()); 12307 } 12308 } else { 12309 byte[] onlineContent = vfsDriver.readContent( 12310 dbc, 12311 CmsProject.ONLINE_PROJECT_ID, 12312 onlineResource.getResourceId()); 12313 12314 CmsFile restoredFile = new CmsFile( 12315 onlineResource.getStructureId(), 12316 onlineResource.getResourceId(), 12317 path, 12318 onlineResource.getTypeId(), 12319 onlineResource.getFlags(), 12320 dbc.currentProject().getUuid(), 12321 newState, 12322 onlineResource.getDateCreated(), 12323 onlineResource.getUserCreated(), 12324 onlineResource.getDateLastModified(), 12325 onlineResource.getUserLastModified(), 12326 onlineResource.getDateReleased(), 12327 onlineResource.getDateExpired(), 12328 0, 12329 onlineResource.getLength(), 12330 onlineResource.getDateContent(), 12331 onlineResource.getVersion(), // version number does not matter since it will be computed later 12332 onlineContent); 12333 12334 // write the file in the offline project 12335 // this sets a flag so that the file date is not set to the current time 12336 restoredFile.setDateLastModified(onlineResource.getDateLastModified()); 12337 12338 // collect the old properties 12339 List<CmsProperty> properties = vfsDriver.readPropertyObjects(dbc, onlineProject, onlineResource); 12340 12341 if (offlineResource != null) { 12342 // bug fix 1020: delete all properties (inclum_rejectStructureIdded shared), 12343 // shared properties will be recreated by the next call of #createResource(...) 12344 vfsDriver.deletePropertyObjects( 12345 dbc, 12346 dbc.currentProject().getUuid(), 12347 onlineResource, 12348 CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES); 12349 12350 // implementation notes: 12351 // undo changes can become complex e.g. if a resource was deleted, and then 12352 // another resource was copied over the deleted file as a sibling 12353 // therefore we must "clean" delete the offline resource, and then create 12354 // an new resource with the create method 12355 // note that this does NOT apply to folders, since a folder cannot be replaced 12356 // like a resource anyway 12357 deleteResource(dbc, offlineResource, CmsResource.DELETE_PRESERVE_SIBLINGS); 12358 } 12359 CmsResource res = createResource( 12360 dbc, 12361 restoredFile.getRootPath(), 12362 restoredFile, 12363 restoredFile.getContents(), 12364 properties, 12365 false); 12366 12367 // copy the access control entries from the online project 12368 if (offlineResource != null) { 12369 userDriver.removeAccessControlEntries(dbc, dbc.currentProject(), onlineResource.getResourceId()); 12370 } 12371 ListIterator<CmsAccessControlEntry> aceList = userDriver.readAccessControlEntries( 12372 dbc, 12373 onlineProject, 12374 onlineResource.getResourceId(), 12375 false).listIterator(); 12376 12377 while (aceList.hasNext()) { 12378 CmsAccessControlEntry ace = aceList.next(); 12379 userDriver.createAccessControlEntry( 12380 dbc, 12381 dbc.currentProject(), 12382 res.getResourceId(), 12383 ace.getPrincipal(), 12384 ace.getPermissions().getAllowedPermissions(), 12385 ace.getPermissions().getDeniedPermissions(), 12386 ace.getFlags()); 12387 } 12388 12389 vfsDriver.deleteUrlNameMappingEntries( 12390 dbc, 12391 false, 12392 CmsUrlNameMappingFilter.ALL.filterStructureId(res.getStructureId()).filterStates( 12393 CmsUrlNameMappingEntry.MAPPING_STATUS_NEW, 12394 CmsUrlNameMappingEntry.MAPPING_STATUS_REPLACE_ON_PUBLISH)); 12395 // restore the state to unchanged 12396 res.setState(newState); 12397 m_vfsDriver.writeResourceState(dbc, dbc.currentProject(), res, UPDATE_ALL, false); 12398 } 12399 12400 // delete all offline relations 12401 if (offlineResource != null) { 12402 vfsDriver.deleteRelations(dbc, dbc.currentProject().getUuid(), offlineResource, CmsRelationFilter.TARGETS); 12403 } 12404 // get online relations 12405 List<CmsRelation> relations = vfsDriver.readRelations( 12406 dbc, 12407 CmsProject.ONLINE_PROJECT_ID, 12408 onlineResource, 12409 CmsRelationFilter.TARGETS); 12410 // write offline relations 12411 Iterator<CmsRelation> itRelations = relations.iterator(); 12412 while (itRelations.hasNext()) { 12413 CmsRelation relation = itRelations.next(); 12414 vfsDriver.createRelation(dbc, dbc.currentProject().getUuid(), relation); 12415 } 12416 12417 // update the cache 12418 m_monitor.clearResourceCache(); 12419 m_monitor.flushCache(CmsMemoryMonitor.CacheType.PROPERTY, CmsMemoryMonitor.CacheType.PROPERTY_LIST); 12420 12421 if ((offlineResource == null) || offlineResource.getRootPath().equals(onlineResource.getRootPath())) { 12422 log( 12423 dbc, 12424 new CmsLogEntry( 12425 dbc, 12426 onlineResource.getStructureId(), 12427 CmsLogEntryType.RESOURCE_RESTORED, 12428 new String[] {onlineResource.getRootPath()}), 12429 false); 12430 } else { 12431 log( 12432 dbc, 12433 new CmsLogEntry( 12434 dbc, 12435 offlineResource.getStructureId(), 12436 CmsLogEntryType.RESOURCE_MOVE_RESTORED, 12437 new String[] {offlineResource.getRootPath(), onlineResource.getRootPath()}), 12438 false); 12439 } 12440 if (offlineResource != null) { 12441 OpenCms.fireCmsEvent( 12442 new CmsEvent( 12443 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 12444 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, offlineResource))); 12445 } else { 12446 OpenCms.fireCmsEvent( 12447 new CmsEvent( 12448 I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED, 12449 Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, onlineResource))); 12450 } 12451 } 12452 12453 /** 12454 * Updates the current users context dates with the given resource.<p> 12455 * 12456 * This checks the date information of the resource based on 12457 * {@link CmsResource#getDateLastModified()} as well as 12458 * {@link CmsResource#getDateReleased()} and {@link CmsResource#getDateExpired()}. 12459 * The current users request context is updated with the the "latest" dates found.<p> 12460 * 12461 * This is required in order to ensure proper setting of <code>"last-modified"</code> http headers 12462 * and also for expiration of cached elements in the Flex cache. 12463 * Consider the following use case: Page A is generated from resources x, y and z. 12464 * If either x, y or z has an expiration / release date set, then page A must expire at a certain point 12465 * in time. This is ensured by the context date check here.<p> 12466 * 12467 * @param dbc the current database context 12468 * @param resource the resource to get the date information from 12469 */ 12470 private void updateContextDates(CmsDbContext dbc, CmsResource resource) { 12471 12472 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12473 if (info != null) { 12474 info.updateFromResource(resource); 12475 } 12476 } 12477 12478 /** 12479 * Updates the current users context dates with each {@link CmsResource} object in the given list.<p> 12480 * 12481 * The given input list is returned unmodified.<p> 12482 * 12483 * Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p> 12484 * 12485 * @param dbc the current database context 12486 * @param resourceList a list of {@link CmsResource} objects 12487 * 12488 * @return the original list of CmsResources with the full resource name set 12489 */ 12490 private List<CmsResource> updateContextDates(CmsDbContext dbc, List<CmsResource> resourceList) { 12491 12492 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12493 if (info != null) { 12494 for (int i = 0; i < resourceList.size(); i++) { 12495 CmsResource resource = resourceList.get(i); 12496 info.updateFromResource(resource); 12497 } 12498 } 12499 return resourceList; 12500 } 12501 12502 /** 12503 * Returns a List of {@link CmsResource} objects generated when applying the given filter to the given list, 12504 * also updates the current users context dates with each {@link CmsResource} object in the given list, 12505 * also applies the selected resource filter to all resources in the list and returns the remaining resources.<p> 12506 * 12507 * Please see {@link #updateContextDates(CmsDbContext, CmsResource)} for an explanation of what this method does.<p> 12508 * 12509 * @param dbc the current database context 12510 * @param resourceList a list of {@link CmsResource} objects 12511 * @param filter the resource filter to use 12512 * 12513 * @return a List of {@link CmsResource} objects generated when applying the given filter to the given list 12514 */ 12515 private List<CmsResource> updateContextDates( 12516 CmsDbContext dbc, 12517 List<CmsResource> resourceList, 12518 CmsResourceFilter filter) { 12519 12520 if (CmsResourceFilter.ALL == filter) { 12521 // if there is no filter required, then use the simpler method that does not apply the filter 12522 return new ArrayList<CmsResource>(updateContextDates(dbc, resourceList)); 12523 } 12524 12525 CmsFlexRequestContextInfo info = dbc.getFlexRequestContextInfo(); 12526 List<CmsResource> result = new ArrayList<CmsResource>(resourceList.size()); 12527 for (int i = 0; i < resourceList.size(); i++) { 12528 CmsResource resource = resourceList.get(i); 12529 if (filter.isValid(dbc.getRequestContext(), resource)) { 12530 result.add(resource); 12531 } 12532 // must also include "invalid" resources for the update of context dates 12533 // since a resource may be invalid because of release / expiration date 12534 if (info != null) { 12535 info.updateFromResource(resource); 12536 } 12537 } 12538 return result; 12539 } 12540 12541 /** 12542 * Updates the state of a resource, depending on the <code>resourceState</code> parameter.<p> 12543 * 12544 * @param dbc the db context 12545 * @param resource the resource 12546 * @param resourceState if <code>true</code> the resource state will be updated, if not just the structure state. 12547 * 12548 * @throws CmsDataAccessException if something goes wrong 12549 */ 12550 private void updateState(CmsDbContext dbc, CmsResource resource, boolean resourceState) 12551 throws CmsDataAccessException { 12552 12553 CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) 12554 ? dbc.currentProject().getUuid() 12555 : dbc.getProjectId(); 12556 resource.setUserLastModified(dbc.currentUser().getId()); 12557 if (resourceState) { 12558 // update the whole resource state 12559 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_RESOURCE_STATE); 12560 } else { 12561 // update the structure state 12562 getVfsDriver(dbc).writeResource(dbc, projectId, resource, UPDATE_STRUCTURE_STATE); 12563 } 12564 } 12565 12566 /** 12567 * Wraps a driver object with a dynamic proxy that counts method calls and their durations.<p> 12568 * 12569 * @param newDriverInstance the driver instance to wrap 12570 * @return the proxy 12571 */ 12572 private Object wrapDriverInProfilingProxy(Object newDriverInstance) { 12573 12574 Class<?> cls = getDriverInterfaceForProxy(newDriverInstance); 12575 if (cls == null) { 12576 return newDriverInstance; 12577 } 12578 return Proxy.newProxyInstance( 12579 Thread.currentThread().getContextClassLoader(), 12580 new Class[] {cls}, 12581 new CmsProfilingInvocationHandler(newDriverInstance, CmsDefaultProfilingHandler.INSTANCE)); 12582 } 12583 12584}