001/*
002 *  Copyright 2020 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.web.parameters.view;
017
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.LinkedHashMap;
021import java.util.LinkedList;
022import java.util.List;
023import java.util.Map;
024import java.util.Optional;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.commons.lang.StringUtils;
031import org.apache.commons.lang3.RegExUtils;
032import org.apache.jackrabbit.util.Text;
033
034import org.ametys.cms.contenttype.ContentType;
035import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
036import org.ametys.cms.contenttype.ContentTypesHelper;
037import org.ametys.cms.repository.Content;
038import org.ametys.plugins.repository.AmetysObject;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.RepositoryConstants;
041import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
042import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
043import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelAwareDataHolder;
044import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData;
045import org.ametys.plugins.repository.model.RepeaterDefinition;
046import org.ametys.runtime.config.DisableCondition;
047import org.ametys.runtime.config.DisableCondition.OPERATOR;
048import org.ametys.runtime.config.DisableConditions;
049import org.ametys.runtime.i18n.I18nizableText;
050import org.ametys.runtime.model.ElementDefinition;
051import org.ametys.runtime.model.Enumerator;
052import org.ametys.runtime.model.Model;
053import org.ametys.runtime.model.ModelItem;
054import org.ametys.runtime.model.StaticEnumerator;
055import org.ametys.runtime.model.View;
056import org.ametys.runtime.model.ViewElement;
057import org.ametys.runtime.model.ViewItem;
058import org.ametys.runtime.model.ViewItemContainer;
059import org.ametys.runtime.model.type.ModelItemTypeConstants;
060import org.ametys.runtime.parameter.DefaultValidator;
061import org.ametys.runtime.plugin.component.AbstractLogEnabled;
062import org.ametys.web.parameters.view.GlobalViewParametersManager.ViewParametersType;
063import org.ametys.web.repository.page.ModifiableZoneItem;
064import org.ametys.web.repository.page.Page;
065import org.ametys.web.repository.page.Page.PageType;
066import org.ametys.web.repository.page.Zone;
067import org.ametys.web.repository.page.ZoneItem;
068import org.ametys.web.repository.page.ZoneItem.ZoneType;
069import org.ametys.web.service.Service;
070import org.ametys.web.service.ServiceExtensionPoint;
071import org.ametys.web.skin.Skin;
072import org.ametys.web.skin.SkinTemplate;
073import org.ametys.web.skin.SkinTemplateZone;
074import org.ametys.web.skin.SkinsManager;
075
076/**
077 * Manager for view parameters
078 */
079public class ViewParametersManager extends AbstractLogEnabled implements Component, Serviceable              
080{
081    /** Avalon Role */
082    public static final String ROLE = ViewParametersManager.class.getName();
083    
084    /** 
085     * The name of the model item to disable inheritance for template
086     * This model item is created from scratch, the name doesn't matter
087     * */
088    public static final String TEMPLATE_INHERIT_MODEL_ITEM_NAME = "template-inheritance";              
089    
090    /** 
091     * The name of the model item to disable inheritance for zone
092     * This model item is created from scratch, the name doesn't matter
093     * */
094    public static final String ZONE_INHERIT_MODEL_ITEM_NAME = "zone-inheritance";              
095    
096    /** 
097     * The name of the model item containing the content view parameters 
098     * This model item is created from scratch, the name doesn't matter
099     * */
100    public static final String CONTENT_VIEW_MODEL_ITEM_NAME = "content-views";              
101    
102    /** 
103     * The name of default attribute in the service parameters to set the service view  
104     * Warning : it's a convention to call this parameter 'xslt', but a service can't have several view xsl with different names
105     * */
106    public static final String SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME = "xslt";  
107    
108    /** The name of the tag containing the template view parameters */
109    public static final String VIEW_PARAMETERS_TEMPLATE_CONF_NAME = "parameters";              
110    
111    /** The name of the tag containing the zone view parameters */
112    public static final String VIEW_PARAMETERS_ZONE_CONF_NAME = "parameters";              
113    
114    /** The name of the parent tag containing the zone item view parameters */
115    public static final String VIEW_PARAMETERS_ZONE_ITEM_PARENT_CONF_NAME = "zone-items";              
116    
117    /** The name of the tag containing the zone item view parameters */
118    public static final String VIEW_PARAMETERS_ZONE_ITEM_CONF_NAME = "parameters";              
119    
120    /** The name of the tag containing the service view parameters */
121    public static final String VIEW_PARAMETERS_SERVICE_CONF_NAME = "parameters";              
122    
123    /** The name of the tag containing the content view parameters */
124    public static final String VIEW_PARAMETERS_CONTENT_CONF_NAME = "parameters";              
125    
126    /** The composite name for view parameters */
127    public static final String VIEW_PARAMETERS_COMPOSITE_NAME = "view_parameters";              
128    
129    /** The composite name for content view parameters */
130    public static final String CONTENT_VIEW_PARAMETERS_COMPOSITE_NAME = "content_view_parameters";              
131    
132    /** The composite name for service view parameters */
133    public static final String SERVICE_VIEW_PARAMETERS_COMPOSITE_NAME = "service_view_parameters";              
134    
135    /** The node type for view parameters */
136    public static final String VIEW_PARAMETERS_NODETYPE = RepositoryConstants.NAMESPACE_PREFIX + ":compositeMetadata";
137    
138    /** The Ametys object resolver */
139    protected AmetysObjectResolver _resolver;
140    
141    /** The skins manager */
142    protected SkinsManager _skinsManager;
143    
144    /** The service view parameters manager */
145    protected ServiceViewParametersManager _serviceViewParametersManager;
146    
147    /** The content view parameters manager */
148    protected ContentViewParametersManager _contentViewParametersManager;
149    
150    /** The global view parameters manager */
151    protected GlobalViewParametersManager _globalViewParametersManager;
152    
153    /** The content types helper */
154    protected ContentTypesHelper _contentTypesHelper;
155    
156    /** The content type extension point */
157    protected ContentTypeExtensionPoint _contentTypeEP;
158    
159    /** The view parameters type extension point */
160    protected ViewParameterTypeExtensionPoint _viewParametersTypeEP;
161    
162    /** The service extension point */
163    protected ServiceExtensionPoint _serviceEP;
164    
165    public void service(ServiceManager manager) throws ServiceException
166    {
167        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
168        _skinsManager = (SkinsManager) manager.lookup(SkinsManager.ROLE);
169        _serviceViewParametersManager = (ServiceViewParametersManager) manager.lookup(ServiceViewParametersManager.ROLE);
170        _contentViewParametersManager = (ContentViewParametersManager) manager.lookup(ContentViewParametersManager.ROLE);
171        _globalViewParametersManager = (GlobalViewParametersManager) manager.lookup(GlobalViewParametersManager.ROLE);
172        _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
173        _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
174        _viewParametersTypeEP = (ViewParameterTypeExtensionPoint) manager.lookup(ViewParameterTypeExtensionPoint.ROLE);
175        _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE);
176    }
177    
178    /**
179     * Get the view parameters holder
180     * @param parentJCRData the parent JCR data
181     * @param viewParametersModel the view parameters model
182     * @return the view parameters holder
183     */
184    public ModifiableModelAwareDataHolder getParametersHolder(JCRRepositoryData parentJCRData, ViewParametersModel viewParametersModel)
185    {
186        JCRRepositoryData viewRepositoryData = _getJCRRepositoryData(parentJCRData, VIEW_PARAMETERS_COMPOSITE_NAME);
187        return new DefaultModifiableModelAwareDataHolder(viewRepositoryData, viewParametersModel);
188    }
189    
190    /**
191     * Get the content view parameters holder
192     * @param parentJCRData the parent JCR data
193     * @param viewName the view name
194     * @param viewParametersModel the view parameters model
195     * @return the view parameters holder
196     */
197    public ModifiableModelAwareDataHolder getContentViewParametersHolder(JCRRepositoryData parentJCRData, String viewName, ViewParametersModel viewParametersModel)
198    {
199        JCRRepositoryData viewParametersRepositoryData = _getJCRRepositoryData(parentJCRData, CONTENT_VIEW_PARAMETERS_COMPOSITE_NAME);
200        JCRRepositoryData viewRepositoryData = _getJCRRepositoryData(viewParametersRepositoryData, viewName);
201        
202        return new DefaultModifiableModelAwareDataHolder(viewRepositoryData, viewParametersModel);
203    }
204    
205    /**
206     * Get the service view parameters holder
207     * @param parentJCRData the parent JCR data
208     * @param viewName the view name
209     * @param viewParametersModel the view parameters model
210     * @return the view parameters holder
211     */
212    public ModifiableModelAwareDataHolder getServiceViewParametersHolder(JCRRepositoryData parentJCRData, String viewName, ViewParametersModel viewParametersModel)
213    {
214        JCRRepositoryData viewParametersRepositoryData = _getJCRRepositoryData(parentJCRData, SERVICE_VIEW_PARAMETERS_COMPOSITE_NAME);
215        JCRRepositoryData xsltRepositoryData = _getJCRRepositoryData(viewParametersRepositoryData, SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
216        JCRRepositoryData viewRepositoryData = _getJCRRepositoryData(xsltRepositoryData, Text.escapeIllegalJcrChars(viewName));
217        
218        return new DefaultModifiableModelAwareDataHolder(viewRepositoryData, viewParametersModel);
219    }
220    
221    private JCRRepositoryData _getJCRRepositoryData(JCRRepositoryData parentJCRData, String jcrName)
222    {
223        JCRRepositoryData jcrData = null;
224        if (parentJCRData.hasValue(jcrName))
225        {
226            jcrData = (JCRRepositoryData) parentJCRData.getRepositoryData(jcrName);
227        }
228        else
229        {
230            jcrData = (JCRRepositoryData) parentJCRData.addRepositoryData(jcrName, VIEW_PARAMETERS_NODETYPE);
231        }
232        
233        return jcrData;
234    }
235    
236    /**
237     * Get the template view parameters model
238     * @param skinId the skin id
239     * @param templateId the template id
240     * @return the view parameters
241     */
242    public Optional<ViewParametersModel> getTemplateViewParametersModel(String skinId, String templateId)
243    {
244        Skin skin = _skinsManager.getSkin(skinId);
245        SkinTemplate skinTemplate = skin.getTemplate(templateId);
246        return skinTemplate != null ? getTemplateViewParametersModel(skinTemplate) : Optional.empty();
247    }
248    
249    /**
250     * Get the template view parameters model
251     * @param skinTemplate the skin template
252     * @return the view parameters
253     */
254    public Optional<ViewParametersModel> getTemplateViewParametersModel(SkinTemplate skinTemplate)
255    {
256        Optional<ViewParametersModel> optionalViewParameters = skinTemplate.getViewParameters();
257        if (optionalViewParameters.isPresent())
258        {
259            ViewParametersModel viewParameters = optionalViewParameters.get();
260            if (viewParameters.isNotEmpty())
261            {
262                ViewParameter<Boolean> inheritCheckboxElement = getInheritCheckboxElement(viewParameters, TEMPLATE_INHERIT_MODEL_ITEM_NAME);
263                viewParameters.addModelItem(inheritCheckboxElement);
264            }
265        }
266        
267        return optionalViewParameters;
268    }
269    
270    /**
271     * Get the zone view parameters model
272     * @param skinId the skin id
273     * @param templateId the template id
274     * @param zoneId the zone id
275     * @return the view parameters
276     */
277    public Optional<ViewParametersModel> getZoneViewParametersModel(String skinId, String templateId, String zoneId)
278    {
279        Skin skin = _skinsManager.getSkin(skinId);
280        SkinTemplate skinTemplate = skin.getTemplate(templateId);
281        if (skinTemplate == null)
282        {
283            return Optional.empty();
284        }
285        
286        SkinTemplateZone skinTemplateZone = skinTemplate.getZone(zoneId);
287        return skinTemplateZone != null ? getZoneViewParametersModel(skinTemplateZone) : Optional.empty();
288    }
289    
290    /**
291     * Get the zone view parameters model
292     * @param skinTemplateZone the skin template zone
293     * @return the view parameters
294     */
295    public Optional<ViewParametersModel> getZoneViewParametersModel(SkinTemplateZone skinTemplateZone)
296    {
297        Optional<ViewParametersModel> optionalViewParameters = skinTemplateZone.getViewParameters();
298        if (optionalViewParameters.isPresent())
299        {
300            ViewParametersModel viewParameters = optionalViewParameters.get();
301            if (viewParameters.isNotEmpty())
302            {
303                ViewParameter<Boolean> inheritCheckboxElement = getInheritCheckboxElement(viewParameters, ZONE_INHERIT_MODEL_ITEM_NAME);
304                viewParameters.addModelItem(inheritCheckboxElement);
305            }
306        }
307        
308        return optionalViewParameters;
309    }
310    
311    /**
312     * Get the zone item view parameters model
313     * @param zoneItem the zone item
314     * @return the view parameters
315     */
316    public Optional<ViewParametersModel> getZoneItemViewParametersModel(ZoneItem zoneItem)
317    {
318        Zone zone = zoneItem.getZone();
319        Page page = zone.getPage();
320        
321        return getZoneItemViewParametersModel(page.getSite().getSkinId(), page.getTemplate(), zone.getName());
322    }
323    
324    /**
325     * Get the zone item view parameters model
326     * @param skinId the skin id
327     * @param templateId the template id
328     * @param zoneName the zone name
329     * @return the view parameters
330     */
331    public Optional<ViewParametersModel> getZoneItemViewParametersModel(String skinId, String templateId, String zoneName)
332    {
333        Skin skin = _skinsManager.getSkin(skinId);
334        SkinTemplate skinTemplate = skin.getTemplate(templateId);
335        if (skinTemplate == null)
336        {
337            return Optional.empty();
338        }
339        
340        SkinTemplateZone skinTemplateZone = skinTemplate.getZone(zoneName);
341        return skinTemplateZone != null ? skinTemplateZone.getZoneItemViewParameters() : Optional.empty();
342    }
343    
344    /**
345     * Get the content view parameters model
346     * @param zoneItem the zone item
347     * @return the view parameters
348     */
349    public Optional<ViewParametersModel> getContentViewParametersModel(ZoneItem zoneItem)
350    {
351        if (zoneItem.getType() == ZoneType.CONTENT)
352        {
353            String viewName = zoneItem.getViewName();
354            Content content = zoneItem.getContent();
355            String skinId = zoneItem.getZone().getPage().getSite().getSkinId();
356            
357            return getContentViewParametersModel(skinId, content, viewName);
358        }
359        else
360        {
361            throw new IllegalArgumentException("Can't get content view parameters model from a none content zone item");
362        }
363    }
364    
365    /**
366     * Get the content view parameters model
367     * @param skinId the skin id
368     * @param content the content
369     * @param viewName the view name
370     * @return the view parameters
371     */
372    public Optional<ViewParametersModel> getContentViewParametersModel(String skinId, Content content, String viewName)
373    {
374        String contentTypeIdForRendering = _contentTypesHelper.getContentTypeIdForRendering(content);
375        return _contentViewParametersManager.getViewParameters(skinId, contentTypeIdForRendering, viewName);
376    }
377    
378    /**
379     * Get all view parameters model for one content
380     * @param skinId the skin id
381     * @param content the content
382     * @return the map of view parameters model (with the view name)
383     */
384    public Map<String, ViewParametersModel> getContentViewParametersModels(String skinId, Content content)
385    {
386        Map<String, ViewParametersModel> viewParametersModels = new HashMap<>();
387        String contentTypeIdForRendering = _contentTypesHelper.getContentTypeIdForRendering(content);
388        if (_contentTypeEP.hasExtension(contentTypeIdForRendering))
389        {
390            ContentType contentType = _contentTypeEP.getExtension(contentTypeIdForRendering);
391            for (String viewName : contentType.getViewNames(false))
392            {
393                Optional<ViewParametersModel> contentViewParameters = getContentViewParametersModel(skinId, content, viewName);
394                if (contentViewParameters.isPresent())
395                {
396                    viewParametersModels.put(viewName, contentViewParameters.get());
397                }
398            }
399        }
400        return viewParametersModels;
401    }
402    
403    /**
404     * Get a static enumerator with all content view
405     * @param content the content
406     * @return the element definition of the enumerator
407     */
408    public ElementDefinition<String> getContentViewEnumerator(Content content)
409    {
410        ElementDefinition<String> elementDef = new ElementDefinition<>();
411
412        String contentTypeIdForRendering = _contentTypesHelper.getContentTypeIdForRendering(content);
413        ContentType contentType = _contentTypeEP.getExtension(contentTypeIdForRendering);
414        
415        StaticEnumerator<String> enumerator = new StaticEnumerator<>();
416        for (String viewName : contentType.getViewNames(false))
417        {
418            View view = contentType.getView(viewName);
419            enumerator.add(view.getLabel(), viewName);
420        }
421
422        elementDef.setName(CONTENT_VIEW_MODEL_ITEM_NAME);
423        elementDef.setLabel(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_DIALOG_BOX_CONTENT_VIEW_FIELD_LABEL"));
424        elementDef.setEnumerator(enumerator);
425        elementDef.setType(_viewParametersTypeEP.getExtension(ModelItemTypeConstants.STRING_TYPE_ID));
426        elementDef.setMultiple(false);
427        elementDef.setValidator(new DefaultValidator(null, true));
428        
429        return elementDef;
430    }
431    
432    /**
433     * Copy content view parameters of the source zone item to the destination zone item
434     * @param zoneItemSource the source zone item
435     * @param zoneItemDestination the destination zone item
436     */
437    public void copyContentViewParameters(ModifiableZoneItem zoneItemSource, ModifiableZoneItem zoneItemDestination)
438    {
439        Content content = zoneItemSource.getContent();
440        
441        String contentTypeIdForRendering = _contentTypesHelper.getContentTypeIdForRendering(content);
442        ContentType contentType = _contentTypeEP.getExtension(contentTypeIdForRendering);
443        
444        for (String viewName : contentType.getViewNames(false))
445        {
446            zoneItemSource.getContentViewParametersHolder(viewName).copyTo(zoneItemDestination.getContentViewParametersHolder(viewName));
447        }
448    }
449    
450    /**
451     * Get the service view parameters model
452     * @param zoneItem the zone item
453     * @return the view parameters
454     */
455    public Optional<ViewParametersModel> getServiceViewParametersModel(ZoneItem zoneItem)
456    {
457        if (zoneItem.getType() == ZoneType.SERVICE)
458        {
459            ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
460            if (serviceParameters.hasDefinition(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME))
461            {
462                String viewName = serviceParameters.getValue(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
463                String serviceId = zoneItem.getServiceId();
464                String skinId = zoneItem.getZone().getPage().getSite().getSkinId();
465                
466                return getServiceViewParametersModel(skinId, serviceId, viewName);
467            }
468        }
469        else
470        {
471            throw new IllegalArgumentException("Can't get service view parameters model from a none service zone item");
472        }
473        
474        return Optional.empty();
475    }
476    
477    /**
478     * Get the service view parameters model
479     * @param skinId the skin id
480     * @param serviceId the service id 
481     * @param viewName the view name
482     * @return the view parameters
483     */
484    public Optional<ViewParametersModel> getServiceViewParametersModel(String skinId, String serviceId, String viewName)
485    {
486        return _serviceViewParametersManager.getViewParameters(skinId, serviceId, viewName);
487    }
488    
489    /**
490     * Get all view parameters model for one service
491     * @param skinId the skin id
492     * @param serviceId the service id
493     * @return the map of view parameters model (with the view name)
494     */
495    public Map<String, ViewParametersModel> getServiceViewParametersModels(String skinId, String serviceId)
496    {
497        Map<String, ViewParametersModel> viewParametersModels = new HashMap<>();
498        
499        ElementDefinition<String> serviceViewEnumerator = getServiceViewEnumerator(serviceId);
500        if (serviceViewEnumerator != null)
501        {
502            Enumerator<String> enumerator = serviceViewEnumerator.getEnumerator();
503            try
504            {
505                for (String viewName : enumerator.getTypedEntries().keySet())
506                {
507                    Optional<ViewParametersModel> serviceViewParameters = getServiceViewParametersModel(skinId, serviceId, viewName);
508                    if (serviceViewParameters.isPresent())
509                    {
510                        viewParametersModels.put(viewName, serviceViewParameters.get());
511                    }
512                }
513            }
514            catch (Exception e)
515            {
516                getLogger().error("Can't get view list for service '{}'", serviceId, e);
517            }
518        }
519        
520        return viewParametersModels;
521    }
522    
523    /**
524     * Get a service enumerator with all content view
525     * @param serviceId the service id
526     * @return the element definition of the enumerator
527     */
528    @SuppressWarnings("unchecked")
529    public ElementDefinition<String> getServiceViewEnumerator(String serviceId)
530    {
531        Service service = _serviceEP.getExtension(serviceId);
532        
533        Map<String, ModelItem> parameters = service.getParameters();
534        if (parameters.containsKey(SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME))
535        {
536            ElementDefinition<String> xsltDefinition = (ElementDefinition<String>) parameters.get(SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
537            return xsltDefinition;
538        }
539        
540        return null;
541    }
542    
543    /**
544     * Copy service view parameters of the source zone item to the destination zone item
545     * @param zoneItemSource the source zone item
546     * @param zoneItemDestination the destination zone item
547     */
548    public void copyServiceViewParameters(ModifiableZoneItem zoneItemSource, ModifiableZoneItem zoneItemDestination)
549    {
550        String serviceId = zoneItemSource.getServiceId();
551        
552        ElementDefinition<String> serviceViewEnumerator = getServiceViewEnumerator(serviceId);
553        if (serviceViewEnumerator != null)
554        {
555            Enumerator<String> enumerator = serviceViewEnumerator.getEnumerator();
556            try
557            {
558                for (String viewName : enumerator.getTypedEntries().keySet())
559                {
560                    zoneItemSource.getServiceViewParametersHolder(viewName).copyTo(zoneItemDestination.getServiceViewParametersHolder(viewName));
561                }
562            }
563            catch (Exception e)
564            {
565                getLogger().error("Can't get view list for service '{}'. So it can't copy the service view parameters", serviceId, e);
566            }
567        }
568    }
569    
570    /**
571     * Normalize the view name (for service view value with /)
572     * @param viewName the view name
573     * @return the normalized view name
574     */
575    public String normalizeViewName(String viewName)
576    {
577        return RegExUtils.replaceAll(viewName, "/", "\\$");
578    }
579    
580    /**
581     * Add model items to the view item container and add prefix to items name
582     * @param modelItems the model items
583     * @param prefix the prefix
584     * @param viewItemContainer the view item container
585     * @return the list of included model items
586     */
587    public List<ModelItem> includeModelItems(Collection<? extends ModelItem> modelItems, String prefix, ViewItemContainer viewItemContainer)
588    {
589        return includeModelItems(modelItems, prefix, viewItemContainer, Optional.empty());
590    }
591    
592    /**
593     * Add model items to the view item container and add prefix to items name
594     * @param modelItems the model items
595     * @param prefix the prefix
596     * @param viewItemContainer the view item container
597     * @param index index at which the model items have to be included
598     * @return the list of included model items
599     */
600    public List<ModelItem> includeModelItems(Collection<? extends ModelItem> modelItems, String prefix, ViewItemContainer viewItemContainer, Optional<Integer> index)
601    {
602        List<ModelItem> copiedModelItems = new LinkedList<>();
603        int i = index.orElse(viewItemContainer.getViewItems().size());
604        for (ModelItem modelItem : modelItems)
605        {
606            if (modelItem instanceof ElementDefinition)
607            {
608                @SuppressWarnings("unchecked")
609                ElementDefinition modelItemCopy = new ElementDefinition((ElementDefinition) modelItem);
610                modelItemCopy.setDisableConditions(null);
611                modelItemCopy.setName(prefix + modelItem.getName());
612                
613                ViewElement viewElement = new ViewElement();
614                viewElement.setDefinition(modelItemCopy);
615                
616                viewItemContainer.insertViewItem(viewElement, i);
617                i++; // increase the index in order to include the next item after this one
618                
619                copiedModelItems.add(modelItemCopy);
620            }
621        }
622        
623        return copiedModelItems;
624    }
625    
626    /**
627     * Set the a disable condition to all model items. These model items will show if the value of the element (elementName) equal the condition value (conditionValue)
628     * @param elementName the element name
629     * @param conditionValue the condition value
630     * @param modelItems the list of model items
631     */
632    public void setDisableConditions(String elementName, String conditionValue, Collection< ? extends ModelItem> modelItems)
633    {
634        for (ModelItem modelItem : modelItems)
635        {
636            if (modelItem instanceof ElementDefinition)
637            {
638                DisableConditions disableConditions = new DisableConditions();
639                DisableCondition condition = new DisableCondition(elementName, OPERATOR.NEQ, conditionValue);
640                disableConditions.getConditions().add(condition);
641                ((ElementDefinition) modelItem).setDisableConditions(disableConditions);
642            }
643            else if (modelItem instanceof RepeaterDefinition)
644            {
645                // Actually we don't handle repeater
646            }
647        }
648    }
649    
650    /**
651     * Get a check box to override inherit parameters
652     * @param model the model of the element
653     * @param name the name of the check box
654     * @return the element definition of the enumerator
655     */
656    public ViewParameter<Boolean> getInheritCheckboxElement(Model model, String name)
657    {
658        ViewParameter<Boolean> elementDef = new ViewParameter<>();
659
660        elementDef.setName(name);
661        elementDef.setLabel(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_INHERITANCE_CHECKBOX_LABEL"));
662        elementDef.setDescription(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_INHERITANCE_CHECKBOX_DESC"));
663        elementDef.setType(_viewParametersTypeEP.getExtension(ModelItemTypeConstants.BOOLEAN_TYPE_ID));
664        elementDef.setMultiple(false);
665        elementDef.setDefaultValue(false);
666        elementDef.setModel(model);
667        elementDef.setInheritances(null);
668        
669        return elementDef;
670    }
671    
672    /**
673     * Get the global view parameters for the given type and add the view parameters to it
674     * @param skinId the skin id
675     * @param type the type
676     * @param viewParameters the view parameters
677     * @return the new view parameters (with global view parameters)
678     */
679    public Optional<ViewParametersModel> addGlobalViewParameters(String skinId, ViewParametersType type, Optional<ViewParametersModel> viewParameters)
680    {
681        Optional<ViewParametersModel> globalViewParameters = _globalViewParametersManager.getViewParameters(skinId, type);
682        if (globalViewParameters.isEmpty() || !globalViewParameters.get().isNotEmpty())
683        {
684            return viewParameters;
685        }
686        
687        if (viewParameters.isEmpty())
688        {
689            return globalViewParameters;
690        }
691
692        ViewParametersModel newViewParameters = new ViewParametersModel(viewParameters.get().getId(), new View(), new LinkedHashMap<>());
693        View newView = newViewParameters.getView();
694        
695        // Add global view parameters
696        _addViewItems(globalViewParameters.get().getView(), newView);
697        _addParameters(globalViewParameters.get().getModelItems(), newViewParameters);
698        
699        // Add view parameters
700        _addViewItems(viewParameters.get().getView(), newView);
701        _addParameters(viewParameters.get().getModelItems(), newViewParameters);
702        
703        return Optional.ofNullable(newViewParameters);
704    }
705    
706    /**
707     * Get the parameter holder with the effective parameter path for a view parameter of a template, taking into account the inheritance.
708     * The parameter path can be different in case of inheritance. 
709     * @param page the page
710     * @param parameterPath the initial parameter path.
711     * @return the {@link ViewParameterHolder} for this page and parameter
712     */
713    public Optional<ViewParameterHolder> getTemplateViewParameterHolderWithInheritance(Page page, String parameterPath)
714    {
715        Optional<ViewParametersModel> viewParametersModelOptional = getTemplateViewParametersModel(page.getSite().getSkinId(), page.getTemplate());
716        if (viewParametersModelOptional.isEmpty())
717        {
718            // No view parameters model
719            return Optional.empty();
720        }
721        
722        ViewParametersModel viewParametersModel = viewParametersModelOptional.get();
723        if (!viewParametersModel.hasModelItem(parameterPath))
724        {
725            // The parameter path doesn't exist in the model
726            return Optional.empty();
727        }
728        
729        ModelItem modelItem = viewParametersModel.getModelItem(parameterPath);
730        return getTemplateViewParameterHolderWithInheritance(page, modelItem);
731    }
732    
733    /**
734     * Get the parameter holder with the effective parameter path for a view parameter of a template, taking into account the inheritance.
735     * The parameter path can be different in case of inheritance. 
736     * @param page the page
737     * @param modelItem the model item of parameter
738     * @return the {@link ViewParameterHolder} for this page and parameter
739     */
740    public Optional<ViewParameterHolder> getTemplateViewParameterHolderWithInheritance(Page page, ModelItem modelItem)
741    {
742        ModelAwareDataHolder dataHolder = page.getTemplateParametersHolder();
743        
744        if (!(modelItem instanceof ViewParameter))
745        {
746            return _getViewParamaterHolder(dataHolder, modelItem.getPath());
747        }
748        
749        ViewParameter viewParameter = (ViewParameter) modelItem; 
750        
751        boolean overrideInherit = dataHolder != null ? dataHolder.getValue(ViewParametersManager.TEMPLATE_INHERIT_MODEL_ITEM_NAME, false, false) : false;
752        if (!viewParameter.hasInheritances() || overrideInherit)
753        {
754            // The param has no inheritance or the override inherit param is checked, so just take the value
755            return _getViewParamaterHolder(dataHolder, modelItem.getPath());
756        }
757        else if (viewParameter.hasInheritances())
758        {
759            return _getTemplateViewParameterHolderWithInheritance(page, viewParameter, modelItem.getPath());
760        }
761        
762        // No value found
763        return Optional.empty();
764    }
765    
766    /**
767     * Get the parameter holder with the effective parameter path for a view parameter of a template, taking into account the inheritance.
768     * The parameter path can be different in case of inheritance. 
769     * @param page the page
770     * @param viewParameter the view parameter
771     * @param parameterPath the parameter path.
772     * @return the {@link ViewParameterHolder} for this page and parameter
773     */
774    protected Optional<ViewParameterHolder> _getTemplateViewParameterHolderWithInheritance(Page page, ViewParameter viewParameter, String parameterPath)
775    {
776        AmetysObject parent = page.getParent();
777        if (!(parent instanceof Page))
778        {
779            // No parent page
780            return Optional.empty();
781        }
782        
783        Page parentPage = (Page) parent;
784        if (parentPage.getType() != PageType.CONTAINER)
785        {
786            // Just ignore blank page or page with redirection
787            return _getTemplateViewParameterHolderWithInheritance(parentPage, viewParameter, parameterPath);
788        }
789        
790        // Parameter has defined inheritance
791        String parentTemplate = parentPage.getTemplate();
792        Map<String, String> inheritances = viewParameter.getInheritances();
793        boolean isTemplateMatch = false;
794        for (String template : inheritances.keySet())
795        {
796            // All template match || The parent page match
797            if (template == null || parentTemplate.equals(template))
798            {
799                String context = inheritances.get(template);
800                if (context != null) // if null, it means the template is excluded from inheritance 
801                {
802                    isTemplateMatch = true;
803                    String inheritModelItemPath = "*".equals(context) ? viewParameter.getName() : context;
804                    Optional<ViewParameterHolder> templateParameterHolder = getTemplateViewParameterHolderWithInheritance(parentPage, inheritModelItemPath);
805                    if (templateParameterHolder.isPresent())
806                    {
807                        return templateParameterHolder;
808                    }
809                }
810            }
811        }
812        
813        if (!isTemplateMatch)
814        {
815            // No template match inheritance, so just take the holder and the path
816            return _getViewParamaterHolder(page.getTemplateParametersHolder(), parameterPath);
817            
818        }
819        
820        return Optional.empty();
821    }
822    
823    /**
824     * Get the parameter holder with the effective parameter path for a view parameter of a zone, taking into account the inheritance.
825     * The parameter path can be different in case of inheritance. 
826     * @param page the page
827     * @param zone the zone
828     * @param parameterPath the initial parameter path.
829     * @return the {@link ViewParameterHolder} for this page, zone and parameter
830     */
831    public Optional<ViewParameterHolder> getZoneViewParameterHolderWithInheritance(Page page, Zone zone, String parameterPath)
832    {
833        Optional<ViewParametersModel> viewParametersModelOptional = getZoneViewParametersModel(page.getSite().getSkinId(), page.getTemplate(), zone.getName());
834        if (viewParametersModelOptional.isEmpty())
835        {
836            // No view parameters model
837            return Optional.empty();
838        }
839        
840        ViewParametersModel viewParametersModel = viewParametersModelOptional.get();
841        if (!viewParametersModel.hasModelItem(parameterPath))
842        {
843            // The parameter path doesn't exist in the model
844            return Optional.empty();
845        }
846        
847        ModelItem modelItem = viewParametersModel.getModelItem(parameterPath);
848        return getZoneViewParameterHolderWithInheritance(page, zone, modelItem);
849    }
850    
851    /**
852     * Get the parameter holder with the effective parameter path for a view parameter of a zone, taking into account the inheritance.
853     * The parameter path can be different in case of inheritance. 
854     * @param page the page
855     * @param zone the zone
856     * @param modelItem the model item of the parameter
857     * @return the {@link ViewParameterHolder} for this page, zone and parameter
858     */
859    public Optional<ViewParameterHolder> getZoneViewParameterHolderWithInheritance(Page page, Zone zone, ModelItem modelItem)
860    {
861        ModelAwareDataHolder dataHolder = zone.getZoneParametersHolder();
862        
863        if (!(modelItem instanceof ViewParameter))
864        {
865            return _getViewParamaterHolder(dataHolder, modelItem.getPath());
866        }
867        
868        ViewParameter viewParameter = (ViewParameter) modelItem; 
869        boolean overrideInherit = dataHolder != null ? dataHolder.getValue(ViewParametersManager.ZONE_INHERIT_MODEL_ITEM_NAME, false, false) : false;
870        if (!viewParameter.hasInheritances() || overrideInherit)
871        {
872            // The param has no inheritance or the override inherit param is checked, so just take the value
873            return _getViewParamaterHolder(dataHolder, modelItem.getPath());
874        }
875        else if (viewParameter.hasInheritances())
876        {
877            return _getZoneViewParameterHolderWithInheritance(page, zone, viewParameter, modelItem.getPath());
878        }
879            
880        // No value found
881        return Optional.empty();
882    }
883    
884    /**
885     * Get the parameter holder with the effective parameter path for a view parameter of a zone, taking into account the inheritance.
886     * The parameter path can be different in case of inheritance. 
887     * @param page the page
888     * @param zone the zone
889     * @param viewParameter the view parameter
890     * @param parameterPath the parameter path.
891     * @return the {@link ViewParameterHolder} for this page, zone and parameter
892     */
893    protected Optional<ViewParameterHolder> _getZoneViewParameterHolderWithInheritance(Page page, Zone zone, ViewParameter viewParameter, String parameterPath)
894    {
895        AmetysObject parent = page.getParent();
896        if (!(parent instanceof Page))
897        {
898            // No parent page
899            return Optional.empty();
900        }
901        
902        Page parentPage = (Page) parent;
903        if (parentPage.getType() != PageType.CONTAINER)
904        {
905            // Just ignore blank page or page with redirection
906            return _getZoneViewParameterHolderWithInheritance(parentPage, zone, viewParameter, parameterPath);
907        }
908        
909        // Parameter has defined inheritance
910        String parentTemplate = parentPage.getTemplate();
911        Map<String, String> inheritances = viewParameter.getInheritances();
912        boolean isTemplateMatch = false;
913        for (String template : inheritances.keySet())
914        {
915            // All template match || The parent page match
916            if (template == null || parentTemplate.equals(template))
917            {
918                String context = inheritances.get(template);
919                if (context != null) // if null, it means the template is excluded from inheritance 
920                {
921                    isTemplateMatch = true;
922                    
923                    String inheritModelItemPath = viewParameter.getName();
924                    String zoneName = zone.getName();
925                    if (!"*".equals(context))
926                    {
927                        zoneName = StringUtils.substringBefore(context, "->");
928                        inheritModelItemPath = StringUtils.substringAfter(context, "->");
929                    }
930                    
931                    if (parentPage.hasZone(zoneName))
932                    {
933                        Optional<ViewParameterHolder> zoneParameterHolder = getZoneViewParameterHolderWithInheritance(parentPage, parentPage.getZone(zoneName), inheritModelItemPath);
934                        if (zoneParameterHolder.isPresent())
935                        {
936                            return zoneParameterHolder;
937                        }
938                    }
939                }
940            }
941        }
942        
943        if (!isTemplateMatch)
944        {
945            // No template match inheritance, so just take the holder and the path
946            return _getViewParamaterHolder(zone.getZoneParametersHolder(), parameterPath);
947        }
948        
949        return Optional.empty();
950    }
951    
952    /**
953     * Get content view parameters holder from the zone item
954     * @param zoneItem the zone item
955     * @return the data holder
956     */
957    public Optional<ModelAwareDataHolder> getContentViewParametersHolder(ZoneItem zoneItem)
958    {
959        if (zoneItem.getType() == ZoneType.CONTENT)
960        {
961            String viewName = zoneItem.getViewName();
962            return Optional.ofNullable(zoneItem.getContentViewParametersHolder(viewName));
963        }
964        else
965        {
966            throw new IllegalArgumentException("Zone item with id '" + zoneItem.getId() + " is not of the expected type 'content' to get the content view parameters");
967        }
968    }
969    
970    /**
971     * Get service view parameters holder from the zone item
972     * @param zoneItem the zone item
973     * @return the data holder
974     */
975    public Optional<ModelAwareDataHolder> getServiceViewParametersHolder(ZoneItem zoneItem)
976    {
977        ModelAwareDataHolder dataHolder = null;
978        if (zoneItem.getType() == ZoneType.SERVICE)
979        {
980            ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
981            if (serviceParameters.hasDefinition(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME))
982            {
983                String viewName = serviceParameters.getValue(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
984                dataHolder = zoneItem.getServiceViewParametersHolder(viewName);
985            }
986        }
987        else
988        {
989            throw new IllegalArgumentException("Zone item with id '" + zoneItem.getId() + " is not of the expected type 'service' to get the service view parameters");
990        }
991        
992        return Optional.ofNullable(dataHolder);
993    }
994    
995    /**
996     * Get the template parameter value
997     * @param <T> type of the value to retrieve
998     * @param page the page
999     * @param parameterPath the parameter path
1000     * @return the template parameter value. Can be null.
1001     */
1002    public <T extends Object> T getTemplateParameterValue(Page page, String parameterPath)
1003    {
1004        Optional<ViewParameterHolder> templateViewParameterHolder = getTemplateViewParameterHolderWithInheritance(page, parameterPath);
1005        return templateViewParameterHolder.isPresent() ? templateViewParameterHolder.get().getValue() : null;
1006    }
1007    
1008    /**
1009     * Get the zone parameter value
1010     * @param <T> type of the value to retrieve
1011     * @param page the page
1012     * @param zone the zone
1013     * @param parameterPath the parameter path
1014     * @return the zone parameter value. Can be null.
1015     */
1016    public <T extends Object> T getZoneParameterValue(Page page, Zone zone, String parameterPath)
1017    {
1018        Optional<ViewParameterHolder> zoneViewParameterHolder = getZoneViewParameterHolderWithInheritance(page, zone, parameterPath);
1019        return zoneViewParameterHolder.isPresent() ? zoneViewParameterHolder.get().getValue() : null;
1020    }
1021    
1022    /**
1023     * Get the zone item parameter value
1024     * @param <T> type of the value to retrieve
1025     * @param zoneItem the zone item
1026     * @param parameterPath the parameter path
1027     * @return the zone item parameter value. Can be null.
1028     */
1029    public <T extends Object> T getZoneItemParameterValue(ZoneItem zoneItem, String parameterPath)
1030    {
1031        ModelAwareDataHolder zoneItemParametersHolder = zoneItem.getZoneItemParametersHolder();
1032        return zoneItemParametersHolder != null ? zoneItemParametersHolder.getValue(parameterPath) : null;
1033    }
1034    
1035    /**
1036     * Get the content parameter value
1037     * @param <T> type of the value to retrieve
1038     * @param zoneItem the zone item
1039     * @param viewName the content view name
1040     * @param parameterPath the parameter path
1041     * @return the content parameter value. Can be null.
1042     */
1043    public <T extends Object> T getContentParameterValue(ZoneItem zoneItem, String viewName, String parameterPath)
1044    {
1045        ModelAwareDataHolder contentParametersHolder = zoneItem.getContentViewParametersHolder(viewName);
1046        return contentParametersHolder != null ? contentParametersHolder.getValue(parameterPath) : null;
1047    }
1048    
1049    /**
1050     * Get the service parameter value
1051     * @param <T> type of the value to retrieve
1052     * @param zoneItem the zone item
1053     * @param viewName the service view name
1054     * @param parameterPath the parameter path
1055     * @return the service parameter value. Can be null.
1056     */
1057    public <T extends Object> T getServiceParameterValue(ZoneItem zoneItem, String viewName, String parameterPath)
1058    {
1059        ModelAwareDataHolder serviceParametersHolder = zoneItem.getServiceViewParametersHolder(viewName);
1060        return serviceParametersHolder != null ? serviceParametersHolder.getValue(parameterPath) : null;
1061    }
1062    
1063    private void _addViewItems(View sourceView, View destView) 
1064    {
1065        for (ViewItem viewItem : sourceView.getViewItems())
1066        {
1067            if (!destView.hasModelViewItem(viewItem.getName()))
1068            {
1069                destView.addViewItem(viewItem);
1070            }
1071        }
1072    }
1073    
1074    private void _addParameters(Collection<? extends ModelItem> modelItems, ViewParametersModel viewParametersModel) 
1075    {
1076        for (ModelItem item : modelItems)
1077        {
1078            viewParametersModel.addModelItem(item);
1079        }
1080    }
1081    
1082    private Optional<ViewParameterHolder> _getViewParamaterHolder(ModelAwareDataHolder dataHolder, String parameterPath)
1083    {
1084        if (dataHolder == null)
1085        {
1086            // No holder, return null
1087            return Optional.empty();
1088        }
1089        
1090        if (!dataHolder.hasDefinition(parameterPath))
1091        {
1092            getLogger().warn("The attribute '{}' is not defined by the model", parameterPath);
1093            return Optional.empty();
1094        }
1095        
1096        if (!dataHolder.hasValue(parameterPath))
1097        {
1098            // No value, return null
1099            return Optional.empty();
1100        }
1101        
1102        return Optional.of(new ViewParameterHolder(dataHolder, parameterPath));
1103    }
1104    
1105    /**
1106     * Object representing the data holder of the parameter with the path of the parameter in this holder   
1107     */
1108    public static class ViewParameterHolder
1109    {
1110        private ModelAwareDataHolder _dataHolder;
1111        private String _parameterPath;
1112        
1113        /**
1114         * Constructor for view parameter holder
1115         * @param dataHolder the holder containing the parameter
1116         * @param parameterPath the path of the parameter in the holder
1117         */
1118        public ViewParameterHolder(ModelAwareDataHolder dataHolder, String parameterPath)
1119        {
1120            this._dataHolder = dataHolder;
1121            this._parameterPath = parameterPath;
1122        }
1123        
1124        /**
1125         * Get the holder containing the parameter
1126         * @return the holder containing the parameter
1127         */
1128        public ModelAwareDataHolder getDataHolder()
1129        {
1130            return this._dataHolder;
1131        }
1132        
1133        /**
1134         * Get the path of the parameter in the holder
1135         * @return the path of the parameter in the holder
1136         */
1137        public String getPath()
1138        {
1139            return this._parameterPath;
1140        }
1141        
1142        /**
1143         * Get the parameter value
1144         * @param <T> type of the value to retrieve
1145         * @return the parameter value
1146         */
1147        public <T extends Object> T getValue()
1148        {
1149            return this._dataHolder.getValue(this._parameterPath);
1150        }
1151    }
1152}