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