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, 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.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        attributeName = entity.getTypeName()
263            + "/"
264            + org.opencms.acacia.shared.CmsContentDefinition.removeIndex(attributeName);
265        CmsEntityAttribute attribute = entity.getAttribute(attributeName);
266        if (!((attribute == null) || (attribute.isComplexValue() && (path == null)))) {
267            if (attribute.isSimpleValue()) {
268                if ((path == null) && (attribute.getValueCount() > 0)) {
269                    List<String> values = attribute.getSimpleValues();
270                    result = values.get(index);
271                }
272            } else if (attribute.getValueCount() > (index)) {
273                List<CmsEntity> values = attribute.getComplexValues();
274                result = getValueForPath(values.get(index), path);
275            }
276        }
277        return result;
278    }
279
280    /**
281     * Transfers values from the original entity to the given target entity.<p>
282     *
283     * @param original the original entity
284     * @param target the target entity
285     * @param transferAttributes the attributes to consider for the value transfer
286     * @param entityTypes the entity types
287     * @param attributeConfigurations the attribute configurations
288     * @param considerDefaults if default values should be added according to minimum occurrence settings
289     */
290    public static void transferValues(
291        CmsEntity original,
292        CmsEntity target,
293        List<String> transferAttributes,
294        Map<String, CmsType> entityTypes,
295        Map<String, CmsAttributeConfiguration> attributeConfigurations,
296        boolean considerDefaults) {
297
298        CmsType entityType = entityTypes.get(target.getTypeName());
299        for (String attributeName : entityType.getAttributeNames()) {
300            CmsType attributeType = entityTypes.get(entityType.getAttributeTypeName(attributeName));
301            if (transferAttributes.contains(attributeName)) {
302
303                target.removeAttribute(attributeName);
304                CmsEntityAttribute attribute = original != null ? original.getAttribute(attributeName) : null;
305                if (attribute != null) {
306                    if (attributeType.isSimpleType()) {
307                        for (String value : attribute.getSimpleValues()) {
308                            target.addAttributeValue(attributeName, value);
309                        }
310                        if (considerDefaults) {
311                            for (int i = attribute.getValueCount(); i < entityType.getAttributeMinOccurrence(
312                                attributeName); i++) {
313                                target.addAttributeValue(
314                                    attributeName,
315                                    attributeConfigurations.get(attributeName).getDefaultValue());
316                            }
317                        }
318                    } else {
319                        for (CmsEntity value : attribute.getComplexValues()) {
320                            target.addAttributeValue(attributeName, value);
321                        }
322                        if (considerDefaults) {
323                            for (int i = attribute.getValueCount(); i < entityType.getAttributeMinOccurrence(
324                                attributeName); i++) {
325                                target.addAttributeValue(
326                                    attributeName,
327                                    createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations));
328                            }
329                        }
330                    }
331                } else if (considerDefaults) {
332                    for (int i = 0; i < entityType.getAttributeMinOccurrence(attributeName); i++) {
333                        if (attributeType.isSimpleType()) {
334                            target.addAttributeValue(
335                                attributeName,
336                                attributeConfigurations.get(attributeName).getDefaultValue());
337                        } else {
338                            target.addAttributeValue(
339                                attributeName,
340                                createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations));
341                        }
342                    }
343                }
344            } else {
345                if (!attributeType.isSimpleType()) {
346                    CmsEntityAttribute targetAttribute = target.getAttribute(attributeName);
347                    CmsEntityAttribute originalAttribute = original != null
348                    ? original.getAttribute(attributeName)
349                    : null;
350                    if (targetAttribute != null) {
351                        for (int i = 0; i < targetAttribute.getComplexValues().size(); i++) {
352                            CmsEntity subTarget = targetAttribute.getComplexValues().get(i);
353                            CmsEntity subOriginal = (originalAttribute != null)
354                                && (originalAttribute.getComplexValues().size() > i)
355                                ? originalAttribute.getComplexValues().get(i)
356                                : null;
357                            transferValues(
358                                subOriginal,
359                                subTarget,
360                                transferAttributes,
361                                entityTypes,
362                                attributeConfigurations,
363                                considerDefaults);
364                        }
365                    }
366                }
367            }
368        }
369    }
370
371    /**
372     * Returns the entity id according to the given UUID.<p>
373     *
374     * @param uuid the UUID
375     * @param locale the content locale
376     *
377     * @return the entity id
378     */
379    public static String uuidToEntityId(CmsUUID uuid, String locale) {
380
381        return ENTITY_ID_PREFIX + locale + "/" + uuid.toString();
382    }
383
384    /**
385     * Creates an entity object containing the default values configured for it's type.<p>
386     *
387     * @param entityType the entity type
388     * @param entityTypes the entity types
389     * @param attributeConfigurations the attribute configurations
390     *
391     * @return the created entity
392     */
393    protected static CmsEntity createDefaultValueEntity(
394        CmsType entityType,
395        Map<String, CmsType> entityTypes,
396        Map<String, CmsAttributeConfiguration> attributeConfigurations) {
397
398        CmsEntity result = new CmsEntity(null, entityType.getId());
399        for (String attributeName : entityType.getAttributeNames()) {
400            CmsType attributeType = entityTypes.get(entityType.getAttributeTypeName(attributeName));
401            for (int i = 0; i < entityType.getAttributeMinOccurrence(attributeName); i++) {
402                if (attributeType.isSimpleType()) {
403                    result.addAttributeValue(
404                        attributeName,
405                        attributeConfigurations.get(attributeName).getDefaultValue());
406                } else {
407                    result.addAttributeValue(
408                        attributeName,
409                        createDefaultValueEntity(attributeType, entityTypes, attributeConfigurations));
410                }
411            }
412        }
413        return result;
414    }
415
416    /**
417     * Returns the available locales.<p>
418     *
419     * @return the available locales
420     */
421    public Map<String, String> getAvailableLocales() {
422
423        return m_availableLocales;
424    }
425
426    /**
427     * Gets the complex widget configurations.<p>
428     *
429     * @return the complex widget configurations
430     */
431    public Map<String, CmsComplexWidgetData> getComplexWidgetData() {
432
433        return m_complexWidgetData;
434    }
435
436    /**
437     * Returns the content locales.<p>
438     *
439     * @return the content locales
440     */
441    public List<String> getContentLocales() {
442
443        return m_contentLocales;
444    }
445
446    /**
447     * Returns the editor change handler scopes.<p>
448     *
449     * @return the editor change handler scopes
450     */
451    public Set<String> getEditorChangeScopes() {
452
453        return m_editorChangeScopes;
454    }
455
456    /**
457     * Returns the external widget configurations.<p>
458     *
459     * @return the external widget configurations
460     */
461    public List<CmsExternalWidgetConfiguration> getExternalWidgetConfigurations() {
462
463        return m_externalWidgetConfigurations;
464    }
465
466    /**
467     * Returns the resource icon classes.<p>
468     *
469     * @return the resource icon classes
470     */
471    public String getIconClasses() {
472
473        return m_iconClasses;
474    }
475
476    /**
477     * Returns the model file informations.<p>
478     *
479     * @return the model file informations
480     */
481    public List<CmsModelResourceInfo> getModelInfos() {
482
483        return m_modelInfos;
484    }
485
486    /**
487     * Returns the new link.<p>
488     *
489     * @return the new link
490     */
491    public String getNewLink() {
492
493        return m_newLink;
494    }
495
496    /**
497     * Returns the reference resource structure id.<p>
498     *
499     * @return the reference resource structure id
500     */
501    public CmsUUID getReferenceResourceId() {
502
503        return m_referenceResourceId;
504    }
505
506    /**
507     * Returns the resource type.<p>
508     *
509     * @return the resource type
510     */
511    public String getResourceType() {
512
513        return m_resourceType;
514    }
515
516    /**
517     * Returns the site path.<p>
518     *
519     * @return the site path
520     */
521    public String getSitePath() {
522
523        return m_sitePath;
524    }
525
526    /**
527     * Returns the paths to skip during locale synchronization.<p>
528     *
529     * @return the paths to skip during locale synchronization
530     */
531    public Collection<String> getSkipPaths() {
532
533        return m_skipPaths;
534    }
535
536    /**
537     * Returns the elements that require a synchronization across all locales.<p>
538     *
539     * @return the element paths
540     */
541    public List<String> getSynchronizations() {
542
543        return m_synchronizations;
544    }
545
546    /**
547     * Returns the locale synchronization values.<p>
548     *
549     * @return the locale synchronization values
550     */
551    public Map<String, String> getSyncValues() {
552
553        return m_syncValues;
554    }
555
556    /**
557     * Returns the title.<p>
558     *
559     * @return the title
560     */
561    public String getTitle() {
562
563        return m_title;
564    }
565
566    /**
567     * Returns <code>true</code> if any editor change handlers have been configured for this content type.<p>
568     *
569     * @return <code>true</code> if any editor change handlers have been configured for this content type.<p>
570     */
571    public boolean hasEditorChangeHandlers() {
572
573        return (m_editorChangeScopes != null) && !m_editorChangeScopes.isEmpty();
574    }
575
576    /**
577     * Returns if there are locale synchronized elements configured.<p>
578     *
579     * @return <code>true</code> if there are locale synchronized elements configured
580     */
581    public boolean hasSynchronizedElements() {
582
583        return !m_synchronizations.isEmpty();
584    }
585
586    /**
587     * Returns the value of the acacia-unlock configuration option.<p>
588     *
589     * @return the value of the acacia-unlock configuration option
590     */
591    public boolean isAutoUnlock() {
592
593        return m_autoUnlock;
594    }
595
596    /**
597     * Returns if the resource needs to removed on cancel.<p>
598     *
599     * @return <code>true</code> if the resource needs to removed on cancel
600     */
601    public boolean isDeleteOnCancel() {
602
603        return m_deleteOnCancel;
604    }
605
606    /**
607     * Returns true if the direct edit flag is set, which means that the editor was opened from the classic direct edit mode.<p>
608     *
609     * @return true if the direct edit flag is set
610     */
611    public boolean isDirectEdit() {
612
613        return m_isDirectEdit;
614    }
615
616    /**
617     * Returns if the model file informations are present, in this case no additional data is contained.<p>
618     *
619     * @return <code>true</code> if the definition contains the model file informations
620     */
621    public boolean isModelInfo() {
622
623        return m_modelInfos != null;
624    }
625
626    /**
627     * Returns if auto correction was performed.<p>
628     *
629     * @return <code>true</code> if auto correction was performed
630     */
631    public boolean isPerformedAutocorrection() {
632
633        return m_performedAutocorrection;
634    }
635
636    /**
637     * Checks if the element should be marked as 'reused' in the editor.
638     *
639     * @return true if the element should be marked as 'reused' in the editor
640     */
641    public boolean isReusedElement() {
642
643        return m_reusedElement;
644    }
645
646    /**
647     * Sets if the resource needs to removed on cancel.<p>
648     *
649     * @param deleteOnCancel <code>true</code> if the resource needs to removed on cancel
650     */
651    public void setDeleteOnCancel(boolean deleteOnCancel) {
652
653        m_deleteOnCancel = deleteOnCancel;
654    }
655
656    /**
657     * Sets the value of the direct edit flag.<p>
658     *
659     * @param isDirectEdit the new value for the direct edit flag
660     */
661    public void setDirectEdit(boolean isDirectEdit) {
662
663        m_isDirectEdit = isDirectEdit;
664    }
665
666    /**
667     * Enables / disables marking of the element as 'reused' in the content editor.
668     *
669     * @param reused true if the element should be shown as 'reused'
670     */
671    public void setReusedElement(boolean reused) {
672
673        m_reusedElement = reused;
674
675    }
676}