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