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