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