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