001/*
002 *  Copyright 2018 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.contenttypeseditor;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025import java.util.Optional;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.configuration.Configuration;
029import org.apache.avalon.framework.configuration.ConfigurationException;
030import org.apache.avalon.framework.configuration.DefaultConfigurationSerializer;
031import org.apache.avalon.framework.logger.AbstractLogEnabled;
032import org.apache.avalon.framework.service.ServiceException;
033import org.apache.avalon.framework.service.ServiceManager;
034import org.apache.avalon.framework.service.Serviceable;
035import org.apache.commons.lang.StringUtils;
036import org.xml.sax.SAXException;
037
038import org.ametys.cms.contenttype.ContentAttributeDefinition;
039import org.ametys.cms.contenttype.ContentType;
040import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
041import org.ametys.cms.contenttype.RichTextAttributeDefinition;
042import org.ametys.cms.contenttype.SemanticAnnotation;
043import org.ametys.cms.contenttype.indexing.CustomIndexingField;
044import org.ametys.cms.contenttype.indexing.IndexingField;
045import org.ametys.cms.contenttype.indexing.IndexingModel;
046import org.ametys.cms.contenttype.indexing.MetadataIndexingField;
047import org.ametys.cms.languages.Language;
048import org.ametys.cms.languages.LanguagesManager;
049import org.ametys.core.right.Right;
050import org.ametys.core.right.RightsExtensionPoint;
051import org.ametys.core.util.I18nUtils;
052import org.ametys.plugins.repository.model.CompositeDefinition;
053import org.ametys.plugins.repository.model.RepeaterDefinition;
054import org.ametys.runtime.i18n.I18nizableText;
055import org.ametys.runtime.model.ElementDefinition;
056import org.ametys.runtime.model.Enumerator;
057import org.ametys.runtime.model.ModelItem;
058import org.ametys.runtime.model.ModelItemGroup;
059import org.ametys.runtime.model.ModelViewItem;
060import org.ametys.runtime.model.SimpleViewItemGroup;
061import org.ametys.runtime.model.StaticEnumerator;
062import org.ametys.runtime.model.View;
063import org.ametys.runtime.model.ViewItem;
064import org.ametys.runtime.model.ViewItemContainer;
065import org.ametys.runtime.parameter.DefaultValidator;
066import org.ametys.runtime.parameter.Validator;
067import org.ametys.runtime.plugin.component.PluginAware;
068
069/**
070 * Helper to retrieve content type infos
071 */
072public class ContentTypeInformationsHelper extends AbstractLogEnabled implements Component, Serviceable, PluginAware
073{
074    /** The Avalon role name */
075    public static final String ROLE = ContentTypeInformationsHelper.class.getName();
076
077    /** The content type extension point instance */
078    protected ContentTypeExtensionPoint _contentTypeExtensionPoint;
079
080    /** The rights extension point instance */
081    protected RightsExtensionPoint _rightsExtensionPoint;
082
083    /** Utility methods helping the management of internationalizable text */
084    protected I18nUtils _i18nUtils;
085
086    /** The langage manager instance */
087    protected LanguagesManager _langageManager;
088
089    Map<String, Language> _availableLanguages;
090
091    private String _pluginName;
092
093    /**
094     * Enumeration of metadata type
095     */
096    public enum ContentTypeAttributeDataType 
097    {
098        /** Metadata type */
099        METADATA,
100
101        /** Metadata set type */
102        METADATA_SET,
103
104        /** Fieldset type */
105        FIELDSET,
106
107        /** Metadata reference type */
108        METADATA_REF,
109
110        /** Indexing field type */
111        INDEXING_FIELD
112    }
113
114    @Override
115    public void service(ServiceManager serviceManager) throws ServiceException
116    {
117        _contentTypeExtensionPoint = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
118        _rightsExtensionPoint = (RightsExtensionPoint) serviceManager.lookup(RightsExtensionPoint.ROLE);
119        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
120        _langageManager = (LanguagesManager) serviceManager.lookup(LanguagesManager.ROLE);
121        _availableLanguages = _langageManager.getAvailableLanguages();
122    }
123
124    public void setPluginInfo(String pluginName, String featureName, String id)
125    {
126        _pluginName = pluginName;
127    }
128
129    /**
130     * Retrieves content type information
131     * @param contentTypeId the content type's identifier
132     * @param hideInheritedModelItems <code>true</code> to hide inherited model items of a content type according to contentTypeId parameter
133     * @param resolveMultilingualString if <code>true</code>, the multilingual string will be returned as simple strings
134     * @return a <code>Map</code> containing all information about the content type
135     */
136    public Map<String, Object> getContentTypeInfos(String contentTypeId, boolean hideInheritedModelItems, boolean resolveMultilingualString)
137    {
138        ContentType cType = _contentTypeExtensionPoint.getExtension(contentTypeId);
139        if (cType == null)
140        {
141            throw new IllegalStateException("Unknown content type '" + contentTypeId + "'");
142        }
143
144        Map<String, Object> contentTypeInfos = new HashMap<>();
145        
146        contentTypeInfos.put("id", cType.getId());
147        contentTypeInfos.put("label", getEnhancedMultilingualString(cType.getLabel(), resolveMultilingualString));
148        contentTypeInfos.put("description", getEnhancedMultilingualString(cType.getDescription(), resolveMultilingualString));
149        
150        contentTypeInfos.put("iconGlyph", cType.getIconGlyph());
151        contentTypeInfos.put("iconDecorator", cType.getIconDecorator());
152        contentTypeInfos.put("largeIcon", cType.getLargeIcon());
153        contentTypeInfos.put("mediumIcon", cType.getMediumIcon());
154        contentTypeInfos.put("smallIcon", cType.getSmallIcon());
155        
156        contentTypeInfos.put("hasImages", _hasImages(cType));
157        contentTypeInfos.put("category", getEnhancedMultilingualString(cType.getCategory(), resolveMultilingualString));
158        contentTypeInfos.put("pluginName", cType.getPluginName());
159        contentTypeInfos.put("defaultTitle", getEnhancedMultilingualString(cType.getDefaultTitle(), resolveMultilingualString));
160        
161        I18nizableText rightLabel = _getRightLabel(cType.getRight());
162        if (rightLabel != null)
163        {
164            contentTypeInfos.put("right", _getTextForAvailableLanguages(rightLabel, false));
165        }
166        
167        contentTypeInfos.put("private", cType.isPrivate());
168        contentTypeInfos.put("abstract", cType.isAbstract());
169        contentTypeInfos.put("multilingual", cType.isMultilingual());
170        contentTypeInfos.put("mixin", cType.isMixin());
171        contentTypeInfos.put("referencetable", cType.isReferenceTable());
172        contentTypeInfos.put("simple", cType.isSimple());
173        
174        contentTypeInfos.put("superTypes", _getSuperTypesInfos(cType));
175        cType.getParentAttributeDefinition().ifPresent(definition -> contentTypeInfos.put("parentRef", definition.getName()));
176        
177        contentTypeInfos.put("metadataNames", _getAttributeNames(cType.getModelItems()));
178        contentTypeInfos.put("attributes", getModelItemsInformation(cType, hideInheritedModelItems, resolveMultilingualString));
179        contentTypeInfos.put("views", _getViews(cType, hideInheritedModelItems, resolveMultilingualString));
180        
181        contentTypeInfos.put("indexingModel", _getIndexingModel(cType));
182        
183        return contentTypeInfos;
184    }
185    
186    private boolean _hasImages(ContentType contentType)
187    {
188        boolean hasImages = !contentType.getSmallIcon().endsWith("/unknown-small.png") || !contentType.getMediumIcon().endsWith("/unknown-medium.png") || !contentType.getLargeIcon().endsWith("/unknown-large.png");
189        
190        return hasImages;
191    }
192
193    private I18nizableText _getRightLabel(String rightId)
194    {
195        Right right = _rightsExtensionPoint.getExtension(rightId);
196        if (right != null)
197        {
198            return right.getLabel();
199        }
200        return null;
201    }
202
203    private List<Map<String, Object>> _getSuperTypesInfos(ContentType cType)
204    {
205        List<Map<String, Object>> superTypesInfos = new ArrayList<>();
206        for (String superTypeId : cType.getSupertypeIds())
207        {
208            Map<String, Object> superTypeInfos = new HashMap<>();
209            ContentType superType = _contentTypeExtensionPoint.getExtension(superTypeId);
210            superTypeInfos.put("id", superTypeId);
211            superTypeInfos.put("label", superType.getLabel());
212            superTypeInfos.put("isMixin", superType.isMixin());
213            superTypesInfos.add(superTypeInfos);
214
215        }
216        return superTypesInfos;
217    }
218    
219    private Collection<String> _getAttributeNames(Collection<? extends ModelItem> modelItems)
220    {
221        Collection<String> attributeNames = new HashSet<>();
222        
223        for (ModelItem modelItem : modelItems)
224        {
225            attributeNames.add(modelItem.getName());
226            if (modelItem instanceof ModelItemGroup)
227            {
228                attributeNames.addAll(_getAttributeNames(((ModelItemGroup) modelItem).getModelItems()));
229            }
230        }
231        
232        return attributeNames;
233    }
234
235    /**
236     * Get model items' information of a content type
237     * @param contentType The content type
238     * @param hideInheritedModelItems <code>true</code> to hide inherited mode items of content type
239     * @param resolveMultilingualString If <code>true</code>, the multilingual string will be returned as simple strings
240     * @return Model items' information of a content type
241     */
242    public List<Map<String, Object>> getModelItemsInformation(ContentType contentType, boolean hideInheritedModelItems, boolean resolveMultilingualString)
243    {
244        List<Map<String, Object>> modelItems = new ArrayList<>();
245        for (ModelItem modelItem : contentType.getModelItems())
246        {
247            if (!hideInheritedModelItems || modelItem.getModel().equals(contentType))
248            {
249                modelItems.add(_getModelItemValues(contentType, modelItem, resolveMultilingualString));
250            }
251        }
252        return modelItems;
253    }
254
255    private Map<String, Object> _getModelItemValues(ContentType contentType, ModelItem modelItem, boolean resolveMultilingualString)
256    {
257        Map<String, Object> values = new HashMap<>();
258        
259        values.put("dataType", ContentTypeAttributeDataType.METADATA.name().toLowerCase());
260        
261        values.put("label", getEnhancedMultilingualString(modelItem.getLabel(), resolveMultilingualString));
262        values.put("description", getEnhancedMultilingualString(modelItem.getDescription(), resolveMultilingualString));
263        
264        values.put("name", modelItem.getName());
265        values.put("path", modelItem.getPath());
266        values.put("type", modelItem.getType().getId());
267        
268        values.put("contentTypeId", contentType.getId());
269        ContentType referenceContentType = (ContentType) modelItem.getModel();
270        values.put("referenceContentTypeId", referenceContentType.getId());
271        values.put("referenceContentTypeLabel", referenceContentType.getLabel());
272        values.put("isParentMetadata", !referenceContentType.equals(contentType));
273        
274        if (modelItem instanceof ElementDefinition)
275        {
276            values.putAll(_getElementDefinitionValues((ElementDefinition) modelItem, resolveMultilingualString));
277        }
278        else if (modelItem instanceof RepeaterDefinition)
279        {
280            values.putAll(_getRepeaterValues((RepeaterDefinition) modelItem, resolveMultilingualString));
281        }
282        
283        if (modelItem instanceof ModelItemGroup)
284        {
285            values.put("leaf", false);
286            
287            List<Map<String, Object>> children = new ArrayList<>();
288            for (ModelItem child : ((ModelItemGroup) modelItem).getModelItems())
289            {
290                children.add(_getModelItemValues(contentType, child, resolveMultilingualString));
291            }
292            values.put("children", children);
293        }
294        else
295        {
296            values.put("leaf", true);
297        }
298        
299        return values;
300    }
301    
302    private Map<String, Object> _getElementDefinitionValues(ElementDefinition definition, boolean resolveMultilingualString)
303    {
304        Map<String, Object> values = new HashMap<>();
305
306        values.put("pluginName", definition.getPluginName());
307        
308        values.put("defaultValue", definition.getDefaultValue());
309        values.put("multiple", definition.isMultiple());
310
311        values.put("widget", definition.getWidget());
312        values.put("widgetParams", _getWidgetParameters(definition, resolveMultilingualString));
313
314        values.put("enumerated", definition.getEnumerator() != null);
315        if (definition.getEnumerator() != null)
316        {
317            Enumerator enumerator = definition.getEnumerator();
318            if (enumerator instanceof StaticEnumerator)
319            {
320                // Default enumerator
321                values.put("defaultEnumerator", _getEnhancedEnumerators(definition));
322            }
323            else
324            {
325                // Custom enumerator
326                values.put("customEnumeratorClass", enumerator.getClass().getName());
327                values.put("customEnumeratorConfiguration", _getConfiguration(definition.getEnumeratorConfiguration()));
328            }
329        }
330
331        values.put("validator", definition.getValidator() != null);
332        values.put("mandatory", _isMandatoryElementDefinition(definition));
333        Validator validator = definition.getValidator();
334        if (validator != null)
335        {
336            if (validator.getClass().equals(DefaultValidator.class))
337            {
338                // Default validator
339                values.put("defaultValidator", _getValidatorParameters(definition, resolveMultilingualString));
340            }
341            else
342            {
343                // Custom validator
344                values.put("customValidatorClass", validator.getClass().getName());
345                values.put("customValidatorConfiguration", _getConfiguration(definition.getValidatorConfiguration()));
346            }
347        }
348        
349        if (definition instanceof RichTextAttributeDefinition)
350        {
351            values.putAll(_getRichTextValues((RichTextAttributeDefinition) definition));
352        }
353        else if (definition instanceof ContentAttributeDefinition)
354        {
355            values.putAll(_getContentValues((ContentAttributeDefinition) definition));
356        }
357        
358        return values;
359    }
360
361    private List<Map<String, Object>> _getWidgetParameters(ElementDefinition definition, boolean resolveMultilingualString)
362    {
363        List<Map<String, Object>> widgetParameters = new ArrayList<>();
364
365        Map<String, I18nizableText> widgetParams = definition.getWidgetParameters();
366        if (widgetParams != null)
367        {
368            for (Entry<String, I18nizableText> widgetParam : widgetParams.entrySet())
369            {
370                Map<String, Object> params = new HashMap<>();
371                params.put("label", widgetParam.getKey());
372                params.put("value", getEnhancedMultilingualString(widgetParam.getValue(), resolveMultilingualString));
373                widgetParameters.add(params);
374            }
375        }
376        return widgetParameters;
377    }
378    
379    private List<Map<String, Object>> _getEnhancedEnumerators(ElementDefinition definition)
380    {
381        List<Map<String, Object>> enhancedEnumerators = new ArrayList<>();
382        Enumerator enumerator = definition.getEnumerator();
383        try
384        {
385            Map<Object, I18nizableText> entries = enumerator.getTypedEntries();
386            for (Entry<Object, I18nizableText> entry : entries.entrySet())
387            {
388                I18nizableText enumeratorLabel = entry.getValue();
389                Map<String, Object> enhancedEnumerator = new HashMap<>();
390                enhancedEnumerator.put("label", entry.getKey().toString());
391
392                Map<String, Object> value = new HashMap<>();
393                value.put("isMultilingual", enumeratorLabel.isI18n());
394                if (enumeratorLabel.isI18n())
395                {
396                    value.put("values", _getTextForAvailableLanguages(enumeratorLabel, false));
397                }
398                else
399                {
400                    value.put("values", enumeratorLabel.getLabel());
401                }
402                enhancedEnumerator.put("value", value);
403                enhancedEnumerators.add(enhancedEnumerator);
404            }
405        }
406        catch (Exception e)
407        {
408            getLogger().error("Unable to set values for enumerator " + enumerator.getClass().getName(), e);
409        }
410        return enhancedEnumerators;
411    }
412
413    private boolean _isMandatoryElementDefinition(ElementDefinition definition)
414    {
415        boolean isMandatory = false;
416        Validator validator = definition.getValidator();
417        if (validator != null)
418        {
419            Map<String, Object> configuration = validator.getConfiguration();
420            if (configuration != null)
421            {
422                Object object = configuration.get("mandatory");
423                if (object instanceof Boolean)
424                {
425                    isMandatory = (boolean) object;
426                }
427            }
428        }
429        return isMandatory;
430    }
431
432    private Map<String, Object> _getValidatorParameters(ElementDefinition definition, boolean resolveMultilingualString)
433    {
434        Map<String, Object> validatorParameters = new HashMap<>();
435        Validator validator = definition.getValidator();
436        Map<String, Object> configuration = validator.getConfiguration();
437        Object mandatory = configuration.get("mandatory");
438        if (mandatory.toString().equals("true"))
439        {
440            validatorParameters.put("mandatory", true);
441        }
442        Object regexp = configuration.get("regexp");
443        if (regexp != null)
444        {
445            validatorParameters.put("regexp", regexp.toString());
446        }
447        Object invalidText = configuration.get("invalidText");
448        if (invalidText != null)
449        {
450            validatorParameters.put("invalidText", getEnhancedMultilingualString((I18nizableText) invalidText, resolveMultilingualString));
451        }
452        return validatorParameters;
453    }
454    
455    private String _getConfiguration(Configuration configuration)
456    {
457        String config = "";
458        try
459        {
460            if (configuration != null)
461            {
462                DefaultConfigurationSerializer defaultConfigurationSerializer = new DefaultConfigurationSerializer();
463                config = defaultConfigurationSerializer.serialize(configuration);
464            }
465        }
466        catch (ConfigurationException | SAXException e)
467        {
468            // Ignore
469        }
470        return config;
471    }
472
473    private Map<String, Object> _getRichTextValues(RichTextAttributeDefinition definition)
474    {
475        // TODO manage transformer and richTextOutgoingReferenceExtractor
476        Map<String, Object> values = new HashMap<>();
477        
478        List<SemanticAnnotation> annotations = definition.getSemanticAnnotations();
479        List<Object> annotationsValues = new ArrayList<>();
480        for (SemanticAnnotation annotation : annotations)
481        {
482            Map<String, Object> annotationValues = new HashMap<>();
483            annotationValues.put("id", annotation.getId());
484            annotationValues.put("label", annotation.getLabel());
485            annotationValues.put("description", annotation.getDescription());
486            annotationsValues.add(annotationValues);
487        }
488        values.put("semanticAnnotations", annotationsValues);
489        
490        return values;
491    }
492
493    private Map<String, Object> _getContentValues(ContentAttributeDefinition definition)
494    {
495        Map<String, Object> values = new HashMap<>();
496
497        String linkedContentTypeId = definition.getContentTypeId();
498        if (linkedContentTypeId != null)
499        {
500            Map<String, Object> linkedCTypeInfos = new HashMap<>();
501            linkedCTypeInfos.put("id", linkedContentTypeId);
502            
503            ContentType linkedCType = _contentTypeExtensionPoint.getExtension(linkedContentTypeId);
504            linkedCTypeInfos.put("label", linkedCType.getLabel());
505            linkedCTypeInfos.put("iconGlyph", linkedCType.getIconGlyph());
506
507            values.put("linkedContentType", linkedCTypeInfos);
508        }
509        
510        values.put("invertRelationPath", definition.getInvertRelationPath());
511        values.put("forceInvert", definition.getForceInvert());
512
513        return values;
514    }
515
516    private Map<String, Object> _getRepeaterValues(RepeaterDefinition definition, boolean resolveMultilingualString)
517    {
518        Map<String, Object> values = new HashMap<>();
519        
520        values.put("initializeSize", definition.getInitialSize());
521        values.put("minSize", definition.getMinSize());
522        values.put("maxSize", definition.getMaxSize());
523        
524        values.put("addLabel", getEnhancedMultilingualString(definition.getAddLabel(), resolveMultilingualString));
525        values.put("deleteLabel", getEnhancedMultilingualString(definition.getDeleteLabel(), resolveMultilingualString));
526        values.put("headerLabel", definition.getHeaderLabel());
527
528        return values;
529    }
530
531    private List<Map<String, Object>> _getViews(ContentType contentType, boolean hideInheritedModelItems, boolean resolveMultilingualString)
532    {
533        List<Map<String, Object>> views = new ArrayList<>();
534        for (String name : contentType.getViewNames())
535        {
536            View view = contentType.getView(name);
537            views.add(_getViewValues(contentType, view, hideInheritedModelItems, resolveMultilingualString));
538        }
539        return views;
540    }
541
542    private Map<String, Object> _getViewValues(ContentType contentType, View view, boolean hideInheritedModelItems, boolean resolveMultilingualString)
543    {
544        Map<String, Object> values = new HashMap<>();
545        values.put("dataType", ContentTypeAttributeDataType.METADATA_SET.name().toLowerCase());
546        
547        values.put("name", view.getName());
548        values.put("label", getEnhancedMultilingualString(view.getLabel(), resolveMultilingualString));
549        values.put("description", getEnhancedMultilingualString(view.getDescription(), resolveMultilingualString));
550        values.put("isInternal", view.isInternal());
551        
552        values.put("iconGlyph", view.getIconGlyph());
553        values.put("iconDecorator", view.getIconDecorator());
554        values.put("smallIcon", view.getSmallIcon());
555        values.put("mediumIcon", view.getMediumIcon());
556        values.put("largeIcon", view.getLargeIcon());
557        if (!view.getIconGlyph().isEmpty())
558        {
559            values.put("iconCls", view.getIconGlyph());
560        }
561        else
562        {
563            values.put("icon", view.getSmallIcon());
564        }
565        
566        values.putAll(_processViewItemContainerChildren(contentType, view, hideInheritedModelItems, resolveMultilingualString));
567        
568        return values;
569    }
570
571    private Map<String, Object> _processViewItemContainerChildren(ContentType contentType, ViewItemContainer viewItemContainer, boolean hideInheritedModelItems, boolean resolveMultilingualString)
572    {
573        Map<String, Object> values = new HashMap<>();
574        
575        List<Map<String, Object>> children = new ArrayList<>();
576        for (ViewItem viewItem : viewItemContainer.getViewItems())
577        {
578            if (viewItem instanceof SimpleViewItemGroup)
579            {
580                children.add(_getSimpleViewItemGroupValues(contentType, (SimpleViewItemGroup) viewItem, hideInheritedModelItems, resolveMultilingualString));
581            }
582            else if (viewItem instanceof ModelViewItem)
583            {
584                ModelItem modelItem = ((ModelViewItem) viewItem).getDefinition();
585                if (hideInheritedModelItems && contentType.equals(modelItem.getModel())
586                    || !hideInheritedModelItems)
587                {
588                    children.add(_getModelViewItemValues(contentType, (ModelViewItem) viewItem, hideInheritedModelItems, resolveMultilingualString));
589                }
590            }
591        }
592        
593        values.put("leaf", children.isEmpty());
594        values.put("children", children);
595        
596        return values;
597    }
598
599    private Map<String, Object> _getSimpleViewItemGroupValues(ContentType contentType, SimpleViewItemGroup fieldset, boolean hideInheritedModelItems, boolean resolveMultilingualString)
600    {
601        Map<String, Object> values = new HashMap<>();
602        values.put("dataType", ContentTypeAttributeDataType.FIELDSET.name().toLowerCase());
603
604        values.put("label", getEnhancedMultilingualString(fieldset.getLabel(), resolveMultilingualString));
605        values.put("role", fieldset.getRole());
606        
607        values.putAll(_processViewItemContainerChildren(contentType, fieldset, hideInheritedModelItems, resolveMultilingualString));
608
609        return values;
610    }
611
612    private Map<String, Object> _getModelViewItemValues(ContentType contentType, ModelViewItem modelViewItem, boolean hideInheritedModelItems, boolean resolveMultilingualString)
613    {
614        Map<String, Object> values = new HashMap<>();
615        
616        values.put("dataType", ContentTypeAttributeDataType.METADATA_REF.name().toLowerCase());
617        
618        ModelItem modelItem = modelViewItem.getDefinition();
619        values.put("label", getEnhancedMultilingualString(modelItem.getLabel(), resolveMultilingualString));
620        values.put("name", modelItem.getName());
621        values.put("path", modelItem.getPath());
622        
623        boolean isParentView = Optional.ofNullable(modelItem).map(item -> !contentType.equals(item.getModel())).orElse(true);
624        values.put("isParentMetadataSet", isParentView);
625        
626        values.put("isRepeaterOrComposite", modelItem instanceof RepeaterDefinition || modelItem instanceof CompositeDefinition);
627        
628        if (modelViewItem instanceof ViewItemContainer)
629        {
630            values.putAll(_processViewItemContainerChildren(contentType, (ViewItemContainer) modelViewItem, hideInheritedModelItems, resolveMultilingualString));
631        }
632        else
633        {
634            values.put("leaf", true);
635        }
636        
637        return values;
638    }
639
640    private List<Map<String, Object>> _getIndexingModel(ContentType cType)
641    {
642        List<Map<String, Object>> result = new ArrayList<>();
643        IndexingModel indexingModel = cType.getIndexingModel();
644        Collection<IndexingField> fields = indexingModel.getFields();
645        for (IndexingField indexingField : fields)
646        {
647            result.add(_getIndexingFieldDetails(indexingField));
648        }
649        return result;
650    }
651
652    private List<String> _getIndexingFieldPath(IndexingField indexingField)
653    {
654        List<String> indexingFieldPath = new ArrayList<>();
655        if (indexingField instanceof CustomIndexingField)
656        {
657            indexingFieldPath = null;
658        }
659        else if (indexingField instanceof MetadataIndexingField)
660        {
661            MetadataIndexingField metadataIndexingField = (MetadataIndexingField) indexingField;
662            String metadataPath = metadataIndexingField.getMetadataPath();
663            indexingFieldPath.add(metadataPath);
664        }
665        return indexingFieldPath;
666    }
667
668    private I18nizableText _getIndexingFieldType(IndexingField indexingField)
669    {
670        I18nizableText type = new I18nizableText("plugin." + _pluginName, "PLUGINS_CONTENTTYPESEDITOR_EDITOR_TOOL_INDEXING_MODEL_METADATA_TYPE");
671        if (indexingField instanceof CustomIndexingField)
672        {
673            type = new I18nizableText("plugin." + _pluginName, "PLUGINS_CONTENTTYPESEDITOR_EDITOR_TOOL_INDEXING_MODEL_CUSTOM_METADATA_TYPE");
674        }
675        return type;
676    }
677
678    private Map<String, Object> _getIndexingFieldDetails(IndexingField indexingField)
679    {
680        Map<String, Object> values = new HashMap<>();
681        values.put("label", indexingField.getLabel());
682        values.put("name", indexingField.getName());
683        values.put("description", indexingField.getDescription());
684        values.put("dataType", ContentTypeAttributeDataType.INDEXING_FIELD.name().toLowerCase());
685
686        List<String> paths = _getIndexingFieldPath(indexingField);
687        values.put("path", paths);
688        if (indexingField instanceof CustomIndexingField)
689        {
690            values.put("class", indexingField.getClass());
691        }
692        I18nizableText type = _getIndexingFieldType(indexingField);
693        values.put("type", type);
694        values.put("leaf", true);
695        return values;
696    }
697
698    /**
699     * Get values in different language of an i18n text
700     * @param text An i18n text
701     * @param resolveMultilingualString When true return a text
702     * @return values in different language of an i18n text
703     */
704    public Object getEnhancedMultilingualString(I18nizableText text, boolean resolveMultilingualString)
705    {
706        if (resolveMultilingualString)
707        {
708            return text;
709        }
710        else
711        {
712            Map<String, Object> translatedText = new HashMap<>();
713            if (text != null)
714            {
715                if (text.isI18n())
716                {
717                    Map<String, String> textForAvailableLanguages = _getTextForAvailableLanguages(text, false);
718                    if (!textForAvailableLanguages.isEmpty())
719                    {
720                        translatedText.put("isMultilingual", true);
721                        translatedText.put("values", textForAvailableLanguages);
722                    }
723                }
724                else
725                {
726                    String label = text.getLabel();
727                    if (StringUtils.isNotBlank(label))
728                    {
729                        translatedText.put("isMultilingual", false);
730                        translatedText.put("values", label);
731                    }
732                }
733            }
734            return translatedText;
735        }
736    }
737
738    /**
739     * Get translations of a text for each available languages
740     * @param text An i18n text
741     * @param useRawValues true to use the raw values
742     * @return All translations of a text
743     */
744    private Map<String, String> _getTextForAvailableLanguages(I18nizableText text, boolean useRawValues)
745    {
746        Map<String, String> translatedTexts = new HashMap<>();
747        for (Entry<String, Language> avaibleLanguage : _availableLanguages.entrySet())
748        {
749            String language = avaibleLanguage.getKey();
750            String translatedText = _i18nUtils.translate(text, language, useRawValues);
751            if (translatedText != null)
752            {
753                translatedTexts.put(language, translatedText);
754            }
755        }
756        return translatedTexts;
757    }
758}