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 /** The content title. */ 113 private String m_title; 114 115 /** 116 * Constructor for model file informations object.<p> 117 * 118 * @param modelInfos the model file informations 119 * @param newLink the new link 120 * @param referenceId the reference resource structure id 121 * @param locale the locale 122 */ 123 public CmsContentDefinition( 124 List<CmsModelResourceInfo> modelInfos, 125 String newLink, 126 CmsUUID referenceId, 127 String locale) { 128 129 super(null, null, null, null, null, true, locale); 130 m_modelInfos = modelInfos; 131 m_newLink = newLink; 132 m_referenceResourceId = referenceId; 133 } 134 135 /** 136 * Constructor.<p> 137 * 138 * @param entityId the entity id 139 * @param entities the locale specific entities of the content 140 * @param configurations the attribute configurations 141 * @param externalWidgetConfigurations the external widget configurations 142 * @param complexWidgetData the complex widget configurations 143 * @param types the types 144 * @param tabInfos the tab information 145 * @param locale the content locale 146 * @param contentLocales the content locales 147 * @param availableLocales the available locales 148 * @param synchronizations the elements that require a synchronization across all locales 149 * @param syncValues the locale synchronization values 150 * @param skipPaths the paths to skip during locale synchronization 151 * @param title the content title 152 * @param sitePath the site path 153 * @param resourceType the resource type name 154 * @param iconClasses the resource icon classes 155 * @param performedAutocorrection flag indicating the current content has an invalid XML structure and was auto corrected 156 * @param autoUnlock false if the editor should not unlock resources automatically in standalone mode 157 * @param editorChangeScopes the editor change handler scopes 158 */ 159 public CmsContentDefinition( 160 String entityId, 161 Map<String, CmsEntity> entities, 162 Map<String, CmsAttributeConfiguration> configurations, 163 Collection<CmsExternalWidgetConfiguration> externalWidgetConfigurations, 164 Map<String, CmsComplexWidgetData> complexWidgetData, 165 Map<String, CmsType> types, 166 List<CmsTabInfo> tabInfos, 167 String locale, 168 List<String> contentLocales, 169 Map<String, String> availableLocales, 170 List<String> synchronizations, 171 Map<String, String> syncValues, 172 Collection<String> skipPaths, 173 String title, 174 String sitePath, 175 String resourceType, 176 String iconClasses, 177 boolean performedAutocorrection, 178 boolean autoUnlock, 179 Set<String> editorChangeScopes) { 180 181 super(entityId, entities, configurations, types, tabInfos, true, locale); 182 m_contentLocales = contentLocales; 183 m_availableLocales = availableLocales; 184 m_synchronizations = synchronizations; 185 m_syncValues = syncValues; 186 m_skipPaths = skipPaths; 187 m_complexWidgetData = complexWidgetData; 188 m_title = title; 189 m_sitePath = sitePath; 190 m_resourceType = resourceType; 191 m_iconClasses = iconClasses; 192 m_externalWidgetConfigurations = new ArrayList<CmsExternalWidgetConfiguration>(externalWidgetConfigurations); 193 m_performedAutocorrection = performedAutocorrection; 194 m_autoUnlock = autoUnlock; 195 m_editorChangeScopes = editorChangeScopes; 196 } 197 198 /** 199 * Constructor for serialization only.<p> 200 */ 201 protected CmsContentDefinition() { 202 203 super(); 204 } 205 206 /** 207 * Returns the UUID according to the given entity id.<p> 208 * 209 * @param entityId the entity id 210 * 211 * @return the entity id 212 */ 213 public static CmsUUID entityIdToUuid(String entityId) { 214 215 if (entityId.startsWith(ENTITY_ID_PREFIX)) { 216 entityId = entityId.substring(entityId.lastIndexOf("/") + 1); 217 } 218 return new CmsUUID(entityId); 219 } 220 221 /** 222 * Extracts the locale from the entity id.<p> 223 * 224 * @param entityId the entity id 225 * 226 * @return the locale 227 */ 228 public static String getLocaleFromId(String entityId) { 229 230 if (entityId.startsWith(ENTITY_ID_PREFIX)) { 231 return entityId.substring(ENTITY_ID_PREFIX.length(), entityId.lastIndexOf("/")); 232 } 233 return null; 234 } 235 236 /** 237 * Returns the value for the given XPath expression.<p> 238 * 239 * @param entity the entity 240 * @param path the path 241 * 242 * @return the value 243 */ 244 public static String getValueForPath(CmsEntity entity, String path) { 245 246 String result = null; 247 if (path.startsWith("/")) { 248 path = path.substring(1); 249 } 250 String attributeName; 251 if (path.contains("/")) { 252 attributeName = path.substring(0, path.indexOf("/")); 253 path = path.substring(path.indexOf("/")); 254 } else { 255 attributeName = path; 256 path = null; 257 } 258 int index = org.opencms.acacia.shared.CmsContentDefinition.extractIndex(attributeName); 259 if (index > 0) { 260 index--; 261 262 } 263 String typeName = entity.getTypeName(); 264 265 attributeName = typeName + "/" + org.opencms.acacia.shared.CmsContentDefinition.removeIndex(attributeName); 266 CmsEntityAttribute choiceAttr = entity.getAttribute(CmsType.CHOICE_ATTRIBUTE_NAME); 267 if ((choiceAttr != null) && choiceAttr.isComplexValue()) { 268 List<CmsEntity> choiceChildren = new ArrayList<>(); 269 for (CmsEntity child : choiceAttr.getComplexValues()) { 270 if (child.getAttribute(attributeName) != null) { 271 choiceChildren.add(child); 272 } 273 } 274 if (index < choiceChildren.size()) { 275 entity = choiceChildren.get(index); 276 index = 0; 277 } else { 278 return null; 279 } 280 } 281 282 CmsEntityAttribute attribute = entity.getAttribute(attributeName); 283 if (!((attribute == null) || (attribute.isComplexValue() && (path == null)))) { 284 if (attribute.isSimpleValue()) { 285 if ((path == null) && (attribute.getValueCount() > 0)) { 286 List<String> values = attribute.getSimpleValues(); 287 result = values.get(index); 288 } 289 } else if (attribute.getValueCount() > (index)) { 290 List<CmsEntity> values = attribute.getComplexValues(); 291 result = getValueForPath(values.get(index), path); 292 } 293 } 294 return result; 295 } 296 297 /** 298 * Transfers values from the original entity to the given target entity.<p> 299 * 300 * @param original the original entity 301 * @param target the target entity 302 * @param transferAttributes the attributes to consider for the value transfer 303 * @param entityTypes the entity types 304 * @param attributeConfigurations the attribute configurations 305 * @param considerDefaults if default values should be added according to minimum occurrence settings 306 */ 307 public static void transferValues( 308 CmsEntity original, 309 CmsEntity target, 310 List<String> transferAttributes, 311 Map<String, CmsType> entityTypes, 312 Map<String, CmsAttributeConfiguration> attributeConfigurations, 313 boolean considerDefaults) { 314 315 CmsType entityType = entityTypes.get(target.getTypeName()); 316 for (String attributeName : entityType.getAttributeNames()) { 317 CmsType attributeType = entityTypes.get(entityType.getAttributeTypeName(attributeName)); 318 if (transferAttributes.contains(attributeName)) { 319 320 target.removeAttribute(attributeName); 321 CmsEntityAttribute attribute = original != null ? original.getAttribute(attributeName) : null; 322 if (attribute != null) { 323 if (attributeType.isSimpleType()) { 324 for (String value : attribute.getSimpleValues()) { 325 target.addAttributeValue(attributeName, value); 326 } 327 if (considerDefaults) { 328 for (int i = attribute.getValueCount(); i < entityType.getAttributeMinOccurrence( 329 attributeName); i++) { 330 target.addAttributeValue( 331 attributeName, 332 attributeConfigurations.get(attributeName).getDefaultValue()); 333 } 334 } 335 } else { 336 for (CmsEntity value : attribute.getComplexValues()) { 337 target.addAttributeValue(attributeName, value); 338 } 339 if (considerDefaults) { 340 for (int i = attribute.getValueCount(); i < entityType.getAttributeMinOccurrence( 341 attributeName); i++) { 342 target.addAttributeValue( 343 attributeName, 344 createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations)); 345 } 346 } 347 } 348 } else if (considerDefaults) { 349 for (int i = 0; i < entityType.getAttributeMinOccurrence(attributeName); i++) { 350 if (attributeType.isSimpleType()) { 351 target.addAttributeValue( 352 attributeName, 353 attributeConfigurations.get(attributeName).getDefaultValue()); 354 } else { 355 target.addAttributeValue( 356 attributeName, 357 createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations)); 358 } 359 } 360 } 361 } else { 362 if (!attributeType.isSimpleType()) { 363 CmsEntityAttribute targetAttribute = target.getAttribute(attributeName); 364 CmsEntityAttribute originalAttribute = original != null 365 ? original.getAttribute(attributeName) 366 : null; 367 if (targetAttribute != null) { 368 for (int i = 0; i < targetAttribute.getComplexValues().size(); i++) { 369 CmsEntity subTarget = targetAttribute.getComplexValues().get(i); 370 CmsEntity subOriginal = (originalAttribute != null) 371 && (originalAttribute.getComplexValues().size() > i) 372 ? originalAttribute.getComplexValues().get(i) 373 : null; 374 transferValues( 375 subOriginal, 376 subTarget, 377 transferAttributes, 378 entityTypes, 379 attributeConfigurations, 380 considerDefaults); 381 } 382 } 383 } 384 } 385 } 386 } 387 388 /** 389 * Returns the entity id according to the given UUID.<p> 390 * 391 * @param uuid the UUID 392 * @param locale the content locale 393 * 394 * @return the entity id 395 */ 396 public static String uuidToEntityId(CmsUUID uuid, String locale) { 397 398 return ENTITY_ID_PREFIX + locale + "/" + uuid.toString(); 399 } 400 401 /** 402 * Creates an entity object containing the default values configured for it's type.<p> 403 * 404 * @param entityType the entity type 405 * @param entityTypes the entity types 406 * @param attributeConfigurations the attribute configurations 407 * 408 * @return the created entity 409 */ 410 protected static CmsEntity createDefaultValueEntity( 411 CmsType entityType, 412 Map<String, CmsType> entityTypes, 413 Map<String, CmsAttributeConfiguration> attributeConfigurations) { 414 415 CmsEntity result = new CmsEntity(null, entityType.getId()); 416 for (String attributeName : entityType.getAttributeNames()) { 417 CmsType attributeType = entityTypes.get(entityType.getAttributeTypeName(attributeName)); 418 for (int i = 0; i < entityType.getAttributeMinOccurrence(attributeName); i++) { 419 if (attributeType.isSimpleType()) { 420 result.addAttributeValue( 421 attributeName, 422 attributeConfigurations.get(attributeName).getDefaultValue()); 423 } else { 424 result.addAttributeValue( 425 attributeName, 426 createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations)); 427 } 428 } 429 } 430 return result; 431 } 432 433 /** 434 * Returns the available locales.<p> 435 * 436 * @return the available locales 437 */ 438 public Map<String, String> getAvailableLocales() { 439 440 return m_availableLocales; 441 } 442 443 /** 444 * Gets the complex widget configurations.<p> 445 * 446 * @return the complex widget configurations 447 */ 448 public Map<String, CmsComplexWidgetData> getComplexWidgetData() { 449 450 return m_complexWidgetData; 451 } 452 453 /** 454 * Returns the content locales.<p> 455 * 456 * @return the content locales 457 */ 458 public List<String> getContentLocales() { 459 460 return m_contentLocales; 461 } 462 463 /** 464 * Returns the editor change handler scopes.<p> 465 * 466 * @return the editor change handler scopes 467 */ 468 public Set<String> getEditorChangeScopes() { 469 470 return m_editorChangeScopes; 471 } 472 473 /** 474 * Returns the external widget configurations.<p> 475 * 476 * @return the external widget configurations 477 */ 478 public List<CmsExternalWidgetConfiguration> getExternalWidgetConfigurations() { 479 480 return m_externalWidgetConfigurations; 481 } 482 483 /** 484 * Returns the resource icon classes.<p> 485 * 486 * @return the resource icon classes 487 */ 488 public String getIconClasses() { 489 490 return m_iconClasses; 491 } 492 493 /** 494 * Returns the model file informations.<p> 495 * 496 * @return the model file informations 497 */ 498 public List<CmsModelResourceInfo> getModelInfos() { 499 500 return m_modelInfos; 501 } 502 503 /** 504 * Returns the new link.<p> 505 * 506 * @return the new link 507 */ 508 public String getNewLink() { 509 510 return m_newLink; 511 } 512 513 /** 514 * Returns the reference resource structure id.<p> 515 * 516 * @return the reference resource structure id 517 */ 518 public CmsUUID getReferenceResourceId() { 519 520 return m_referenceResourceId; 521 } 522 523 /** 524 * Returns the resource type.<p> 525 * 526 * @return the resource type 527 */ 528 public String getResourceType() { 529 530 return m_resourceType; 531 } 532 533 /** 534 * Returns the site path.<p> 535 * 536 * @return the site path 537 */ 538 public String getSitePath() { 539 540 return m_sitePath; 541 } 542 543 /** 544 * Returns the paths to skip during locale synchronization.<p> 545 * 546 * @return the paths to skip during locale synchronization 547 */ 548 public Collection<String> getSkipPaths() { 549 550 return m_skipPaths; 551 } 552 553 /** 554 * Returns the elements that require a synchronization across all locales.<p> 555 * 556 * @return the element paths 557 */ 558 public List<String> getSynchronizations() { 559 560 return m_synchronizations; 561 } 562 563 /** 564 * Returns the locale synchronization values.<p> 565 * 566 * @return the locale synchronization values 567 */ 568 public Map<String, String> getSyncValues() { 569 570 return m_syncValues; 571 } 572 573 /** 574 * Returns the title.<p> 575 * 576 * @return the title 577 */ 578 public String getTitle() { 579 580 return m_title; 581 } 582 583 /** 584 * Returns <code>true</code> if any editor change handlers have been configured for this content type.<p> 585 * 586 * @return <code>true</code> if any editor change handlers have been configured for this content type.<p> 587 */ 588 public boolean hasEditorChangeHandlers() { 589 590 return (m_editorChangeScopes != null) && !m_editorChangeScopes.isEmpty(); 591 } 592 593 /** 594 * Returns if there are locale synchronized elements configured.<p> 595 * 596 * @return <code>true</code> if there are locale synchronized elements configured 597 */ 598 public boolean hasSynchronizedElements() { 599 600 return !m_synchronizations.isEmpty(); 601 } 602 603 /** 604 * Returns the value of the acacia-unlock configuration option.<p> 605 * 606 * @return the value of the acacia-unlock configuration option 607 */ 608 public boolean isAutoUnlock() { 609 610 return m_autoUnlock; 611 } 612 613 /** 614 * Returns if the resource needs to removed on cancel.<p> 615 * 616 * @return <code>true</code> if the resource needs to removed on cancel 617 */ 618 public boolean isDeleteOnCancel() { 619 620 return m_deleteOnCancel; 621 } 622 623 /** 624 * Returns true if the direct edit flag is set, which means that the editor was opened from the classic direct edit mode.<p> 625 * 626 * @return true if the direct edit flag is set 627 */ 628 public boolean isDirectEdit() { 629 630 return m_isDirectEdit; 631 } 632 633 /** 634 * Returns if the model file informations are present, in this case no additional data is contained.<p> 635 * 636 * @return <code>true</code> if the definition contains the model file informations 637 */ 638 public boolean isModelInfo() { 639 640 return m_modelInfos != null; 641 } 642 643 /** 644 * Returns if auto correction was performed.<p> 645 * 646 * @return <code>true</code> if auto correction was performed 647 */ 648 public boolean isPerformedAutocorrection() { 649 650 return m_performedAutocorrection; 651 } 652 653 /** 654 * Checks if the element should be marked as 'reused' in the editor. 655 * 656 * @return true if the element should be marked as 'reused' in the editor 657 */ 658 public boolean isReusedElement() { 659 660 return m_reusedElement; 661 } 662 663 /** 664 * Sets if the resource needs to removed on cancel.<p> 665 * 666 * @param deleteOnCancel <code>true</code> if the resource needs to removed on cancel 667 */ 668 public void setDeleteOnCancel(boolean deleteOnCancel) { 669 670 m_deleteOnCancel = deleteOnCancel; 671 } 672 673 /** 674 * Sets the value of the direct edit flag.<p> 675 * 676 * @param isDirectEdit the new value for the direct edit flag 677 */ 678 public void setDirectEdit(boolean isDirectEdit) { 679 680 m_isDirectEdit = isDirectEdit; 681 } 682 683 /** 684 * Enables / disables marking of the element as 'reused' in the content editor. 685 * 686 * @param reused true if the element should be shown as 'reused' 687 */ 688 public void setReusedElement(boolean reused) { 689 690 m_reusedElement = reused; 691 692 } 693}