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