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.io.IOException;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Optional;
025import java.util.stream.Collectors;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.context.ContextException;
030import org.apache.avalon.framework.context.Contextualizable;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034import org.apache.cocoon.ProcessingException;
035import org.apache.cocoon.components.ContextHelper;
036import org.apache.cocoon.environment.Request;
037import org.apache.commons.collections4.MapUtils;
038import org.apache.commons.lang3.StringUtils;
039
040import org.ametys.cms.repository.Content;
041import org.ametys.core.observation.Event;
042import org.ametys.core.observation.ObservationManager;
043import org.ametys.core.ui.Callable;
044import org.ametys.core.user.CurrentUserProvider;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.repository.UnknownAmetysObjectException;
047import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
048import org.ametys.runtime.i18n.I18nizableText;
049import org.ametys.runtime.i18n.I18nizableTextParameter;
050import org.ametys.runtime.model.DefinitionContext;
051import org.ametys.runtime.model.ElementDefinition;
052import org.ametys.runtime.model.Model;
053import org.ametys.runtime.model.ModelItem;
054import org.ametys.runtime.model.SimpleViewItemGroup;
055import org.ametys.runtime.model.View;
056import org.ametys.runtime.model.ViewElement;
057import org.ametys.runtime.model.ViewItemContainer;
058import org.ametys.runtime.model.ViewItemGroup;
059import org.ametys.runtime.model.disableconditions.DisableCondition.OPERATOR;
060import org.ametys.runtime.plugin.component.AbstractLogEnabled;
061import org.ametys.web.ObservationConstants;
062import org.ametys.web.WebConstants;
063import org.ametys.web.parameters.ParametersManager;
064import org.ametys.web.repository.page.LockablePage;
065import org.ametys.web.repository.page.ModifiableSitemapElement;
066import org.ametys.web.repository.page.ModifiableZone;
067import org.ametys.web.repository.page.ModifiableZoneItem;
068import org.ametys.web.repository.page.SitemapElement;
069import org.ametys.web.repository.page.Zone;
070import org.ametys.web.repository.page.ZoneItem;
071import org.ametys.web.repository.page.ZoneItem.ZoneType;
072import org.ametys.web.repository.site.Site;
073import org.ametys.web.rights.PageRightAssignmentContext;
074import org.ametys.web.service.Service;
075import org.ametys.web.service.ServiceExtensionPoint;
076import org.ametys.web.skin.Skin;
077import org.ametys.web.skin.SkinTemplate;
078import org.ametys.web.skin.SkinTemplateZone;
079import org.ametys.web.skin.SkinsManager;
080
081/**
082 * Manager for view parameters
083 */
084public class ViewParametersDAO extends AbstractLogEnabled implements Component, Serviceable, Contextualizable
085{
086    /** Avalon Role */
087    public static final String ROLE = ViewParametersDAO.class.getName();
088    
089    /** The separator for model item name */
090    public static final String MODEL_ITEM_NAME_SEPARATOR = "$";
091    
092    /** The prefix for template view parameters */
093    public static final String PREFIX_TEMPLATE = "template";
094    
095    /** The prefix for zone view parameters */
096    public static final String PREFIX_ZONE = "zone";
097    
098    /** The prefix for zoneItem view parameters */
099    public static final String PREFIX_ZONE_ITEM = "zoneitem";
100    
101    /** The prefix for content view parameters */
102    public static final String PREFIX_CONTENT = "content";
103    
104    /** The prefix for service view parameters */
105    public static final String PREFIX_SERVICE = "service" + MODEL_ITEM_NAME_SEPARATOR + ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME;
106    
107    /** The Ametys object resolver */
108    protected AmetysObjectResolver _resolver;
109    
110    /** The skins manager */
111    protected SkinsManager _skinsManager;
112    
113    /** The service view parameters manager */
114    protected ServiceViewParametersManager _serviceViewParametersManager;
115    
116    /** The content view parameters manager */
117    protected ContentViewParametersManager _contentViewParametersManager;
118    
119    /** The service extension point */
120    protected ServiceExtensionPoint _serviceEP;
121    
122    /** The parameters manager */
123    protected ParametersManager _parametersManager;
124    
125    /** The observation manager */
126    protected ObservationManager _observationManager;
127    
128    /** The current user provider */
129    protected CurrentUserProvider _currentUserProvider;
130    
131    /** The view parameter manager */
132    protected ViewParametersManager _viewParametersManager;
133
134    /** The Avalon context */
135    protected Context _context;
136    
137    public void service(ServiceManager manager) throws ServiceException
138    {
139        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
140        _skinsManager = (SkinsManager) manager.lookup(SkinsManager.ROLE);
141        _serviceViewParametersManager = (ServiceViewParametersManager) manager.lookup(ServiceViewParametersManager.ROLE);
142        _contentViewParametersManager = (ContentViewParametersManager) manager.lookup(ContentViewParametersManager.ROLE);
143        _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE);
144        _parametersManager = (ParametersManager) manager.lookup(ParametersManager.ROLE);
145        _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE);
146        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
147        _viewParametersManager = (ViewParametersManager) manager.lookup(ViewParametersManager.ROLE);
148    }
149    
150    public void contextualize(Context context) throws ContextException
151    {
152        _context = context;
153    }
154    
155    /**
156     * Get the definition of the created view of all view parameters
157     * @param pageId the page id
158     * @param zoneName the zone name (can be null)
159     * @param zoneItemId the zone item id (can be null)
160     * @return the JSON of view parameters
161     * @throws ProcessingException if a processing error occurred
162     */
163    @Callable(rights = Callable.NO_CHECK_REQUIRED)
164    public Map<String, Object> getViewParametersDefinitions(String pageId, String zoneName, String zoneItemId) throws ProcessingException
165    {
166        Map<String, Object> response = new HashMap<>();
167        View view = _createViewParametersDialogView(pageId, zoneName, zoneItemId);
168        response.put("parameters", view.toJSON(DefinitionContext.newInstance().withEdition(true)));
169        
170        return response;
171    }
172    
173    /**
174     * Create the view of all view parameters
175     * @param pageId the page id
176     * @param zoneName the zone name (can be null)
177     * @param zoneItemId the zone item id (can be null)
178     * @return the view of all view parameters
179     */
180    protected View _createViewParametersDialogView(String pageId, String zoneName, String zoneItemId)
181    {
182        View view = new View();
183        
184        ModifiableSitemapElement page = _getModifiablePage(pageId);
185        Site site = page.getSite();
186        Skin skin = _skinsManager.getSkin(site.getSkinId());
187        
188        SkinTemplate template = skin.getTemplate(page.getTemplate());
189        if (template != null)
190        {
191            _addTemplateViewItem(page, skin, template, view);
192    
193            Optional<ModifiableZoneItem> zoneItem = _getModifiableZoneItem(zoneItemId);
194            Optional<SkinTemplateZone> templateZone = _getSkinTemplateZone(template, zoneName, zoneItem);
195            if (templateZone.isPresent())
196            {
197                SkinTemplateZone skinTemplateZone = templateZone.get();
198                _addZoneViewItem(page, skin, template, skinTemplateZone, view);
199                
200                if (zoneItem.isPresent())
201                {
202                    _addZoneItemViewItem(skin, template, skinTemplateZone, zoneItem.get(), view);
203                }
204            }
205        }
206        
207        return view;
208    }
209
210    /**
211     * Add template view item
212     * @param page the page (to determine inheritance)
213     * @param skin the skin
214     * @param template the template
215     * @param viewItemContainer the container in which add the template view
216     */
217    protected void _addTemplateViewItem(ModifiableSitemapElement page, Skin skin, SkinTemplate template, ViewItemContainer viewItemContainer)
218    {
219        String templateId = template.getId();
220        Optional<ViewParametersModel> templateViewParametersOptional = _viewParametersManager.getTemplateViewParametersModel(skin.getId(), templateId);
221        if (templateViewParametersOptional.isEmpty())
222        {
223            return;
224        }
225        
226        ViewParametersModel templateViewParameters = templateViewParametersOptional.get();
227        if (templateViewParameters.isNotEmpty())
228        {
229            SimpleViewItemGroup group = new SimpleViewItemGroup();
230            group.setName(templateId);
231            
232            Map<String, I18nizableTextParameter> parameters = new HashMap<>();
233            parameters.put("title", template.getLabel());
234            group.setLabel(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_DIALOG_BOX_TEMPLATE_LABEL", parameters));
235            group.setRole(ViewItemGroup.FIELDSET_ROLE);
236            
237            Collection< ? extends ModelItem> inheritModelItems = templateViewParameters.getInheritedModelItems(page);
238
239            List< ? extends ModelItem> noInheritModelItems = templateViewParameters.getModelItems()
240                .stream()
241                .filter(m -> !inheritModelItems.contains(m))
242                .filter(m -> !(m.getName().equals(ViewParametersManager.TEMPLATE_INHERIT_MODEL_ITEM_NAME) && inheritModelItems.isEmpty()))
243                .collect(Collectors.toList());
244            
245            List<ModelItem> includedModelItems = new ArrayList<>();
246            
247            String prefix = PREFIX_TEMPLATE + MODEL_ITEM_NAME_SEPARATOR + template.getId() + MODEL_ITEM_NAME_SEPARATOR;
248            includedModelItems.addAll(_viewParametersManager.includeModelItems(noInheritModelItems, prefix, group));
249
250            if (!inheritModelItems.isEmpty())
251            {
252                List<ModelItem> includedInheritModelItems = _viewParametersManager.includeModelItems(inheritModelItems, prefix, group);
253                includedModelItems.addAll(includedInheritModelItems);
254                _viewParametersManager.setDisableConditions(prefix + ViewParametersManager.TEMPLATE_INHERIT_MODEL_ITEM_NAME, OPERATOR.EQ, "false", includedInheritModelItems);
255            }
256            
257            // Create a model from the included (copied) model items. So the model of the model items is not the one with the original not prefixed items
258            Model.of(PREFIX_TEMPLATE, ROLE, includedModelItems.toArray(new ModelItem[includedModelItems.size()]));
259            
260            viewItemContainer.addViewItem(group);
261        }
262    }
263    
264    /**
265     * Add zone view item
266     * @param page the page (to determine inheritance)
267     * @param skin the skin
268     * @param template the template
269     * @param zone the zone
270     * @param viewItemContainer the container in which add the zone view
271     */
272    protected void _addZoneViewItem(SitemapElement page, Skin skin, SkinTemplate template, SkinTemplateZone zone, ViewItemContainer viewItemContainer)
273    {
274        String zoneId = zone.getId();
275        Optional<ViewParametersModel> zoneViewParametersOptional = _viewParametersManager.getZoneViewParametersModel(skin.getId(), template.getId(), zoneId);
276        if (zoneViewParametersOptional.isEmpty())
277        {
278            return;
279        }
280        
281        ViewParametersModel zoneViewParameters = zoneViewParametersOptional.get();
282        if (zoneViewParameters.isNotEmpty())
283        {
284            SimpleViewItemGroup group = new SimpleViewItemGroup();
285            group.setName(zoneId);
286            
287            Map<String, I18nizableTextParameter> parameters = new HashMap<>();
288            parameters.put("title", zone.getLabel());
289            group.setLabel(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_DIALOG_BOX_ZONE_LABEL", parameters));
290            group.setRole(ViewItemGroup.FIELDSET_ROLE);
291            
292            Collection< ? extends ModelItem> inheritModelItems = zoneViewParameters.getInheritedModelItems(page);
293
294            List< ? extends ModelItem> noInheritModelItems = zoneViewParameters.getModelItems()
295                    .stream()
296                    .filter(m -> !inheritModelItems.contains(m))
297                    .filter(m -> !(m.getName().equals(ViewParametersManager.ZONE_INHERIT_MODEL_ITEM_NAME) && inheritModelItems.isEmpty()))
298                    .collect(Collectors.toList());
299            
300            List<ModelItem> includedModelItems = new ArrayList<>();
301            
302            String prefix = PREFIX_ZONE + MODEL_ITEM_NAME_SEPARATOR + zoneId + MODEL_ITEM_NAME_SEPARATOR;
303            includedModelItems.addAll(_viewParametersManager.includeModelItems(noInheritModelItems, prefix, group));
304            
305            if (!inheritModelItems.isEmpty())
306            {
307                List<ModelItem> includedInheritModelItems = _viewParametersManager.includeModelItems(inheritModelItems, prefix, group);
308                includedModelItems.addAll(includedInheritModelItems);
309                _viewParametersManager.setDisableConditions(prefix + ViewParametersManager.ZONE_INHERIT_MODEL_ITEM_NAME, OPERATOR.EQ, "false", includedInheritModelItems);
310            }
311            
312            // Create a model from the included (copied) model items. So the model of the model items is not the one with the original not prefixed items
313            Model.of(PREFIX_ZONE, ROLE, includedModelItems.toArray(new ModelItem[includedModelItems.size()]));
314            
315            viewItemContainer.addViewItem(group);
316        }
317    }
318    
319    /**
320     * Add zone item view item and the service or content view items
321     * @param skin the skin
322     * @param template the template
323     * @param zone the zone
324     * @param zoneItem the zone item
325     * @param viewItemContainer the container in which add the zone item view
326     */
327    protected void _addZoneItemViewItem(Skin skin, SkinTemplate template, SkinTemplateZone zone, ZoneItem zoneItem, ViewItemContainer viewItemContainer)
328    {
329        ModelItemViewsWrapper modelItemViews = new ModelItemViewsWrapper();
330        
331        String skinId = skin.getId();
332        Optional<ViewParametersModel> zoneItemViewParameters = _viewParametersManager.getZoneItemViewParametersModel(skinId, template.getId(), zone.getId());
333        modelItemViews.setZoneItemViewParameters(zoneItemViewParameters);
334        
335        ZoneType type = zoneItem.getType();
336        if (type == ZoneType.SERVICE)
337        {
338            _addServiceViewParameters(modelItemViews, zoneItem, skinId);
339        }
340        else if (type == ZoneType.CONTENT)
341        {
342            _addContentViewParameters(modelItemViews, zoneItem, skinId);
343        }
344        
345        _addZoneItemViewParameters(modelItemViews, zone, zoneItem, viewItemContainer);
346    }
347    
348    /**
349     * Add all view parameters of the zone item
350     * @param modelItemViews the model item views
351     * @param zone the zone
352     * @param zoneItem the zone item
353     * @param viewItemContainer the container in which add the zone item view
354     */
355    protected void _addZoneItemViewParameters(ModelItemViewsWrapper modelItemViews, SkinTemplateZone zone, ZoneItem zoneItem, ViewItemContainer viewItemContainer)
356    {
357        if (modelItemViews.hasViewParameters())
358        {
359            SimpleViewItemGroup group = new SimpleViewItemGroup();
360            group.setName(zoneItem.getName());
361            group.setLabel(modelItemViews.getLabel());
362            group.setRole(ViewItemGroup.FIELDSET_ROLE);
363
364            if (modelItemViews.hasZoneItemViewParameters())
365            {
366                ViewParametersModel zoneItemViewParameters = modelItemViews.getZoneItemViewParameters().get();
367                
368                SimpleViewItemGroup zoneItemGroup = new SimpleViewItemGroup();
369                zoneItemGroup.setName(zoneItem.getName() + "_zoneItem");
370                
371                String prefix = PREFIX_ZONE_ITEM + MODEL_ITEM_NAME_SEPARATOR + zone.getId() + MODEL_ITEM_NAME_SEPARATOR;
372                List<ModelItem> includedModelItems = _viewParametersManager.includeModelItems(zoneItemViewParameters.getModelItems(), prefix, zoneItemGroup);
373                
374                // Create a model from the included (copied) model items. So the model of the model items is not the one with the original not prefixed items
375                Model.of(PREFIX_ZONE_ITEM, ROLE, includedModelItems.toArray(new ModelItem[includedModelItems.size()]));
376                
377                group.addViewItem(zoneItemGroup);
378            }
379            
380            if (modelItemViews.hasServiceOrContentViewParameters())
381            {
382                SimpleViewItemGroup serviceOrContentGroup = new SimpleViewItemGroup();
383                serviceOrContentGroup.setName(zoneItem.getName() + "_contentOrService");
384                
385                ViewElement viewElement = new ViewElement();
386                ElementDefinition<String> enumeratorViewDefinition = modelItemViews.getEnumeratorViewDefinition();
387                viewElement.setDefinition(enumeratorViewDefinition);
388                serviceOrContentGroup.addViewItem(viewElement);
389                
390                Map<String, ViewParametersModel> serviceOrContentViewParameters = modelItemViews.getServiceOrContentViewParameters();
391                for (String viewName : serviceOrContentViewParameters.keySet())
392                {
393                    ViewParametersModel viewParameters = serviceOrContentViewParameters.get(viewName);
394                    Collection< ? extends ModelItem> modelItems = viewParameters.getModelItems();
395                    
396                    String prefix = modelItemViews.getPrefix() + MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + MODEL_ITEM_NAME_SEPARATOR;
397                    List<ModelItem> includedModelItems = _viewParametersManager.includeModelItems(modelItems, prefix, serviceOrContentGroup);
398                    _viewParametersManager.setDisableConditions(enumeratorViewDefinition.getName(), OPERATOR.NEQ, viewName, includedModelItems);
399                    
400                    // Create a model from the included (copied) model items. So the model of the model items is not the one with the original not prefixed items
401                    Model.of(PREFIX_TEMPLATE, ROLE, includedModelItems.toArray(new ModelItem[includedModelItems.size()]));
402                }
403                
404                group.addViewItem(serviceOrContentGroup);
405            }
406            
407            viewItemContainer.addViewItem(group);
408        }
409    }
410    
411
412    private void _setRequestAttribute(String serviceId, String pageId, String zoneItemId, String zoneName)
413    {
414        Request request = ContextHelper.getRequest(_context);
415        
416        request.setAttribute(WebConstants.REQUEST_ATTR_SERVICE_ID, serviceId);
417        request.setAttribute(WebConstants.REQUEST_ATTR_PAGE_ID, pageId);
418        request.setAttribute(WebConstants.REQUEST_ATTR_ZONEITEM_ID, zoneItemId);
419        request.setAttribute(WebConstants.REQUEST_ATTR_ZONE_NAME, zoneName);
420    }
421    
422    /**
423     * Add service view parameters to the model item views wrapper
424     * @param modelItemViews the model item views wrapper
425     * @param zoneItem the zone item
426     * @param skinId the skin id
427     */
428    protected void _addServiceViewParameters(ModelItemViewsWrapper modelItemViews, ZoneItem zoneItem, String skinId)
429    {
430        // The zone item contains a service
431        String serviceId = zoneItem.getServiceId();
432        Service service = _serviceEP.getExtension(serviceId);
433        
434        Zone zone = zoneItem.getZone();
435        _setRequestAttribute(serviceId, zone.getSitemapElement().getId(), zoneItem.getId(), zone.getName());
436        
437        Map<String, I18nizableTextParameter> parameters = new HashMap<>();
438        parameters.put("title", service.getLabel());
439        modelItemViews.setLabel(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_DIALOG_BOX_SERVICE_LABEL", parameters));
440        modelItemViews.setPrefix(PREFIX_SERVICE);
441        
442        ElementDefinition<String> serviceViewEnumerator = _viewParametersManager.getServiceViewEnumerator(serviceId);
443        if (serviceViewEnumerator != null)
444        {
445            modelItemViews.setEnumeratorViewDefinition(serviceViewEnumerator);
446            
447            Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(skinId, serviceId);
448            for (String viewName : serviceViewParametersModels.keySet())
449            {
450                ViewParametersModel serviceViewParameters = serviceViewParametersModels.get(viewName);
451                modelItemViews.addServiceOrContentViewParameters(viewName, serviceViewParameters);
452            }
453        }
454    }
455    
456    /**
457     * Add content view parameters to the model item views wrapper
458     * @param modelItemViews the model item views wrapper
459     * @param zoneItem the zone item
460     * @param skinId the skin id
461     */
462    protected void _addContentViewParameters(ModelItemViewsWrapper modelItemViews, ZoneItem zoneItem, String skinId)
463    {
464        // The zone item contains a content
465        Content content = zoneItem.getContent();
466        List<String> parameters = new ArrayList<>();
467        parameters.add(content.getTitle());
468        modelItemViews.setLabel(new I18nizableText("plugin.web", "PLUGINS_WEB_VIEW_PARAMETERS_DIALOG_BOX_CONTENT_LABEL", parameters));
469        modelItemViews.setPrefix(PREFIX_CONTENT);
470        
471        ElementDefinition<String> elementDef = _viewParametersManager.getContentViewEnumerator(skinId, content);
472        modelItemViews.setEnumeratorViewDefinition(elementDef);
473        
474        Map<String, ViewParametersModel> contentViewParametersModels = _viewParametersManager.getContentViewParametersModels(skinId, content);
475        for (String viewName : contentViewParametersModels.keySet())
476        {
477            ViewParametersModel contentViewParameters = contentViewParametersModels.get(viewName);
478            modelItemViews.addServiceOrContentViewParameters(viewName, contentViewParameters);
479        }
480    }
481
482    /**
483     * Get the view parameters values
484     * @param pageId the page id
485     * @param zoneName the zone name (Can be null)
486     * @param zoneItemId the zone item id (Can be null)
487     * @return the values
488     */
489    @Callable(rights = "Web_Rights_Page_Configure_View_Parameters", rightContext = PageRightAssignmentContext.ID, paramIndex = 0)
490    public Map<String, Object> getViewParametersValues(String pageId, String zoneName, String zoneItemId)
491    {
492        Map<String, Object> response = new HashMap<>();
493        Map<String, Object> values = new HashMap<>();
494
495        ModifiableSitemapElement page = _getModifiablePage(pageId);
496        Site site = page.getSite();
497        Skin skin = _skinsManager.getSkin(site.getSkinId());
498        
499        SkinTemplate template = skin.getTemplate(page.getTemplate());
500        if (template != null)
501        {
502            values.putAll(_getTemplateViewParametersValues(page, template));
503            
504            Optional<ModifiableZoneItem> zoneItem = _getModifiableZoneItem(zoneItemId);
505            Optional<SkinTemplateZone> templateZone = _getSkinTemplateZone(template, zoneName, zoneItem);
506            if (templateZone.isPresent())
507            {
508                SkinTemplateZone skinTemplateZone = templateZone.get();
509                values.putAll(_getZoneViewParametersValues(page, skinTemplateZone));
510    
511                if (zoneItem.isPresent())
512                {
513                    values.putAll(_getZoneItemViewParametersValues(page, skin, skinTemplateZone, zoneItem.get()));
514                }
515            }
516        }
517        
518        response.put("values", values);
519        return response;
520    }
521    
522    /**
523     * Get the template view parameters values
524     * @param page the page
525     * @param template the template
526     * @return the values
527     */
528    protected Map<String, Object> _getTemplateViewParametersValues(ModifiableSitemapElement page, SkinTemplate template)
529    {
530        Optional<ViewParametersModel> templateViewParameters = _viewParametersManager.getTemplateViewParametersModel(template);
531        if (templateViewParameters.isEmpty())
532        {
533            return MapUtils.EMPTY_SORTED_MAP;
534        }
535        
536        ModifiableModelAwareDataHolder pageViewParametersHolder = page.getTemplateParametersHolder();
537        Map<String, Object> templateValues = _parametersManager.getParametersValues(templateViewParameters.get().getModelItems(), pageViewParametersHolder, "");
538
539        String prefix = PREFIX_TEMPLATE + MODEL_ITEM_NAME_SEPARATOR + template.getId() + MODEL_ITEM_NAME_SEPARATOR;
540        return _parametersManager.addPrefixToParameters(templateValues, prefix);
541    }
542    
543    /**
544     * Get the zone view parameters values
545     * @param page the page
546     * @param skinZone the zone
547     * @return the values
548     */
549    protected Map<String, Object> _getZoneViewParametersValues(ModifiableSitemapElement page, SkinTemplateZone skinZone)
550    {
551        Optional<ViewParametersModel> zoneViewParameters = _viewParametersManager.getZoneViewParametersModel(skinZone);
552        if (zoneViewParameters.isEmpty())
553        {
554            return MapUtils.EMPTY_SORTED_MAP;
555        }
556        
557        String zoneName = skinZone.getId();
558        if (page.hasZone(zoneName))
559        {
560            ModifiableZone zone = page.getZone(zoneName);
561    
562            ModifiableModelAwareDataHolder zoneViewParametersHolder = zone.getZoneParametersHolder();
563            Map<String, Object> zoneValues = _parametersManager.getParametersValues(zoneViewParameters.get().getModelItems(), zoneViewParametersHolder, "");
564            
565            String prefix = PREFIX_ZONE + MODEL_ITEM_NAME_SEPARATOR + zoneName + MODEL_ITEM_NAME_SEPARATOR;
566            return _parametersManager.addPrefixToParameters(zoneValues, prefix);
567        }
568        
569        return MapUtils.EMPTY_SORTED_MAP;
570    }
571    
572    /**
573     * Get the zone item view parameters values
574     * @param page the page
575     * @param skin the skin
576     * @param skinZone the zone
577     * @param zoneItem the zone item
578     * @return the values
579     */
580    protected Map<String, Object> _getZoneItemViewParametersValues(ModifiableSitemapElement page, Skin skin, SkinTemplateZone skinZone, ModifiableZoneItem zoneItem)
581    {
582        Map<String, Object> values = new HashMap<>();
583        
584        ModifiableModelAwareDataHolder zoneItemViewParametersHolder = zoneItem.getZoneItemParametersHolder();
585        Optional<ViewParametersModel> zoneItemViewParameters = skinZone.getZoneItemViewParameters();
586        if (zoneItemViewParameters.isEmpty())
587        {
588            return MapUtils.EMPTY_SORTED_MAP;
589        }
590        
591        Map<String, Object> zoneItemValues = _parametersManager.getParametersValues(zoneItemViewParameters.get().getModelItems(), zoneItemViewParametersHolder, "");
592        String prefix = PREFIX_ZONE_ITEM + MODEL_ITEM_NAME_SEPARATOR + skinZone.getId() + MODEL_ITEM_NAME_SEPARATOR;
593        values.putAll(_parametersManager.addPrefixToParameters(zoneItemValues, prefix));
594        
595        if (zoneItem.getType() == ZoneType.SERVICE)
596        {
597            values.putAll(_getServiceViewParametersValues(skin, zoneItem));
598        }
599        else if (zoneItem.getType() == ZoneType.CONTENT)
600        {
601            values.putAll(_getContentViewParametersValues(skin, zoneItem));
602        }
603        
604        return values;
605    }
606    
607    /**
608     * Get the service view parameters values
609     * @param skin the skin
610     * @param zoneItem the zone item
611     * @return the values
612     */
613    protected Map<String, Object> _getServiceViewParametersValues(Skin skin, ModifiableZoneItem zoneItem)
614    {
615        Map<String, Object> values = new HashMap<>();
616
617        Zone zone = zoneItem.getZone();
618        _setRequestAttribute(zoneItem.getServiceId(), zone.getSitemapElement().getId(), zoneItem.getId(), zone.getName());
619
620        ModifiableModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
621        if (serviceParameters.hasDefinition(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME))
622        {
623            values.put(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME, serviceParameters.getValue(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME));
624        }
625        
626        Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(skin.getId(), zoneItem.getServiceId());
627        for (String viewName : serviceViewParametersModels.keySet())
628        {
629            ViewParametersModel serviceViewParameters = serviceViewParametersModels.get(viewName);
630            ModifiableModelAwareDataHolder serviceViewParametersHolder = zoneItem.getServiceViewParametersHolder(viewName);
631            
632            Map<String, Object> serviceValues = _parametersManager.getParametersValues(serviceViewParameters.getModelItems(), serviceViewParametersHolder, "");
633            String prefix = PREFIX_SERVICE + MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + MODEL_ITEM_NAME_SEPARATOR;
634            values.putAll(_parametersManager.addPrefixToParameters(serviceValues, prefix));
635        }
636        
637        return values;
638    }
639    
640    /**
641     * Get the content view parameters values
642     * @param skin the skin
643     * @param zoneItem the zone item
644     * @return the values
645     */
646    protected Map<String, Object> _getContentViewParametersValues(Skin skin, ModifiableZoneItem zoneItem)
647    {
648        Map<String, Object> values = new HashMap<>();
649        
650        values.put(ViewParametersManager.CONTENT_VIEW_MODEL_ITEM_NAME, zoneItem.getViewName());
651        
652        Map<String, ViewParametersModel> contentViewParametersModels = _viewParametersManager.getContentViewParametersModels(skin.getId(), zoneItem.getContent());
653        for (String viewName : contentViewParametersModels.keySet())
654        {
655            ViewParametersModel contentViewParameters = contentViewParametersModels.get(viewName);
656            ModifiableModelAwareDataHolder contentViewParametersHolder = zoneItem.getContentViewParametersHolder(viewName);
657                
658            Map<String, Object> serviceValues = _parametersManager.getParametersValues(contentViewParameters.getModelItems(), contentViewParametersHolder, "");
659            String prefix = PREFIX_CONTENT + MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + MODEL_ITEM_NAME_SEPARATOR;
660            values.putAll(_parametersManager.addPrefixToParameters(serviceValues, prefix));
661        }
662        
663        return values;
664    }
665    
666    /**
667     * Set view parameters values
668     * @param pageId the page id
669     * @param zoneName the zone name (Can be null)
670     * @param zoneItemId the zone item id (Can be null)
671     * @param parameterValues the parameter values
672     * @return results map
673     * @throws IOException If an error occurred
674     */
675    @Callable(rights = "Web_Rights_Page_Configure_View_Parameters", rightContext = PageRightAssignmentContext.ID, paramIndex = 0)
676    public Map<String, Object> setViewParametersValues(String pageId, String zoneName, String zoneItemId, Map<String, Object> parameterValues) throws IOException
677    {
678        Map<String, Object> results = new HashMap<>();
679        Map<String, List<I18nizableText>> allErrors = new HashMap<>();
680        
681        ModifiableSitemapElement sitemapElement = _getModifiablePage(pageId);
682        
683        if (sitemapElement instanceof LockablePage lockablePage && lockablePage.isLocked())
684        {
685            throw new IllegalArgumentException("Can not change view of a locked page '/" + sitemapElement.getSitemapName() + "/" + sitemapElement.getPathInSitemap() + "'");
686        }
687        
688        Site site = sitemapElement.getSite();
689        Skin skin = _skinsManager.getSkin(site.getSkinId());
690        SkinTemplate template = skin.getTemplate(sitemapElement.getTemplate());
691        
692        if (template != null)
693        {
694            allErrors.putAll(_setTemplateViewParameters(sitemapElement, template, parameterValues));
695    
696            Optional<ModifiableZoneItem> zoneItem = _getModifiableZoneItem(zoneItemId);
697            Optional<SkinTemplateZone> templateZone = _getSkinTemplateZone(template, zoneName, zoneItem);
698            if (templateZone.isPresent())
699            {
700                SkinTemplateZone skinTemplateZone = templateZone.get();
701                allErrors.putAll(_setZoneViewParameters(sitemapElement, skinTemplateZone, parameterValues));
702                
703                if (zoneItem.isPresent())
704                {
705                    allErrors.putAll(_setZoneItemViewParameters(sitemapElement, skin, skinTemplateZone, zoneItem.get(), parameterValues));
706                }
707            }
708        }
709        
710        if (!allErrors.isEmpty())
711        {
712            results.put("errors", allErrors);
713            return results;
714        }
715        
716        sitemapElement.saveChanges();
717        
718        Map<String, Object> eventParams = new HashMap<>();
719        eventParams.put(ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement);
720        _observationManager.notify(new Event(ObservationConstants.EVENT_VIEW_PARAMETERS_MODIFIED, _currentUserProvider.getUser(), eventParams));
721        
722        return results;
723    }
724    
725    /**
726     * Set template view parameters values
727     * @param page the page
728     * @param template the template
729     * @param parameterValues the parameter values
730     * @return the errors
731     */
732    protected Map<String, List<I18nizableText>> _setTemplateViewParameters(ModifiableSitemapElement page, SkinTemplate template, Map<String, Object> parameterValues)
733    {
734        Optional<ViewParametersModel> templateViewParameters = _viewParametersManager.getTemplateViewParametersModel(template);
735        if (templateViewParameters.isEmpty())
736        {
737            return MapUtils.EMPTY_SORTED_MAP;
738        }
739        
740        String templatePrefix = PREFIX_TEMPLATE + MODEL_ITEM_NAME_SEPARATOR + template.getId() + MODEL_ITEM_NAME_SEPARATOR;
741        Map<String, Object> templateParameters = _parametersManager.getParametersStartWithPrefix(parameterValues, templatePrefix);
742        
743        // Do this because can't set null value to boolean model item. FIXME CMS-10275
744        if (!templateParameters.containsKey(ViewParametersManager.TEMPLATE_INHERIT_MODEL_ITEM_NAME))
745        {
746            templateParameters.put(ViewParametersManager.TEMPLATE_INHERIT_MODEL_ITEM_NAME, true);
747        }
748        
749        ModifiableModelAwareDataHolder pageViewParametersHolder = page.getTemplateParametersHolder();
750        return _parametersManager.setParameterValues(pageViewParametersHolder, templateViewParameters.get().getModelItems(), templateParameters);
751    }
752    
753    /**
754     * Set zone view parameters values
755     * @param page the page
756     * @param skinZone the zone
757     * @param parameterValues the parameter values
758     * @return the errors
759     */
760    protected Map<String, List<I18nizableText>> _setZoneViewParameters(ModifiableSitemapElement page, SkinTemplateZone skinZone, Map<String, Object> parameterValues)
761    {
762        Optional<ViewParametersModel> zoneViewParameters = _viewParametersManager.getZoneViewParametersModel(skinZone);
763        if (zoneViewParameters.isEmpty())
764        {
765            return MapUtils.EMPTY_SORTED_MAP;
766        }
767
768        String zoneName = skinZone.getId();
769        ModifiableZone zone = page.hasZone(zoneName) ? page.getZone(zoneName) : page.createZone(zoneName);
770        
771        String zonePrefix = PREFIX_ZONE + MODEL_ITEM_NAME_SEPARATOR + zoneName + MODEL_ITEM_NAME_SEPARATOR;
772        Map<String, Object> zoneParameters = _parametersManager.getParametersStartWithPrefix(parameterValues, zonePrefix);
773        
774        // Do this because can't set null value to boolean model item. FIXME CMS-10275
775        if (!zoneParameters.containsKey(ViewParametersManager.ZONE_INHERIT_MODEL_ITEM_NAME))
776        {
777            zoneParameters.put(ViewParametersManager.ZONE_INHERIT_MODEL_ITEM_NAME, true);
778        }
779        
780        ModifiableModelAwareDataHolder zoneViewParametersHolder = zone.getZoneParametersHolder();
781        return _parametersManager.setParameterValues(zoneViewParametersHolder, zoneViewParameters.get().getModelItems(), zoneParameters);
782    }
783    
784    /**
785     * Set zone item view parameters values
786     * @param page the page
787     * @param skin the skin
788     * @param skinZone the zone
789     * @param zoneItem the zone item
790     * @param parameterValues the parameter values
791     * @return the errors
792     */
793    protected Map<String, List<I18nizableText>> _setZoneItemViewParameters(ModifiableSitemapElement page, Skin skin, SkinTemplateZone skinZone, ModifiableZoneItem zoneItem, Map<String, Object> parameterValues)
794    {
795        Map<String, List<I18nizableText>> allErrors = new HashMap<>();
796        
797        Optional<ViewParametersModel> zoneItemViewParameters = skinZone.getZoneItemViewParameters();
798        if (zoneItemViewParameters.isEmpty())
799        {
800            return allErrors;
801        }
802        
803        ModifiableModelAwareDataHolder zoneItemViewParametersHolder = zoneItem.getZoneItemParametersHolder();
804        
805        String zoneItemPrefix = PREFIX_ZONE_ITEM + MODEL_ITEM_NAME_SEPARATOR + skinZone.getId() + MODEL_ITEM_NAME_SEPARATOR;
806        Map<String, Object> zoneItemParameters = _parametersManager.getParametersStartWithPrefix(parameterValues, zoneItemPrefix);
807        
808        allErrors.putAll(_parametersManager.setParameterValues(zoneItemViewParametersHolder, zoneItemViewParameters.get().getModelItems(), zoneItemParameters));
809        
810        if (zoneItem.getType() == ZoneType.SERVICE)
811        {
812            allErrors.putAll(_setServiceViewParameters(skin, zoneItem, parameterValues));
813        }
814        else if (zoneItem.getType() == ZoneType.CONTENT)
815        {
816            allErrors.putAll(_setContentViewParameters(skin, zoneItem, parameterValues));
817        }
818        
819        return allErrors;
820    }
821    
822    /**
823     * Set service view parameters values
824     * @param skin the skin
825     * @param zoneItem the zone item
826     * @param parameterValues the parameter values
827     * @return the errors
828     */
829    protected Map<String, List<I18nizableText>> _setServiceViewParameters(Skin skin, ModifiableZoneItem zoneItem, Map<String, Object> parameterValues)
830    {
831        String viewName = (String) parameterValues.get(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
832        if (StringUtils.isEmpty(viewName))
833        {
834            // We have a service with no view parameters
835            return MapUtils.EMPTY_SORTED_MAP;
836        }
837        
838        String serviceId = zoneItem.getServiceId();
839        ModifiableModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
840        serviceParameters.setValue(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME, viewName);
841        
842        Optional<ViewParametersModel> serviceViewParameters = _viewParametersManager.getServiceViewParametersModel(skin.getId(), serviceId, viewName);
843        if (serviceViewParameters.isEmpty())
844        {
845            return MapUtils.EMPTY_SORTED_MAP;
846        }
847        
848        ModifiableModelAwareDataHolder serviceViewParametersHolder = zoneItem.getServiceViewParametersHolder(viewName);
849        
850        String servicePrefix = PREFIX_SERVICE + MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + MODEL_ITEM_NAME_SEPARATOR;
851        Map<String, Object> parameters = _parametersManager.getParametersStartWithPrefix(parameterValues, servicePrefix);
852        
853        return _parametersManager.setParameterValues(serviceViewParametersHolder, serviceViewParameters.get().getModelItems(), parameters);
854    }
855    
856    /**
857     * Set content view parameters values
858     * @param skin the skin
859     * @param zoneItem the zone item
860     * @param parameterValues the parameter values
861     * @return the errors
862     */
863    protected Map<String, List<I18nizableText>> _setContentViewParameters(Skin skin, ModifiableZoneItem zoneItem, Map<String, Object> parameterValues)
864    {
865        String viewName = (String) parameterValues.get(ViewParametersManager.CONTENT_VIEW_MODEL_ITEM_NAME);
866        if (StringUtils.isEmpty(viewName))
867        {
868            // We have a content with no view parameters
869            return MapUtils.EMPTY_SORTED_MAP;
870        }
871
872        Content content = zoneItem.getContent();
873        zoneItem.setViewName(viewName);
874        Optional<ViewParametersModel> contentViewParameters = _viewParametersManager.getContentViewParametersModel(skin.getId(), content, viewName);
875        if (contentViewParameters.isEmpty())
876        {
877            return MapUtils.EMPTY_SORTED_MAP;
878        }
879        
880        ModifiableModelAwareDataHolder contentViewParametersHolder = zoneItem.getContentViewParametersHolder(viewName);
881        
882        String contentPrefix = PREFIX_CONTENT + MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + MODEL_ITEM_NAME_SEPARATOR;
883        Map<String, Object> contentParameters = _parametersManager.getParametersStartWithPrefix(parameterValues, contentPrefix);
884        
885        return _parametersManager.setParameterValues(contentViewParametersHolder, contentViewParameters.get().getModelItems(), contentParameters);
886    }
887    
888    /**
889     * Get the modifiable page from id
890     * @param pageId the page id
891     * @return the modifiable page
892     */
893    protected ModifiableSitemapElement _getModifiablePage(String pageId)
894    {
895        try
896        {
897            SitemapElement page = _resolver.resolveById(pageId);
898            if (!(page instanceof ModifiableSitemapElement))
899            {
900                throw new IllegalArgumentException("The non-modifiable page (" + pageId + ") can't have view parameters");
901            }
902            
903            return (ModifiableSitemapElement) page;
904            
905        }
906        catch (UnknownAmetysObjectException e)
907        {
908            throw new IllegalArgumentException("An error occured getting the modifiable page from id " + pageId, e);
909        }
910    }
911    
912    /**
913     * Get the modifiable zone item from id
914     * @param zoneItemId the zone item id
915     * @return the modifiable zone item
916     */
917    protected Optional<ModifiableZoneItem> _getModifiableZoneItem(String zoneItemId)
918    {
919        if (StringUtils.isNotBlank(zoneItemId))
920        {
921            try
922            {
923                ZoneItem zoneItem = _resolver.resolveById(zoneItemId);
924                if (!(zoneItem instanceof ModifiableZoneItem))
925                {
926                    throw new IllegalArgumentException("The non-modifiable zone item (" + zoneItemId + ") can't have view parameters");
927                }
928                
929                return Optional.of((ModifiableZoneItem) zoneItem);
930                
931            }
932            catch (UnknownAmetysObjectException e)
933            {
934                throw new IllegalArgumentException("An error occured getting the modifiable zone item from id " + zoneItemId, e);
935            }
936        }
937        
938        return Optional.empty();
939    }
940    
941    /**
942     * Get the skin template zone
943     * @param template the template
944     * @param zoneName the zone name
945     * @param zoneItem the zone item
946     * @return the skin template zone
947     */
948    protected Optional<SkinTemplateZone> _getSkinTemplateZone(SkinTemplate template, String zoneName, Optional<ModifiableZoneItem> zoneItem)
949    {
950        if (StringUtils.isNotBlank(zoneName))
951        {
952            return Optional.ofNullable(template.getZone(zoneName));
953        }
954        else if (zoneItem.isPresent())
955        {
956            String name = zoneItem.get().getZone().getName();
957            return Optional.ofNullable(template.getZone(name));
958        }
959        
960        return Optional.empty();
961    }
962    
963    /**
964     * Class representing a model item and its view parameters
965     */
966    static class ModelItemViewsWrapper
967    {
968        I18nizableText _label;
969        Optional<ViewParametersModel> _zoneItemViewParameters;
970        Map<String, ViewParametersModel> _contentOrServiceViewParameters;
971        ElementDefinition<String> _enumeratorViewDefinition;
972        String _prefix;
973        
974        ModelItemViewsWrapper()
975        {
976            this._label = null;
977            this._zoneItemViewParameters = null;
978            this._contentOrServiceViewParameters = new HashMap<>();
979            this._enumeratorViewDefinition = null;
980            this._prefix = StringUtils.EMPTY;
981        }
982        
983        I18nizableText getLabel()
984        {
985            return this._label;
986        }
987
988        void setLabel(I18nizableText label)
989        {
990            this._label = label;
991        }
992        
993        Optional<ViewParametersModel> getZoneItemViewParameters()
994        {
995            return this._zoneItemViewParameters;
996        }
997        
998        void setZoneItemViewParameters(Optional<ViewParametersModel> viewParameters)
999        {
1000            this._zoneItemViewParameters = viewParameters;
1001        }
1002        
1003        Map<String, ViewParametersModel> getServiceOrContentViewParameters()
1004        {
1005            return this._contentOrServiceViewParameters;
1006        }
1007        
1008        void addServiceOrContentViewParameters(String viewName, ViewParametersModel serviceViewParameters)
1009        {
1010            this._contentOrServiceViewParameters.put(viewName, serviceViewParameters);
1011        }
1012
1013        ElementDefinition<String> getEnumeratorViewDefinition()
1014        {
1015            return this._enumeratorViewDefinition;
1016        }
1017        
1018        void setEnumeratorViewDefinition(ElementDefinition<String> elementDefinition)
1019        {
1020            this._enumeratorViewDefinition = elementDefinition;
1021        }
1022        
1023        String getPrefix()
1024        {
1025            return this._prefix;
1026        }
1027        
1028        void setPrefix(String prefix)
1029        {
1030            this._prefix = prefix;
1031        }
1032        
1033        boolean hasZoneItemViewParameters()
1034        {
1035            return _zoneItemViewParameters.isPresent() && _zoneItemViewParameters.get().isNotEmpty();
1036        }
1037        
1038        boolean hasServiceOrContentViewParameters()
1039        {
1040            boolean hasViewParameters = false;
1041            for (ViewParametersModel viewParameters : _contentOrServiceViewParameters.values())
1042            {
1043                hasViewParameters = viewParameters.isNotEmpty() || hasViewParameters;
1044            }
1045            
1046            return hasViewParameters;
1047        }
1048        
1049        boolean hasViewParameters()
1050        {
1051            return hasZoneItemViewParameters() || hasServiceOrContentViewParameters();
1052        }
1053    }
1054}