001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.contenteditor.shared; 029 030import org.opencms.acacia.shared.CmsAttributeConfiguration; 031import org.opencms.acacia.shared.CmsEntity; 032import org.opencms.acacia.shared.CmsEntityAttribute; 033import org.opencms.acacia.shared.CmsTabInfo; 034import org.opencms.acacia.shared.CmsType; 035import org.opencms.gwt.shared.CmsModelResourceInfo; 036import org.opencms.util.CmsUUID; 037 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.List; 041import java.util.Map; 042import java.util.Set; 043 044/** 045 * Contains all information needed for editing an XMLContent.<p> 046 */ 047public class CmsContentDefinition extends org.opencms.acacia.shared.CmsContentDefinition { 048 049 /** The id of the element settings tab. */ 050 public static final String SETTINGS_TAB_ID = "###formattersettings###"; 051 052 /** The entity id prefix. */ 053 private static final String ENTITY_ID_PREFIX = "http://opencms.org/resources/"; 054 055 /** The value of the acacia-unlock configuration option. */ 056 private boolean m_autoUnlock; 057 058 /** The available locales. */ 059 private Map<String, String> m_availableLocales; 060 061 /** A map from attribute names to complex widget configurations. */ 062 private Map<String, CmsComplexWidgetData> m_complexWidgetData; 063 064 /** The content locales. */ 065 private List<String> m_contentLocales; 066 067 /** Flag indicating the resource needs to removed on cancel. */ 068 private boolean m_deleteOnCancel; 069 070 /** Indicates that editor change handlers are configured. */ 071 private Set<String> m_editorChangeScopes; 072 073 /** The external widget configurations. */ 074 private List<CmsExternalWidgetConfiguration> m_externalWidgetConfigurations; 075 076 /** The resource icon classes. */ 077 private String m_iconClasses; 078 079 /** The direct edit flag (set to true for classic direct edit mode). */ 080 private boolean m_isDirectEdit; 081 082 /** The model file informations. */ 083 private List<CmsModelResourceInfo> m_modelInfos; 084 085 /** The new link. */ 086 private String m_newLink; 087 088 /** Flag indicating the current content has an invalid XML structure and was auto corrected. */ 089 private boolean m_performedAutocorrection; 090 091 /** The reference resource structure id. */ 092 private CmsUUID m_referenceResourceId; 093 094 /** The resource type name. */ 095 private String m_resourceType; 096 097 /** True if the element should be marked as 'reused' in the content editor. */ 098 private boolean m_reusedElement; 099 100 /** The site path. */ 101 private String m_sitePath; 102 103 /** The paths to skip during locale synchronization. */ 104 private Collection<String> m_skipPaths; 105 106 /** The elements that require a synchronization across all locales. */ 107 private List<String> m_synchronizations; 108 109 /** The locale synchronization values. */ 110 private Map<String, String> m_syncValues; 111 112 /** Is there an active content augmentation? */ 113 private boolean m_isTranslationEnabled; 114 115 /** The content title. */ 116 private String m_title; 117 118 /** 119 * Constructor for model file informations object.<p> 120 * 121 * @param modelInfos the model file informations 122 * @param newLink the new link 123 * @param referenceId the reference resource structure id 124 * @param locale the locale 125 */ 126 public CmsContentDefinition( 127 List<CmsModelResourceInfo> modelInfos, 128 String newLink, 129 CmsUUID referenceId, 130 String locale) { 131 132 super(null, null, null, null, null, true, locale); 133 m_modelInfos = modelInfos; 134 m_newLink = newLink; 135 m_referenceResourceId = referenceId; 136 } 137 138 /** 139 * Constructor.<p> 140 * 141 * @param entityId the entity id 142 * @param entities the locale specific entities of the content 143 * @param configurations the attribute configurations 144 * @param externalWidgetConfigurations the external widget configurations 145 * @param complexWidgetData the complex widget configurations 146 * @param types the types 147 * @param tabInfos the tab information 148 * @param locale the content locale 149 * @param contentLocales the content locales 150 * @param availableLocales the available locales 151 * @param synchronizations the elements that require a synchronization across all locales 152 * @param syncValues the locale synchronization values 153 * @param skipPaths the paths to skip during locale synchronization 154 * @param title the content title 155 * @param sitePath the site path 156 * @param resourceType the resource type name 157 * @param iconClasses the resource icon classes 158 * @param performedAutocorrection flag indicating the current content has an invalid XML structure and was auto corrected 159 * @param autoUnlock false if the editor should not unlock resources automatically in standalone mode 160 * @param editorChangeScopes the editor change handler scopes 161 */ 162 public CmsContentDefinition( 163 String entityId, 164 Map<String, CmsEntity> entities, 165 Map<String, CmsAttributeConfiguration> configurations, 166 Collection<CmsExternalWidgetConfiguration> externalWidgetConfigurations, 167 Map<String, CmsComplexWidgetData> complexWidgetData, 168 Map<String, CmsType> types, 169 List<CmsTabInfo> tabInfos, 170 String locale, 171 List<String> contentLocales, 172 Map<String, String> availableLocales, 173 List<String> synchronizations, 174 Map<String, String> syncValues, 175 Collection<String> skipPaths, 176 String title, 177 String sitePath, 178 String resourceType, 179 String iconClasses, 180 boolean performedAutocorrection, 181 boolean autoUnlock, 182 Set<String> editorChangeScopes) { 183 184 super(entityId, entities, configurations, types, tabInfos, true, locale); 185 m_contentLocales = contentLocales; 186 m_availableLocales = availableLocales; 187 m_synchronizations = synchronizations; 188 m_syncValues = syncValues; 189 m_skipPaths = skipPaths; 190 m_complexWidgetData = complexWidgetData; 191 m_title = title; 192 m_sitePath = sitePath; 193 m_resourceType = resourceType; 194 m_iconClasses = iconClasses; 195 m_externalWidgetConfigurations = new ArrayList<CmsExternalWidgetConfiguration>(externalWidgetConfigurations); 196 m_performedAutocorrection = performedAutocorrection; 197 m_autoUnlock = autoUnlock; 198 m_editorChangeScopes = editorChangeScopes; 199 } 200 201 /** 202 * Constructor for serialization only.<p> 203 */ 204 protected CmsContentDefinition() { 205 206 super(); 207 } 208 209 /** 210 * Returns the UUID according to the given entity id.<p> 211 * 212 * @param entityId the entity id 213 * 214 * @return the entity id 215 */ 216 public static CmsUUID entityIdToUuid(String entityId) { 217 218 if (entityId.startsWith(ENTITY_ID_PREFIX)) { 219 entityId = entityId.substring(entityId.lastIndexOf("/") + 1); 220 } 221 return new CmsUUID(entityId); 222 } 223 224 /** 225 * Extracts the locale from the entity id.<p> 226 * 227 * @param entityId the entity id 228 * 229 * @return the locale 230 */ 231 public static String getLocaleFromId(String entityId) { 232 233 if (entityId.startsWith(ENTITY_ID_PREFIX)) { 234 return entityId.substring(ENTITY_ID_PREFIX.length(), entityId.lastIndexOf("/")); 235 } 236 return null; 237 } 238 239 /** 240 * Returns the value for the given XPath expression.<p> 241 * 242 * @param entity the entity 243 * @param path the path 244 * 245 * @return the value 246 */ 247 public static String getValueForPath(CmsEntity entity, String path) { 248 249 String result = null; 250 if (path.startsWith("/")) { 251 path = path.substring(1); 252 } 253 String attributeName; 254 if (path.contains("/")) { 255 attributeName = path.substring(0, path.indexOf("/")); 256 path = path.substring(path.indexOf("/")); 257 } else { 258 attributeName = path; 259 path = null; 260 } 261 int index = org.opencms.acacia.shared.CmsContentDefinition.extractIndex(attributeName); 262 if (index > 0) { 263 index--; 264 265 } 266 String typeName = entity.getTypeName(); 267 268 attributeName = typeName + "/" + org.opencms.acacia.shared.CmsContentDefinition.removeIndex(attributeName); 269 CmsEntityAttribute choiceAttr = entity.getAttribute(CmsType.CHOICE_ATTRIBUTE_NAME); 270 if ((choiceAttr != null) && choiceAttr.isComplexValue()) { 271 List<CmsEntity> choiceChildren = new ArrayList<>(); 272 for (CmsEntity child : choiceAttr.getComplexValues()) { 273 if (child.getAttribute(attributeName) != null) { 274 choiceChildren.add(child); 275 } 276 } 277 if (index < choiceChildren.size()) { 278 entity = choiceChildren.get(index); 279 index = 0; 280 } else { 281 return null; 282 } 283 } 284 285 CmsEntityAttribute attribute = entity.getAttribute(attributeName); 286 if (!((attribute == null) || (attribute.isComplexValue() && (path == null)))) { 287 if (attribute.isSimpleValue()) { 288 if ((path == null) && (attribute.getValueCount() > 0)) { 289 List<String> values = attribute.getSimpleValues(); 290 result = values.get(index); 291 } 292 } else if (attribute.getValueCount() > (index)) { 293 List<CmsEntity> values = attribute.getComplexValues(); 294 result = getValueForPath(values.get(index), path); 295 } 296 } 297 return result; 298 } 299 300 /** 301 * Transfers values from the original entity to the given target entity.<p> 302 * 303 * @param original the original entity 304 * @param target the target entity 305 * @param transferAttributes the attributes to consider for the value transfer 306 * @param entityTypes the entity types 307 * @param attributeConfigurations the attribute configurations 308 * @param considerDefaults if default values should be added according to minimum occurrence settings 309 */ 310 public static void transferValues( 311 CmsEntity original, 312 CmsEntity target, 313 List<String> transferAttributes, 314 Map<String, CmsType> entityTypes, 315 Map<String, CmsAttributeConfiguration> attributeConfigurations, 316 boolean considerDefaults) { 317 318 CmsType entityType = entityTypes.get(target.getTypeName()); 319 for (String attributeName : entityType.getAttributeNames()) { 320 CmsType attributeType = entityTypes.get(entityType.getAttributeTypeName(attributeName)); 321 if (transferAttributes.contains(attributeName)) { 322 323 target.removeAttribute(attributeName); 324 CmsEntityAttribute attribute = original != null ? original.getAttribute(attributeName) : null; 325 if (attribute != null) { 326 if (attributeType.isSimpleType()) { 327 for (String value : attribute.getSimpleValues()) { 328 target.addAttributeValue(attributeName, value); 329 } 330 if (considerDefaults) { 331 for (int i = attribute.getValueCount(); i < entityType.getAttributeMinOccurrence( 332 attributeName); i++) { 333 target.addAttributeValue( 334 attributeName, 335 attributeConfigurations.get(attributeName).getDefaultValue()); 336 } 337 } 338 } else { 339 for (CmsEntity value : attribute.getComplexValues()) { 340 target.addAttributeValue(attributeName, value); 341 } 342 if (considerDefaults) { 343 for (int i = attribute.getValueCount(); i < entityType.getAttributeMinOccurrence( 344 attributeName); i++) { 345 target.addAttributeValue( 346 attributeName, 347 createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations)); 348 } 349 } 350 } 351 } else if (considerDefaults) { 352 for (int i = 0; i < entityType.getAttributeMinOccurrence(attributeName); i++) { 353 if (attributeType.isSimpleType()) { 354 target.addAttributeValue( 355 attributeName, 356 attributeConfigurations.get(attributeName).getDefaultValue()); 357 } else { 358 target.addAttributeValue( 359 attributeName, 360 createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations)); 361 } 362 } 363 } 364 } else { 365 if (!attributeType.isSimpleType()) { 366 CmsEntityAttribute targetAttribute = target.getAttribute(attributeName); 367 CmsEntityAttribute originalAttribute = original != null 368 ? original.getAttribute(attributeName) 369 : null; 370 if (targetAttribute != null) { 371 for (int i = 0; i < targetAttribute.getComplexValues().size(); i++) { 372 CmsEntity subTarget = targetAttribute.getComplexValues().get(i); 373 CmsEntity subOriginal = (originalAttribute != null) 374 && (originalAttribute.getComplexValues().size() > i) 375 ? originalAttribute.getComplexValues().get(i) 376 : null; 377 transferValues( 378 subOriginal, 379 subTarget, 380 transferAttributes, 381 entityTypes, 382 attributeConfigurations, 383 considerDefaults); 384 } 385 } 386 } 387 } 388 } 389 } 390 391 /** 392 * Returns the entity id according to the given UUID.<p> 393 * 394 * @param uuid the UUID 395 * @param locale the content locale 396 * 397 * @return the entity id 398 */ 399 public static String uuidToEntityId(CmsUUID uuid, String locale) { 400 401 return ENTITY_ID_PREFIX + locale + "/" + uuid.toString(); 402 } 403 404 /** 405 * Creates an entity object containing the default values configured for it's type.<p> 406 * 407 * @param entityType the entity type 408 * @param entityTypes the entity types 409 * @param attributeConfigurations the attribute configurations 410 * 411 * @return the created entity 412 */ 413 protected static CmsEntity createDefaultValueEntity( 414 CmsType entityType, 415 Map<String, CmsType> entityTypes, 416 Map<String, CmsAttributeConfiguration> attributeConfigurations) { 417 418 CmsEntity result = new CmsEntity(null, entityType.getId()); 419 for (String attributeName : entityType.getAttributeNames()) { 420 CmsType attributeType = entityTypes.get(entityType.getAttributeTypeName(attributeName)); 421 for (int i = 0; i < entityType.getAttributeMinOccurrence(attributeName); i++) { 422 if (attributeType.isSimpleType()) { 423 result.addAttributeValue( 424 attributeName, 425 attributeConfigurations.get(attributeName).getDefaultValue()); 426 } else { 427 result.addAttributeValue( 428 attributeName, 429 createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations)); 430 } 431 } 432 } 433 return result; 434 } 435 436 /** 437 * Returns the available locales.<p> 438 * 439 * @return the available locales 440 */ 441 public Map<String, String> getAvailableLocales() { 442 443 return m_availableLocales; 444 } 445 446 /** 447 * Gets the complex widget configurations.<p> 448 * 449 * @return the complex widget configurations 450 */ 451 public Map<String, CmsComplexWidgetData> getComplexWidgetData() { 452 453 return m_complexWidgetData; 454 } 455 456 /** 457 * Returns the content locales.<p> 458 * 459 * @return the content locales 460 */ 461 public List<String> getContentLocales() { 462 463 return m_contentLocales; 464 } 465 466 /** 467 * Returns the editor change handler scopes.<p> 468 * 469 * @return the editor change handler scopes 470 */ 471 public Set<String> getEditorChangeScopes() { 472 473 return m_editorChangeScopes; 474 } 475 476 /** 477 * Returns the external widget configurations.<p> 478 * 479 * @return the external widget configurations 480 */ 481 public List<CmsExternalWidgetConfiguration> getExternalWidgetConfigurations() { 482 483 return m_externalWidgetConfigurations; 484 } 485 486 /** 487 * Returns the resource icon classes.<p> 488 * 489 * @return the resource icon classes 490 */ 491 public String getIconClasses() { 492 493 return m_iconClasses; 494 } 495 496 /** 497 * Returns the model file informations.<p> 498 * 499 * @return the model file informations 500 */ 501 public List<CmsModelResourceInfo> getModelInfos() { 502 503 return m_modelInfos; 504 } 505 506 /** 507 * Returns the new link.<p> 508 * 509 * @return the new link 510 */ 511 public String getNewLink() { 512 513 return m_newLink; 514 } 515 516 /** 517 * Returns the reference resource structure id.<p> 518 * 519 * @return the reference resource structure id 520 */ 521 public CmsUUID getReferenceResourceId() { 522 523 return m_referenceResourceId; 524 } 525 526 /** 527 * Returns the resource type.<p> 528 * 529 * @return the resource type 530 */ 531 public String getResourceType() { 532 533 return m_resourceType; 534 } 535 536 /** 537 * Returns the site path.<p> 538 * 539 * @return the site path 540 */ 541 public String getSitePath() { 542 543 return m_sitePath; 544 } 545 546 /** 547 * Returns the paths to skip during locale synchronization.<p> 548 * 549 * @return the paths to skip during locale synchronization 550 */ 551 public Collection<String> getSkipPaths() { 552 553 return m_skipPaths; 554 } 555 556 /** 557 * Returns the elements that require a synchronization across all locales.<p> 558 * 559 * @return the element paths 560 */ 561 public List<String> getSynchronizations() { 562 563 return m_synchronizations; 564 } 565 566 /** 567 * Returns the locale synchronization values.<p> 568 * 569 * @return the locale synchronization values 570 */ 571 public Map<String, String> getSyncValues() { 572 573 return m_syncValues; 574 } 575 576 /** 577 * Returns the title.<p> 578 * 579 * @return the title 580 */ 581 public String getTitle() { 582 583 return m_title; 584 } 585 586 /** 587 * Checks whether there is an active content augmentation. 588 * 589 * @return true if there is an active content augmentation 590 */ 591 public boolean isTranslationEnabled() { 592 593 return m_isTranslationEnabled; 594 } 595 596 /** 597 * Returns <code>true</code> if any editor change handlers have been configured for this content type.<p> 598 * 599 * @return <code>true</code> if any editor change handlers have been configured for this content type.<p> 600 */ 601 public boolean hasEditorChangeHandlers() { 602 603 return (m_editorChangeScopes != null) && !m_editorChangeScopes.isEmpty(); 604 } 605 606 /** 607 * Returns if there are locale synchronized elements configured.<p> 608 * 609 * @return <code>true</code> if there are locale synchronized elements configured 610 */ 611 public boolean hasSynchronizedElements() { 612 613 return !m_synchronizations.isEmpty(); 614 } 615 616 /** 617 * Returns the value of the acacia-unlock configuration option.<p> 618 * 619 * @return the value of the acacia-unlock configuration option 620 */ 621 public boolean isAutoUnlock() { 622 623 return m_autoUnlock; 624 } 625 626 /** 627 * Returns if the resource needs to removed on cancel.<p> 628 * 629 * @return <code>true</code> if the resource needs to removed on cancel 630 */ 631 public boolean isDeleteOnCancel() { 632 633 return m_deleteOnCancel; 634 } 635 636 /** 637 * Returns true if the direct edit flag is set, which means that the editor was opened from the classic direct edit mode.<p> 638 * 639 * @return true if the direct edit flag is set 640 */ 641 public boolean isDirectEdit() { 642 643 return m_isDirectEdit; 644 } 645 646 /** 647 * Returns if the model file informations are present, in this case no additional data is contained.<p> 648 * 649 * @return <code>true</code> if the definition contains the model file informations 650 */ 651 public boolean isModelInfo() { 652 653 return m_modelInfos != null; 654 } 655 656 /** 657 * Returns if auto correction was performed.<p> 658 * 659 * @return <code>true</code> if auto correction was performed 660 */ 661 public boolean isPerformedAutocorrection() { 662 663 return m_performedAutocorrection; 664 } 665 666 /** 667 * Checks if the element should be marked as 'reused' in the editor. 668 * 669 * @return true if the element should be marked as 'reused' in the editor 670 */ 671 public boolean isReusedElement() { 672 673 return m_reusedElement; 674 } 675 676 /** 677 * Sets if the resource needs to removed on cancel.<p> 678 * 679 * @param deleteOnCancel <code>true</code> if the resource needs to removed on cancel 680 */ 681 public void setDeleteOnCancel(boolean deleteOnCancel) { 682 683 m_deleteOnCancel = deleteOnCancel; 684 } 685 686 /** 687 * Sets the value of the direct edit flag.<p> 688 * 689 * @param isDirectEdit the new value for the direct edit flag 690 */ 691 public void setDirectEdit(boolean isDirectEdit) { 692 693 m_isDirectEdit = isDirectEdit; 694 } 695 696 public void setTranslationEnabled(boolean translationEnabled) { 697 698 m_isTranslationEnabled = translationEnabled; 699 } 700 701 /** 702 * Enables / disables marking of the element as 'reused' in the content editor. 703 * 704 * @param reused true if the element should be shown as 'reused' 705 */ 706 public void setReusedElement(boolean reused) { 707 708 m_reusedElement = reused; 709 710 } 711}