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.file.types; 029 030import org.opencms.configuration.CmsConfigurationCopyResource; 031import org.opencms.configuration.CmsConfigurationException; 032import org.opencms.configuration.CmsParameterConfiguration; 033import org.opencms.db.CmsSecurityManager; 034import org.opencms.file.CmsFile; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsProperty; 037import org.opencms.file.CmsResource; 038import org.opencms.file.CmsResourceFilter; 039import org.opencms.file.CmsVfsException; 040import org.opencms.file.CmsVfsResourceNotFoundException; 041import org.opencms.loader.CmsLoaderException; 042import org.opencms.loader.CmsResourceManager; 043import org.opencms.lock.CmsLockType; 044import org.opencms.main.CmsException; 045import org.opencms.main.CmsIllegalArgumentException; 046import org.opencms.main.CmsLog; 047import org.opencms.main.CmsRuntimeException; 048import org.opencms.main.OpenCms; 049import org.opencms.relations.CmsLink; 050import org.opencms.relations.I_CmsLinkParseable; 051import org.opencms.staticexport.CmsLinkManager; 052import org.opencms.util.CmsFileUtil; 053import org.opencms.util.CmsMacroResolver; 054import org.opencms.util.CmsStringUtil; 055import org.opencms.xml.containerpage.CmsFormatterConfiguration; 056 057import java.util.ArrayList; 058import java.util.Collections; 059import java.util.HashMap; 060import java.util.Iterator; 061import java.util.List; 062import java.util.Map; 063 064import org.apache.commons.logging.Log; 065 066/** 067 * Base implementation for resource type classes.<p> 068 * 069 * @since 6.0.0 070 */ 071public abstract class A_CmsResourceType implements I_CmsResourceType { 072 073 /** Configuration key for optional javascript in galleries. */ 074 public static final String CONFIGURATION_GALLERY_JAVASCRIPT_PATH = "gallery.javascript.path"; 075 076 /** Configuration key for optional preview provider in galleries. */ 077 public static final String CONFIGURATION_GALLERY_PREVIEW_PROVIDER = "gallery.preview.provider"; 078 079 /** Configuration key for the optional folder class name. */ 080 public static final String CONFIGURATION_GALLERY_TYPE_NAMES = "gallery.type.names"; 081 082 /** Configuration key for the (optional) internal flag. */ 083 public static final String CONFIGURATION_INTERNAL = "resource.flag.internal"; 084 085 /** The default gallery preview provider. */ 086 public static final String DEFAULT_GALLERY_PREVIEW_PROVIDER = "org.opencms.ade.galleries.preview.CmsBinaryPreviewProvider"; 087 088 /** Macro for the folder path of the current resource. */ 089 public static final String MACRO_RESOURCE_FOLDER_PATH = "resource.folder.path"; 090 091 /** Macro for the folder path of the current resource, with touch enabled for the copied resources. */ 092 public static final String MACRO_RESOURCE_FOLDER_PATH_TOUCH = "resource.folder.path.touch"; 093 094 /** Macro for the name of the current resource. */ 095 public static final String MACRO_RESOURCE_NAME = "resource.name"; 096 097 /** Macro for the parent folder path of the current resource. */ 098 public static final String MACRO_RESOURCE_PARENT_PATH = "resource.parent.path"; 099 100 /** Macro for the root path of the current resource. */ 101 public static final String MACRO_RESOURCE_ROOT_PATH = "resource.root.path"; 102 103 /** Macro for the site path of the current resource. */ 104 public static final String MACRO_RESOURCE_SITE_PATH = "resource.site.path"; 105 106 /** The log object for this class. */ 107 private static final Log LOG = CmsLog.getLog(A_CmsResourceType.class); 108 109 /** The serial version id. */ 110 private static final long serialVersionUID = 2131071233840674874L; 111 112 /** Flag for showing that this is an additional resource type which is defined in a module. */ 113 protected boolean m_addititionalModuleResourceType; 114 115 /** The configured class name of this resource type. */ 116 protected String m_className; 117 118 /** Configuration parameters. */ 119 protected CmsParameterConfiguration m_configuration; 120 121 /** The list of resources to copy. */ 122 protected List<CmsConfigurationCopyResource> m_copyResources; 123 124 /** The list of configured default properties. */ 125 protected List<CmsProperty> m_defaultProperties; 126 127 /** Indicates that the configuration of the resource type has been frozen. */ 128 protected boolean m_frozen; 129 130 /** The gallery preview provider. */ 131 protected String m_galleryPreviewProvider; 132 133 /** Contains the file extensions mapped to this resource type. */ 134 protected List<String> m_mappings; 135 136 /** The module name if this is an additional resource type which is defined in a module. */ 137 protected String m_moduleName; 138 139 /** The configured id of this resource type. */ 140 protected int m_typeId; 141 142 /** The configured name of this resource type. */ 143 protected String m_typeName; 144 145 /** The folder for which links should be adjusted after copying the copy-resources. */ 146 private String m_adjustLinksFolder; 147 148 /** The gallery type name for this resource type. */ 149 private String m_galleryTypeNames; 150 151 /** The gallery type for this resource type. */ 152 private List<I_CmsResourceType> m_galleryTypes; 153 154 /** The optional internal parameter value. */ 155 private Boolean m_internal; 156 157 /** 158 * Default constructor, used to initialize some member variables.<p> 159 */ 160 public A_CmsResourceType() { 161 162 m_typeId = -1; 163 m_mappings = new ArrayList<String>(); 164 m_defaultProperties = new ArrayList<CmsProperty>(); 165 m_copyResources = new ArrayList<CmsConfigurationCopyResource>(); 166 m_configuration = new CmsParameterConfiguration(); 167 } 168 169 /** 170 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String) 171 */ 172 public void addConfigurationParameter(String paramName, String paramValue) { 173 174 m_configuration.add(paramName, paramValue); 175 if (CmsStringUtil.isNotEmpty(paramName) && CmsStringUtil.isNotEmpty(paramValue)) { 176 if (CONFIGURATION_INTERNAL.equalsIgnoreCase(paramName)) { 177 m_internal = Boolean.valueOf(paramValue.trim()); 178 } 179 if (CONFIGURATION_GALLERY_TYPE_NAMES.equalsIgnoreCase(paramName)) { 180 m_galleryTypeNames = paramValue.trim(); 181 } 182 } 183 } 184 185 /** 186 * Adds a new "copy resource" to this resource type, 187 * allowed only during the configuration phase.<p> 188 * 189 * The "copy resources" are copied to the specified location after 190 * a new resource of this type is created. Usually this feature is used to 191 * populate a newly created folder with some default resources.<p> 192 * 193 * If target is <code>null</code>, the macro {@link #MACRO_RESOURCE_FOLDER_PATH} is used as default. 194 * If type is <code>null</code>, the copy type {@link CmsResource#COPY_AS_NEW} is used as default.<p> 195 * 196 * @param source the source resource 197 * @param target the target resource (may contain macros) 198 * @param type the type of the copy, for example "as new", "as sibling" etc 199 * 200 * @throws CmsConfigurationException if the configuration is already frozen 201 */ 202 public void addCopyResource(String source, String target, String type) throws CmsConfigurationException { 203 204 if (LOG.isDebugEnabled()) { 205 LOG.debug( 206 Messages.get().getBundle().key( 207 Messages.LOG_ADD_COPY_RESOURCE_4, 208 new Object[] {this, source, target, type})); 209 } 210 211 if (m_frozen) { 212 // configuration already frozen 213 throw new CmsConfigurationException( 214 Messages.get().container( 215 Messages.ERR_CONFIG_FROZEN_3, 216 this.getClass().getName(), 217 getTypeName(), 218 new Integer(getTypeId()))); 219 } 220 221 // create the copy resource object an add it to the list 222 CmsConfigurationCopyResource copyResource = new CmsConfigurationCopyResource(source, target, type); 223 m_copyResources.add(copyResource); 224 } 225 226 /** 227 * Adds a default property to this resource type, 228 * allowed only during the configuration phase.<p> 229 * 230 * @param property the default property to add 231 * 232 * @throws CmsConfigurationException if the configuration is already frozen 233 */ 234 public void addDefaultProperty(CmsProperty property) throws CmsConfigurationException { 235 236 if (LOG.isDebugEnabled()) { 237 LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_DFLT_PROP_2, this, property)); 238 } 239 240 if (m_frozen) { 241 // configuration already frozen 242 throw new CmsConfigurationException( 243 Messages.get().container( 244 Messages.ERR_CONFIG_FROZEN_3, 245 this.getClass().getName(), 246 getTypeName(), 247 new Integer(getTypeId()))); 248 } 249 250 m_defaultProperties.add(property); 251 } 252 253 /** 254 * @see org.opencms.file.types.I_CmsResourceType#addMappingType(java.lang.String) 255 */ 256 public void addMappingType(String mapping) { 257 258 // this configuration does not support parameters 259 if (LOG.isDebugEnabled()) { 260 LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_MAPPING_TYPE_2, mapping, this)); 261 } 262 if (m_mappings == null) { 263 m_mappings = new ArrayList<String>(); 264 } 265 m_mappings.add(mapping); 266 } 267 268 /** 269 * @see org.opencms.file.types.I_CmsResourceType#changeLock(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource) 270 */ 271 public void changeLock(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource) 272 throws CmsException { 273 274 securityManager.changeLock(cms.getRequestContext(), resource); 275 } 276 277 /** 278 * @see org.opencms.file.types.I_CmsResourceType#chflags(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, int) 279 */ 280 public void chflags(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, int flags) 281 throws CmsException { 282 283 securityManager.chflags(cms.getRequestContext(), resource, flags); 284 } 285 286 /** 287 * @see org.opencms.file.types.I_CmsResourceType#chtype(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, org.opencms.file.types.I_CmsResourceType) 288 */ 289 public void chtype(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, I_CmsResourceType type) 290 throws CmsException { 291 292 // TODO: Refactor driver layer to use resource type id classes (or names) instead of int 293 chtype(cms, securityManager, resource, type.getTypeId()); 294 } 295 296 /** 297 * @see org.opencms.file.types.I_CmsResourceType#chtype(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, int) 298 * 299 * @deprecated 300 * Use {@link #chtype(CmsObject, CmsSecurityManager, CmsResource, I_CmsResourceType)} instead. 301 * Resource types should always be referenced either by its type class (preferred) or by type name. 302 * Use of int based resource type references will be discontinued in a future OpenCms release. 303 */ 304 @Deprecated 305 public void chtype(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, int type) 306 throws CmsException { 307 308 // change type 309 securityManager.chtype(cms.getRequestContext(), resource, type); 310 // type may have changed from non link parseable to link parseable 311 createRelations(cms, securityManager, resource.getRootPath()); 312 } 313 314 /** 315 * @see org.opencms.file.types.I_CmsResourceType#copyResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, java.lang.String, CmsResource.CmsResourceCopyMode) 316 */ 317 public void copyResource( 318 CmsObject cms, 319 CmsSecurityManager securityManager, 320 CmsResource source, 321 String destination, 322 CmsResource.CmsResourceCopyMode siblingMode) 323 throws CmsException { 324 325 securityManager.copyResource( 326 cms.getRequestContext(), 327 source, 328 cms.getRequestContext().addSiteRoot(destination), 329 siblingMode); 330 // create the relations for the new resource, this could be improved by an sql query for copying relations 331 createRelations(cms, securityManager, cms.getRequestContext().addSiteRoot(destination)); 332 } 333 334 /** 335 * @see org.opencms.file.types.I_CmsResourceType#copyResourceToProject(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource) 336 */ 337 public void copyResourceToProject(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource) 338 throws CmsException { 339 340 securityManager.copyResourceToProject(cms.getRequestContext(), resource); 341 } 342 343 /** 344 * @see org.opencms.file.types.I_CmsResourceType#createResource(org.opencms.file.CmsObject, CmsSecurityManager, java.lang.String, byte[], List) 345 */ 346 public CmsResource createResource( 347 CmsObject cms, 348 CmsSecurityManager securityManager, 349 String resourcename, 350 byte[] content, 351 List<CmsProperty> properties) 352 throws CmsException { 353 354 // initialize a macro resolver with the current user OpenCms context 355 CmsMacroResolver resolver = getMacroResolver(cms, resourcename); 356 357 // add the predefined property values from the XML configuration to the resource 358 List<CmsProperty> newProperties = processDefaultProperties(properties, resolver); 359 360 CmsResource result = securityManager.createResource( 361 cms.getRequestContext(), 362 cms.getRequestContext().addSiteRoot(resourcename), 363 getTypeId(), 364 content, 365 newProperties); 366 367 if ((m_internal != null) && m_internal.booleanValue()) { 368 securityManager.chflags(cms.getRequestContext(), result, result.getFlags() ^ CmsResource.FLAG_INTERNAL); 369 } 370 371 // process the (optional) copy resources from the configuration 372 processCopyResources(cms, resourcename, resolver); 373 374 // create the relations for the new resource 375 createRelations(cms, securityManager, cms.getRequestContext().addSiteRoot(resourcename)); 376 377 // return the created resource 378 return result; 379 } 380 381 /** 382 * @see org.opencms.file.types.I_CmsResourceType#createSibling(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, CmsResource, java.lang.String, java.util.List) 383 */ 384 public CmsResource createSibling( 385 CmsObject cms, 386 CmsSecurityManager securityManager, 387 CmsResource source, 388 String destination, 389 List<CmsProperty> properties) 390 throws CmsException { 391 392 CmsResource sibling = securityManager.createSibling( 393 cms.getRequestContext(), 394 source, 395 cms.getRequestContext().addSiteRoot(destination), 396 properties); 397 // create the relations for the new resource, this could be improved by an sql query for copying relations 398 createRelations(cms, securityManager, sibling.getRootPath()); 399 return sibling; 400 } 401 402 /** 403 * @see org.opencms.file.types.I_CmsResourceType#deleteResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceDeleteMode) 404 */ 405 public void deleteResource( 406 CmsObject cms, 407 CmsSecurityManager securityManager, 408 CmsResource resource, 409 CmsResource.CmsResourceDeleteMode siblingMode) 410 throws CmsException { 411 412 securityManager.deleteResource(cms.getRequestContext(), resource, siblingMode); 413 } 414 415 /** 416 * Returns <code>true</code>, if this resource type is equal to the given Object.<p> 417 * 418 * Please note: A resource type is identified by it's id {@link #getTypeId()} and it's name {@link #getTypeName()}. 419 * Two resource types are considered equal, if either their id or their name is equal. 420 * This is to prevent issues in the configuration with multiple occurrences of the same name or id.<p> 421 * 422 * @param obj the Object to compare this resource type with 423 * 424 * @return <code>true</code>, if this resource type is equal to the given Object 425 * 426 * @see #getTypeId() 427 * @see #getTypeName() 428 * @see #isIdentical(I_CmsResourceType) 429 * @see java.lang.Object#equals(java.lang.Object) 430 */ 431 @Override 432 public boolean equals(Object obj) { 433 434 if (obj == this) { 435 return true; 436 } 437 if (obj instanceof I_CmsResourceType) { 438 I_CmsResourceType other = (I_CmsResourceType)obj; 439 if ((getTypeName() != null) && (getTypeName().equals(other.getTypeName()))) { 440 return true; 441 } 442 if (getTypeId() == other.getTypeId()) { 443 return true; 444 } 445 } 446 return false; 447 } 448 449 /** 450 * @see org.opencms.file.types.I_CmsResourceType#getAdjustLinksFolder() 451 */ 452 public String getAdjustLinksFolder() { 453 454 return m_adjustLinksFolder; 455 } 456 457 /** 458 * @see org.opencms.file.types.I_CmsResourceType#getCachePropertyDefault() 459 */ 460 public String getCachePropertyDefault() { 461 462 return null; 463 } 464 465 /** 466 * Returns the configured class name of this resource type.<p> 467 * 468 * @see org.opencms.file.types.I_CmsResourceType#getClassName() 469 */ 470 public String getClassName() { 471 472 return m_className; 473 } 474 475 /** 476 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() 477 */ 478 public CmsParameterConfiguration getConfiguration() { 479 480 if (LOG.isDebugEnabled()) { 481 LOG.debug(Messages.get().getBundle().key(Messages.LOG_GET_CONFIGURATION_1, this)); 482 } 483 484 return m_configuration; 485 } 486 487 /** 488 * Returns the (unmodifiable) list of copy resources.<p> 489 * 490 * @return the (unmodifiable) list of copy resources 491 */ 492 public List<CmsConfigurationCopyResource> getConfiguredCopyResources() { 493 494 return m_copyResources; 495 } 496 497 /** 498 * Returns the default properties for this resource type in an unmodifiable List.<p> 499 * 500 * @return the default properties for this resource type in an unmodifiable List 501 */ 502 public List<CmsProperty> getConfiguredDefaultProperties() { 503 504 return m_defaultProperties; 505 } 506 507 /** 508 * @see org.opencms.file.types.I_CmsResourceType#getConfiguredMappings() 509 */ 510 public List<String> getConfiguredMappings() { 511 512 return m_mappings; 513 } 514 515 /** 516 * 517 * @see org.opencms.file.types.I_CmsResourceType#getFormattersForResource(org.opencms.file.CmsObject, org.opencms.file.CmsResource) 518 */ 519 public CmsFormatterConfiguration getFormattersForResource(CmsObject cms, CmsResource res) { 520 521 return CmsFormatterConfiguration.EMPTY_CONFIGURATION; 522 } 523 524 /** 525 * @see org.opencms.file.types.I_CmsResourceType#getGalleryPreviewProvider() 526 */ 527 public String getGalleryPreviewProvider() { 528 529 if (m_galleryPreviewProvider == null) { 530 m_galleryPreviewProvider = getConfiguration().getString(CONFIGURATION_GALLERY_PREVIEW_PROVIDER, null); 531 } 532 return m_galleryPreviewProvider; 533 } 534 535 /** 536 * @see org.opencms.file.types.I_CmsResourceType#getGalleryTypes() 537 */ 538 public List<I_CmsResourceType> getGalleryTypes() { 539 540 if (m_galleryTypes == null) { 541 m_galleryTypes = new ArrayList<I_CmsResourceType>(); 542 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_galleryTypeNames)) { 543 CmsResourceManager rm = OpenCms.getResourceManager(); 544 Iterator<String> iTypeNames = CmsStringUtil.splitAsList( 545 m_galleryTypeNames, 546 CmsProperty.VALUE_LIST_DELIMITER).iterator(); 547 while (iTypeNames.hasNext()) { 548 String typeName = iTypeNames.next(); 549 try { 550 m_galleryTypes.add(rm.getResourceType(typeName)); 551 } catch (CmsLoaderException e) { 552 if (LOG.isWarnEnabled()) { 553 LOG.warn(Messages.get().container(Messages.ERR_COULD_NOT_READ_RESOURCE_TYPE_1, typeName)); 554 } 555 } 556 } 557 } 558 559 } 560 return m_galleryTypes; 561 } 562 563 /** 564 * @see org.opencms.file.types.I_CmsResourceType#getLoaderId() 565 */ 566 public abstract int getLoaderId(); 567 568 /** 569 * @see org.opencms.file.types.I_CmsResourceType#getModuleName() 570 */ 571 public String getModuleName() { 572 573 return m_moduleName; 574 } 575 576 /** 577 * @see org.opencms.file.types.I_CmsResourceType#getTypeId() 578 * 579 * @deprecated 580 * Use this class or {@link #getTypeName()} instead. 581 * Resource types should always be referenced either by its type class (preferred) or by type name. 582 * Use of int based resource type references will be discontinued in a future OpenCms release. 583 */ 584 @Deprecated 585 public int getTypeId() { 586 587 return m_typeId; 588 } 589 590 /** 591 * @see org.opencms.file.types.I_CmsResourceType#getTypeName() 592 */ 593 public String getTypeName() { 594 595 return m_typeName; 596 } 597 598 /** 599 * The hash code implementation uses the type name to generate a hash code.<p> 600 * 601 * @see #getTypeId() 602 * @see java.lang.Object#hashCode() 603 */ 604 @Override 605 public int hashCode() { 606 607 return getTypeName().hashCode(); 608 } 609 610 /** 611 * @see org.opencms.file.types.I_CmsResourceType#importResource(org.opencms.file.CmsObject, CmsSecurityManager, java.lang.String, org.opencms.file.CmsResource, byte[], List) 612 */ 613 public CmsResource importResource( 614 CmsObject cms, 615 CmsSecurityManager securityManager, 616 String resourcename, 617 CmsResource resource, 618 byte[] content, 619 List<CmsProperty> properties) 620 throws CmsException { 621 622 // this triggers the internal "is touched" state 623 // and prevents the security manager from inserting the current time 624 resource.setDateLastModified(resource.getDateLastModified()); 625 // ensure resource record is updated 626 resource.setState(CmsResource.STATE_NEW); 627 return securityManager.importResource( 628 cms.getRequestContext(), 629 cms.getRequestContext().addSiteRoot(resourcename), 630 resource, 631 content, 632 properties, 633 true); 634 } 635 636 /** 637 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() 638 */ 639 public final void initConfiguration() { 640 641 // final since subclasses should NOT implement this, but rather the version with 3 String parameters (see below) 642 if (LOG.isDebugEnabled()) { 643 LOG.debug(Messages.get().getBundle().key(Messages.LOG_INIT_CONFIGURATION_1, this)); 644 } 645 } 646 647 /** 648 * @see org.opencms.file.types.I_CmsResourceType#initConfiguration(java.lang.String, java.lang.String, java.lang.String) 649 */ 650 public void initConfiguration(String name, String id, String className) throws CmsConfigurationException { 651 652 if (LOG.isDebugEnabled()) { 653 LOG.debug(Messages.get().getBundle().key(Messages.LOG_INIT_CONFIGURATION_3, this, name, id)); 654 } 655 656 if (m_frozen) { 657 // configuration already frozen 658 throw new CmsConfigurationException( 659 org.opencms.configuration.Messages.get().container( 660 org.opencms.file.types.Messages.ERR_CONFIG_FROZEN_3, 661 className, 662 getTypeName(), 663 new Integer(getTypeId()))); 664 } 665 666 // freeze the configuration 667 m_frozen = true; 668 669 // set type name and id (please note that some resource types have a fixed type / id) 670 if (name != null) { 671 m_typeName = name; 672 } 673 if (id != null) { 674 m_typeId = Integer.valueOf(id).intValue(); 675 } 676 if (className != null) { 677 m_className = className; 678 } 679 680 // check type id, type name and class name 681 if ((getTypeName() == null) 682 || (getClassName() == null) 683 || ((getTypeId() < 0) 684 && (!m_typeName.equals(CmsResourceTypeUnknownFile.getStaticTypeName())) 685 && (!m_typeName.equals(CmsResourceTypeUnknownFolder.getStaticTypeName())))) { 686 throw new CmsConfigurationException( 687 Messages.get().container( 688 Messages.ERR_INVALID_RESTYPE_CONFIG_3, 689 className, 690 m_typeName, 691 new Integer(m_typeId))); 692 } 693 694 m_defaultProperties = Collections.unmodifiableList(m_defaultProperties); 695 m_copyResources = Collections.unmodifiableList(m_copyResources); 696 m_mappings = Collections.unmodifiableList(m_mappings); 697 m_configuration = CmsParameterConfiguration.unmodifiableVersion(m_configuration); 698 } 699 700 /** 701 * @see org.opencms.file.types.I_CmsResourceType#initialize(org.opencms.file.CmsObject) 702 */ 703 public void initialize(CmsObject cms) { 704 705 // most resource type do not require any runtime information 706 if (LOG.isDebugEnabled()) { 707 LOG.debug(Messages.get().getBundle().key(Messages.LOG_INITIALIZE_1, this)); 708 } 709 } 710 711 /** 712 * @see org.opencms.file.types.I_CmsResourceType#isAdditionalModuleResourceType() 713 */ 714 public boolean isAdditionalModuleResourceType() { 715 716 return m_addititionalModuleResourceType; 717 } 718 719 /** 720 * @see org.opencms.file.types.I_CmsResourceType#isDirectEditable() 721 */ 722 public boolean isDirectEditable() { 723 724 return false; 725 } 726 727 /** 728 * @see org.opencms.file.types.I_CmsResourceType#isFolder() 729 */ 730 public boolean isFolder() { 731 732 return false; 733 } 734 735 /** 736 * @see org.opencms.file.types.I_CmsResourceType#isIdentical(org.opencms.file.types.I_CmsResourceType) 737 */ 738 public boolean isIdentical(I_CmsResourceType type) { 739 740 if (type == null) { 741 return false; 742 } 743 744 return (getTypeId() == type.getTypeId()) && (getTypeName().equals(type.getTypeName())); 745 } 746 747 /** 748 * @see org.opencms.file.types.I_CmsResourceType#lockResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, org.opencms.lock.CmsLockType) 749 */ 750 public void lockResource(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, CmsLockType type) 751 throws CmsException { 752 753 securityManager.lockResource(cms.getRequestContext(), resource, type); 754 } 755 756 /** 757 * @see org.opencms.file.types.I_CmsResourceType#moveResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, java.lang.String) 758 */ 759 public void moveResource( 760 CmsObject cms, 761 CmsSecurityManager securityManager, 762 CmsResource resource, 763 String destination) 764 throws CmsException, CmsIllegalArgumentException { 765 766 String dest = cms.getRequestContext().addSiteRoot(destination); 767 if (resource.getRootPath().equals(dest)) { 768 // move to target with same name is not allowed 769 throw new CmsVfsException( 770 org.opencms.file.Messages.get().container(org.opencms.file.Messages.ERR_MOVE_SAME_NAME_1, destination)); 771 } 772 // check the destination 773 try { 774 securityManager.readResource(cms.getRequestContext(), dest, CmsResourceFilter.ALL); 775 throw new CmsVfsException( 776 org.opencms.file.Messages.get().container( 777 org.opencms.file.Messages.ERR_OVERWRITE_RESOURCE_2, 778 cms.getRequestContext().removeSiteRoot(resource.getRootPath()), 779 destination)); 780 } catch (CmsVfsResourceNotFoundException e) { 781 // ok 782 } 783 String targetName = CmsResource.getName(destination).replace("/", ""); 784 CmsResource.checkResourceName(targetName); 785 // move 786 securityManager.moveResource(cms.getRequestContext(), resource, dest); 787 } 788 789 /** 790 * @see org.opencms.file.types.I_CmsResourceType#removeResourceFromProject(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource) 791 */ 792 public void removeResourceFromProject(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource) 793 throws CmsException { 794 795 securityManager.removeResourceFromProject(cms.getRequestContext(), resource); 796 } 797 798 /** 799 * @see org.opencms.file.types.I_CmsResourceType#replaceResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, org.opencms.file.types.I_CmsResourceType, byte[], java.util.List) 800 */ 801 public void replaceResource( 802 CmsObject cms, 803 CmsSecurityManager securityManager, 804 CmsResource resource, 805 I_CmsResourceType type, 806 byte[] content, 807 List<CmsProperty> properties) 808 throws CmsException { 809 810 // TODO: Refactor driver layer to use resource type id classes (or names) instead of int 811 replaceResource(cms, securityManager, resource, type.getTypeId(), content, properties); 812 } 813 814 /** 815 * @see org.opencms.file.types.I_CmsResourceType#replaceResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, int, byte[], List) 816 * 817 * @deprecated 818 * Use {@link #replaceResource(CmsObject, CmsSecurityManager, CmsResource, I_CmsResourceType, byte[], List)} instead. 819 * Resource types should always be referenced either by its type class (preferred) or by type name. 820 * Use of int based resource type references will be discontinued in a future OpenCms release. 821 */ 822 @Deprecated 823 public void replaceResource( 824 CmsObject cms, 825 CmsSecurityManager securityManager, 826 CmsResource resource, 827 int type, 828 byte[] content, 829 List<CmsProperty> properties) 830 throws CmsException { 831 832 securityManager.replaceResource(cms.getRequestContext(), resource, type, content, properties); 833 // type may have changed from non link parseable to link parseable 834 createRelations(cms, securityManager, resource.getRootPath()); 835 } 836 837 /** 838 * @see org.opencms.file.types.I_CmsResourceType#restoreResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, int) 839 */ 840 public void restoreResource(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, int version) 841 throws CmsException { 842 843 securityManager.restoreResource(cms.getRequestContext(), resource, version); 844 // type may have changed from non link parseable to link parseable 845 createRelations(cms, securityManager, resource.getRootPath()); 846 } 847 848 /** 849 * @see org.opencms.file.types.I_CmsResourceType#setAdditionalModuleResourceType(boolean) 850 */ 851 public void setAdditionalModuleResourceType(boolean additionalType) { 852 853 m_addititionalModuleResourceType = additionalType; 854 } 855 856 /** 857 * @see org.opencms.file.types.I_CmsResourceType#setAdjustLinksFolder(String) 858 */ 859 public void setAdjustLinksFolder(String adjustLinksFolder) { 860 861 m_adjustLinksFolder = adjustLinksFolder; 862 } 863 864 /** 865 * @see org.opencms.file.types.I_CmsResourceType#setDateExpired(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, long, boolean) 866 */ 867 public void setDateExpired( 868 CmsObject cms, 869 CmsSecurityManager securityManager, 870 CmsResource resource, 871 long dateExpired, 872 boolean recursive) 873 throws CmsException { 874 875 securityManager.setDateExpired(cms.getRequestContext(), resource, dateExpired); 876 } 877 878 /** 879 * @see org.opencms.file.types.I_CmsResourceType#setDateLastModified(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, long, boolean) 880 */ 881 public void setDateLastModified( 882 CmsObject cms, 883 CmsSecurityManager securityManager, 884 CmsResource resource, 885 long dateLastModified, 886 boolean recursive) 887 throws CmsException { 888 889 securityManager.setDateLastModified(cms.getRequestContext(), resource, dateLastModified); 890 } 891 892 /** 893 * @see org.opencms.file.types.I_CmsResourceType#setDateReleased(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, long, boolean) 894 */ 895 public void setDateReleased( 896 CmsObject cms, 897 CmsSecurityManager securityManager, 898 CmsResource resource, 899 long dateReleased, 900 boolean recursive) 901 throws CmsException { 902 903 securityManager.setDateReleased(cms.getRequestContext(), resource, dateReleased); 904 } 905 906 /** 907 * @see org.opencms.file.types.I_CmsResourceType#setModuleName(java.lang.String) 908 */ 909 public void setModuleName(String moduleName) { 910 911 m_moduleName = moduleName; 912 } 913 914 /** 915 * @see java.lang.Object#toString() 916 */ 917 @Override 918 public String toString() { 919 920 StringBuffer output = new StringBuffer(); 921 output.append("[ResourceType] class="); 922 output.append(getClass().getName()); 923 output.append(" name="); 924 output.append(getTypeName()); 925 output.append(" id="); 926 output.append(getTypeId()); 927 output.append(" loaderId="); 928 output.append(getLoaderId()); 929 return output.toString(); 930 } 931 932 /** 933 * @see org.opencms.file.types.I_CmsResourceType#undelete(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, boolean) 934 */ 935 public void undelete(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, boolean recursive) 936 throws CmsException { 937 938 securityManager.undelete(cms.getRequestContext(), resource); 939 } 940 941 /** 942 * @see org.opencms.file.types.I_CmsResourceType#undoChanges(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode) 943 */ 944 public void undoChanges( 945 CmsObject cms, 946 CmsSecurityManager securityManager, 947 CmsResource resource, 948 CmsResource.CmsResourceUndoMode mode) 949 throws CmsException { 950 951 securityManager.undoChanges(cms.getRequestContext(), resource, mode); 952 updateRelationForUndo(cms, securityManager, resource); 953 } 954 955 /** 956 * @see org.opencms.file.types.I_CmsResourceType#unlockResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource) 957 */ 958 public void unlockResource(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource) 959 throws CmsException { 960 961 securityManager.unlockResource(cms.getRequestContext(), resource); 962 } 963 964 /** 965 * @see org.opencms.file.types.I_CmsResourceType#writeFile(org.opencms.file.CmsObject, CmsSecurityManager, CmsFile) 966 */ 967 public CmsFile writeFile(CmsObject cms, CmsSecurityManager securityManager, CmsFile resource) throws CmsException { 968 969 if (resource.isFile()) { 970 CmsFile file = securityManager.writeFile(cms.getRequestContext(), resource); 971 I_CmsResourceType type = getResourceType(file); 972 // update the relations after writing!! 973 List<CmsLink> links = null; 974 if (type instanceof I_CmsLinkParseable) { // this check is needed because of type change 975 // if the new type is link parseable 976 links = ((I_CmsLinkParseable)type).parseLinks(cms, file); 977 } 978 // this has to be always executed, even if not link parseable to remove old links 979 securityManager.updateRelationsForResource(cms.getRequestContext(), file, links); 980 return file; 981 } 982 // folders can never be written like a file 983 throw new CmsVfsException( 984 Messages.get().container(Messages.ERR_WRITE_FILE_IS_FOLDER_1, cms.getSitePath(resource))); 985 } 986 987 /** 988 * @see org.opencms.file.types.I_CmsResourceType#writePropertyObject(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, CmsResource, org.opencms.file.CmsProperty) 989 */ 990 public void writePropertyObject( 991 CmsObject cms, 992 CmsSecurityManager securityManager, 993 CmsResource resource, 994 CmsProperty property) 995 throws CmsException { 996 997 securityManager.writePropertyObject(cms.getRequestContext(), resource, property); 998 } 999 1000 /** 1001 * @see org.opencms.file.types.I_CmsResourceType#writePropertyObjects(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, CmsResource, java.util.List) 1002 */ 1003 public void writePropertyObjects( 1004 CmsObject cms, 1005 CmsSecurityManager securityManager, 1006 CmsResource resource, 1007 List<CmsProperty> properties) 1008 throws CmsException { 1009 1010 securityManager.writePropertyObjects(cms.getRequestContext(), resource, properties); 1011 } 1012 1013 /** 1014 * Creates the relation information for the resource with the given resource name.<p> 1015 * 1016 * @param cms the cms context 1017 * @param securityManager the security manager 1018 * @param resourceName the resource name of the resource to update the relations for 1019 * 1020 * @return the fresh read resource 1021 * 1022 * @throws CmsException if something goes wrong 1023 */ 1024 protected CmsResource createRelations(CmsObject cms, CmsSecurityManager securityManager, String resourceName) 1025 throws CmsException { 1026 1027 CmsResource resource = securityManager.readResource( 1028 cms.getRequestContext(), 1029 resourceName, 1030 CmsResourceFilter.ALL); 1031 I_CmsResourceType resourceType = getResourceType(resource); 1032 List<CmsLink> links = null; 1033 if (resourceType instanceof I_CmsLinkParseable) { 1034 I_CmsLinkParseable linkParseable = (I_CmsLinkParseable)resourceType; 1035 links = linkParseable.parseLinks(cms, cms.readFile(resource)); 1036 } 1037 securityManager.updateRelationsForResource(cms.getRequestContext(), resource, links); 1038 return resource; 1039 } 1040 1041 /** 1042 * Creates a macro resolver based on the current users OpenCms context and the provided resource name.<p> 1043 * 1044 * @param cms the current OpenCms user context 1045 * @param resourcename the resource name for macros like {@link A_CmsResourceType#MACRO_RESOURCE_FOLDER_PATH} 1046 * 1047 * @return a macro resolver based on the current users OpenCms context and the provided resource name 1048 */ 1049 protected CmsMacroResolver getMacroResolver(CmsObject cms, String resourcename) { 1050 1051 CmsMacroResolver result = CmsMacroResolver.newInstance().setCmsObject(cms); 1052 if (isFolder() && (!CmsResource.isFolder(resourcename))) { 1053 // ensure folder ends with "/" so 1054 resourcename = resourcename.concat("/"); 1055 } 1056 // add special mappings for macros in default properties 1057 result.addMacro(MACRO_RESOURCE_ROOT_PATH, cms.getRequestContext().addSiteRoot(resourcename)); 1058 result.addMacro(MACRO_RESOURCE_SITE_PATH, resourcename); 1059 result.addMacro(MACRO_RESOURCE_FOLDER_PATH, CmsResource.getFolderPath(resourcename)); 1060 result.addMacro(MACRO_RESOURCE_FOLDER_PATH_TOUCH, CmsResource.getFolderPath(resourcename)); 1061 result.addMacro(MACRO_RESOURCE_PARENT_PATH, CmsResource.getParentFolder(resourcename)); 1062 result.addMacro(MACRO_RESOURCE_NAME, CmsResource.getName(resourcename)); 1063 1064 return result; 1065 } 1066 1067 /** 1068 * Convenience method to get the initialized resource type instance for the given resource, 1069 * with a fall back to special "unknown" resource types in case the resource type is not configured.<p> 1070 * 1071 * @param resource the resource to get the type for 1072 * 1073 * @return the initialized resource type instance for the given resource 1074 * 1075 * @see org.opencms.loader.CmsResourceManager#getResourceType(int) 1076 */ 1077 protected I_CmsResourceType getResourceType(CmsResource resource) { 1078 1079 return OpenCms.getResourceManager().getResourceType(resource); 1080 } 1081 1082 /** 1083 * Processes the copy resources of this resource type.<p> 1084 * 1085 * @param cms the current OpenCms user context 1086 * @param resourcename the name of the base resource 1087 * @param resolver the resolver used for resolving target macro names 1088 */ 1089 protected void processCopyResources(CmsObject cms, String resourcename, CmsMacroResolver resolver) { 1090 1091 Map<String, String> copiedResources = new HashMap<String, String>(); 1092 for (CmsConfigurationCopyResource oriCopyResource : m_copyResources) { 1093 1094 // store original copy target 1095 String oriTarget = oriCopyResource.getTarget(); 1096 String target = oriTarget; 1097 1098 List<CmsConfigurationCopyResource> copyResources = new ArrayList<CmsConfigurationCopyResource>(); 1099 try { 1100 // determine if source definition has a wild card character at the end 1101 if (oriCopyResource.getSource().endsWith("*")) { 1102 // add all sub resources of the specified source folder to the set of resources to copy 1103 String source = oriCopyResource.getSource().substring(0, oriCopyResource.getSource().length() - 1); 1104 List<CmsResource> sources = cms.readResources(source, CmsResourceFilter.IGNORE_EXPIRATION, false); 1105 for (CmsResource sourceRes : sources) { 1106 copyResources.add( 1107 new CmsConfigurationCopyResource( 1108 cms.getSitePath(sourceRes), 1109 oriCopyResource.getTarget(), 1110 oriCopyResource.getTypeString())); 1111 } 1112 copiedResources.put(source, resolver.resolveMacros(target)); 1113 } else { 1114 // just add the single specified source 1115 copyResources.add(oriCopyResource); 1116 } 1117 1118 // loop the calculated resources to copy 1119 for (CmsConfigurationCopyResource copyResource : copyResources) { 1120 1121 target = copyResource.getTarget(); 1122 if (copyResource.isTargetWasNull() 1123 || CmsMacroResolver.isMacro(target, MACRO_RESOURCE_FOLDER_PATH) 1124 || CmsMacroResolver.isMacro(target, MACRO_RESOURCE_FOLDER_PATH_TOUCH)) { 1125 // target is just the resource folder, must add source file name to target 1126 target = target.concat(CmsResource.getName(copyResource.getSource())); 1127 } 1128 // now resolve the macros in the target name 1129 target = resolver.resolveMacros(target); 1130 // now resolve possible relative paths in the target 1131 target = CmsFileUtil.normalizePath(CmsLinkManager.getAbsoluteUri(target, resourcename), '/'); 1132 1133 // copy the resource 1134 cms.copyResource(copyResource.getSource(), target, copyResource.getType()); 1135 copiedResources.put(copyResource.getSource(), target); 1136 if (CmsMacroResolver.isMacro(oriTarget, MACRO_RESOURCE_FOLDER_PATH_TOUCH)) { 1137 // copied resources should be touched in order to be able to do additional stuff 1138 CmsResource res = cms.readResource(target); 1139 if (res.isFile()) { 1140 // single file, just rewrite it 1141 CmsFile file = cms.readFile(res); 1142 cms.writeFile(file); 1143 } else { 1144 // folder, get all sub resources that are files 1145 Iterator<CmsResource> it = cms.readResources( 1146 target, 1147 CmsResourceFilter.DEFAULT_FILES, 1148 true).iterator(); 1149 while (it.hasNext()) { 1150 // rewrite the sub resource 1151 CmsResource subRes = it.next(); 1152 CmsFile file = cms.readFile(subRes); 1153 cms.writeFile(file); 1154 } 1155 } 1156 } 1157 1158 } 1159 } catch (Exception e) { 1160 // CmsIllegalArgumentException as well as CmsException 1161 // log the error and continue with the other copy resources 1162 if (LOG.isDebugEnabled()) { 1163 // log stack trace in debug level only 1164 LOG.debug( 1165 Messages.get().getBundle().key( 1166 Messages.LOG_PROCESS_COPY_RESOURCES_3, 1167 resourcename, 1168 oriCopyResource, 1169 target), 1170 e); 1171 } else { 1172 LOG.error( 1173 Messages.get().getBundle().key( 1174 Messages.LOG_PROCESS_COPY_RESOURCES_3, 1175 resourcename, 1176 oriCopyResource, 1177 target)); 1178 } 1179 } 1180 } 1181 // only adjust links for successfully copied resources and if the feature is enabled 1182 try { 1183 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_adjustLinksFolder) && !copiedResources.isEmpty()) { 1184 String realAdjustFolderPath = resolver.resolveMacros(m_adjustLinksFolder); 1185 cms.adjustLinks(copiedResources, realAdjustFolderPath); 1186 } 1187 } catch (CmsException e) { 1188 LOG.error(e.getLocalizedMessage(), e); 1189 } catch (CmsIllegalArgumentException e) { 1190 LOG.error(e.getLocalizedMessage(), e); 1191 } 1192 } 1193 1194 /** 1195 * Returns a list of property objects that are attached to the resource on creation.<p> 1196 * 1197 * It's possible to use OpenCms macros for the property values. 1198 * Please see {@link CmsMacroResolver} for allowed macro values.<p> 1199 * 1200 * @param properties the (optional) properties provided by the user 1201 * @param resolver the resolver used to resolve the macro values 1202 * 1203 * @return a list of property objects that are attached to the resource on creation 1204 */ 1205 protected List<CmsProperty> processDefaultProperties(List<CmsProperty> properties, CmsMacroResolver resolver) { 1206 1207 if ((m_defaultProperties == null) || (m_defaultProperties.size() == 0)) { 1208 // no default properties are defined 1209 return properties; 1210 } 1211 1212 // the properties must be copied since the macros could contain macros that are 1213 // resolved differently for every user / context 1214 ArrayList<CmsProperty> result = new ArrayList<CmsProperty>(); 1215 Iterator<CmsProperty> i = m_defaultProperties.iterator(); 1216 1217 while (i.hasNext()) { 1218 // create a clone of the next property 1219 CmsProperty property = (i.next()).clone(); 1220 1221 // resolve possible macros in the property values 1222 if (property.getResourceValue() != null) { 1223 property.setResourceValue(resolver.resolveMacros(property.getResourceValue())); 1224 } 1225 if (property.getStructureValue() != null) { 1226 property.setStructureValue(resolver.resolveMacros(property.getStructureValue())); 1227 } 1228 1229 // save the new property in the result list 1230 result.add(property); 1231 } 1232 1233 // add the original properties 1234 if (properties != null) { 1235 result.addAll(properties); 1236 } 1237 1238 // return the result 1239 return result; 1240 } 1241 1242 /** 1243 * Update the relations after an undo changes operation.<p> 1244 * 1245 * @param cms the cms context 1246 * @param securityManager the security manager 1247 * @param resource the resource that has been undone 1248 * 1249 * @throws CmsException if something goes wrong 1250 */ 1251 protected void updateRelationForUndo(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource) 1252 throws CmsException { 1253 1254 // type may have changed from non link parseable to link parseable 1255 CmsResource undoneResource1 = null; 1256 try { 1257 // first try to locate the resource by path 1258 undoneResource1 = createRelations(cms, securityManager, resource.getRootPath()); 1259 } catch (CmsVfsResourceNotFoundException e) { 1260 // ignore, undone move operation 1261 } 1262 // now, in case a move operation has been undone, locate the resource by id 1263 CmsResource undoneResource2 = securityManager.readResource( 1264 cms.getRequestContext(), 1265 resource.getStructureId(), 1266 CmsResourceFilter.ALL); 1267 if (!undoneResource2.equals(undoneResource1)) { 1268 I_CmsResourceType resourceType = getResourceType(resource); 1269 List<CmsLink> links = null; 1270 if (resourceType instanceof I_CmsLinkParseable) { 1271 I_CmsLinkParseable linkParseable = (I_CmsLinkParseable)resourceType; 1272 if ((undoneResource1 == null) || !undoneResource2.getRootPath().equals(undoneResource1.getRootPath())) { 1273 try { 1274 links = linkParseable.parseLinks(cms, cms.readFile(undoneResource2)); 1275 } catch (CmsException e) { 1276 if (LOG.isWarnEnabled()) { 1277 LOG.warn(e); 1278 } 1279 } catch (CmsRuntimeException e) { 1280 if (LOG.isWarnEnabled()) { 1281 LOG.warn(e); 1282 } 1283 } 1284 } 1285 } 1286 securityManager.updateRelationsForResource(cms.getRequestContext(), undoneResource2, links); 1287 } 1288 } 1289}