001/*
002 *  Copyright 2018 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.repository.page;
017
018import java.io.IOException;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024import java.util.stream.Collectors;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.context.Context;
028import org.apache.avalon.framework.context.ContextException;
029import org.apache.avalon.framework.context.Contextualizable;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.apache.cocoon.ProcessingException;
034import org.apache.cocoon.components.ContextHelper;
035import org.apache.cocoon.environment.Request;
036import org.apache.commons.lang.StringUtils;
037import org.apache.commons.lang3.tuple.ImmutablePair;
038import org.apache.commons.lang3.tuple.Pair;
039
040import org.ametys.core.observation.Event;
041import org.ametys.core.observation.ObservationManager;
042import org.ametys.core.right.RightManager;
043import org.ametys.core.right.RightManager.RightResult;
044import org.ametys.core.ui.Callable;
045import org.ametys.core.user.CurrentUserProvider;
046import org.ametys.plugins.repository.AmetysObjectResolver;
047import org.ametys.plugins.repository.UnknownAmetysObjectException;
048import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
049import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
050import org.ametys.runtime.authentication.AccessDeniedException;
051import org.ametys.runtime.i18n.I18nizableText;
052import org.ametys.runtime.model.DefinitionContext;
053import org.ametys.runtime.model.Model;
054import org.ametys.runtime.model.ModelItem;
055import org.ametys.runtime.model.View;
056import org.ametys.runtime.model.ViewHelper;
057import org.ametys.runtime.model.ViewItem;
058import org.ametys.runtime.model.ViewItemContainer;
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.parameters.view.ViewParametersDAO;
065import org.ametys.web.parameters.view.ViewParametersManager;
066import org.ametys.web.parameters.view.ViewParametersModel;
067import org.ametys.web.repository.page.ZoneItem.ZoneType;
068import org.ametys.web.rights.PageRightAssignmentContext;
069import org.ametys.web.service.Service;
070import org.ametys.web.service.ServiceExtensionPoint;
071
072/**
073 * Class containing callables to retrieve and configure service parameters
074 */
075public class ZoneItemManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable
076{
077    /** Avalon Role */
078    public static final String ROLE = ZoneItemManager.class.getName();
079    
080    /** Constant for untouched binary metadata. */
081    protected static final String __SERVICE_PARAM_UNTOUCHED_BINARY = "untouched";
082    
083    private ServiceExtensionPoint _serviceExtensionPoint;
084    private AmetysObjectResolver _resolver;
085    private ObservationManager _observationManager;
086    private CurrentUserProvider _currentUserProvider;
087    private ParametersManager _parametersManager;
088    private ViewParametersManager _viewParametersManager;
089    private Context _context;
090    private PageDAO _pageDAO;
091    private RightManager _rightManager;
092    
093    public void service(ServiceManager manager) throws ServiceException
094    {
095        _serviceExtensionPoint = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE);
096        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
097        _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE);
098        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
099        _parametersManager = (ParametersManager) manager.lookup(ParametersManager.ROLE);
100        _viewParametersManager = (ViewParametersManager) manager.lookup(ViewParametersManager.ROLE);
101        _pageDAO = (PageDAO) manager.lookup(PageDAO.ROLE);
102        _rightManager = (RightManager) manager.lookup(RightManager.ROLE);
103    }
104    
105    public void contextualize(Context context) throws ContextException
106    {
107        _context = context;
108    }
109    
110    /**
111     * Retrieves the parameter definitions of the given service
112     * @param serviceId Identifier of the service
113     * @param pageId the page id
114     * @param zoneItemId the zone item id
115     * @param zoneName the zone name
116     * @return the parameter definitions
117     * @throws ProcessingException if an error occurs
118     */
119    @Callable(rights = Callable.NO_CHECK_REQUIRED)
120    public Map<String, Object> getServiceParameterDefinitions(String serviceId, String pageId, String zoneItemId, String zoneName) throws ProcessingException
121    {
122        _setRequestAttribute(serviceId, pageId, zoneItemId, zoneName);
123        
124        Map<String, Object> response = new HashMap<>();
125        
126        Service service = _serviceExtensionPoint.getExtension(serviceId);
127        
128        response.put("id", serviceId);
129        response.put("url", service.getURL());
130        response.put("label", service.getLabel());
131        response.put("height", service.getCreationBoxHeight());
132        response.put("width", service.getCreationBoxWidth());
133        
134        // Clone the view to not record in the service view the added view parameters
135        View clonedView = _cloneView(service.getView());
136        
137        SitemapElement sitemapElement = _resolver.resolveById(pageId);
138        
139        Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(sitemapElement.getSite().getSkinId(), serviceId);
140        
141        Pair<ViewItemContainer, Integer> containerAndIndex = _getXSLTViewItemContainerAndIndex(clonedView);
142        if (containerAndIndex != null)
143        {
144            for (String viewName : serviceViewParametersModels.keySet())
145            {
146                ViewParametersModel viewParameters = serviceViewParametersModels.get(viewName);
147                Collection< ? extends ModelItem> modelItems = viewParameters.getModelItems();
148                
149                String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR;
150                Optional<Integer> index = Optional.of(containerAndIndex.getRight() + 1); //add 1 to the XSLT parameter index to include the view parameters after the XSLT one
151                List<ModelItem> includedModelItems = _viewParametersManager.includeModelItems(modelItems, prefix, containerAndIndex.getLeft(), index); //add 1 to the XSL
152                _viewParametersManager.setDisableConditions(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME, OPERATOR.NEQ, viewName, includedModelItems);
153                
154                // 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
155                Model.of(ViewParametersDAO.PREFIX_SERVICE, ViewParametersDAO.ROLE, includedModelItems.toArray(new ModelItem[includedModelItems.size()]));
156            }
157        }
158        
159        response.put("parameters", clonedView.toJSON(DefinitionContext.newInstance().withEdition(true)));
160        
161        return response;
162    }
163    
164    private View _cloneView(View serviceView)
165    {
166        View clonedView = new View();
167        serviceView.copyTo(clonedView);
168        clonedView.addViewItems(ViewHelper.copyViewItems(serviceView.getViewItems()));
169        return clonedView;
170    }
171    
172    private Pair<ViewItemContainer, Integer> _getXSLTViewItemContainerAndIndex(ViewItemContainer viewItemContainer)
173    {
174        List<ViewItem> viewItems = viewItemContainer.getViewItems();
175        for (int i = 0; i < viewItems.size(); i++)
176        {
177            ViewItem viewItem = viewItems.get(i);
178
179            if (ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME.equals(viewItem.getName()))
180            {
181                return ImmutablePair.of(viewItemContainer, i);
182            }
183            else if (viewItem instanceof ViewItemContainer)
184            {
185                Pair<ViewItemContainer, Integer> xsltContainerAndIndex = _getXSLTViewItemContainerAndIndex((ViewItemContainer) viewItem);
186                if (xsltContainerAndIndex != null)
187                {
188                    return xsltContainerAndIndex;
189                }
190            }
191        }
192        
193        return null;
194    }
195    
196    private void _setRequestAttribute(String serviceId, String pageId, String zoneItemId, String zoneName)
197    {
198        Request request = ContextHelper.getRequest(_context);
199        
200        request.setAttribute(WebConstants.REQUEST_ATTR_SERVICE_ID, serviceId);
201        request.setAttribute(WebConstants.REQUEST_ATTR_PAGE_ID, pageId);
202        request.setAttribute(WebConstants.REQUEST_ATTR_ZONEITEM_ID, zoneItemId);
203        request.setAttribute(WebConstants.REQUEST_ATTR_ZONE_NAME, zoneName);
204    }
205    
206    /**
207     * Get the service parameter values
208     * @param zoneItemId the zone item id
209     * @param serviceId the service Id
210     * @return the values
211     */
212    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
213    public Map<String, Object> getServiceParameterValues(String zoneItemId, String serviceId)
214    {
215        Service service = _serviceExtensionPoint.getExtension(serviceId);
216        ZoneItem zoneItem = _resolver.resolveById(zoneItemId);
217        Zone zone = zoneItem.getZone();
218        SitemapElement sitemapElement = zone.getSitemapElement();
219        if (_rightManager.currentUserHasRight("Web_Rights_Page_ConfigureService", sitemapElement) != RightResult.RIGHT_ALLOW)
220        {
221            throw new AccessDeniedException(_currentUserProvider.getUser() + " tried to get service parameters values of zone item " + zoneItemId + " without sufficient right");
222        }
223        
224        _setRequestAttribute(serviceId, sitemapElement.getId(), zoneItemId, zone.getName());
225        
226        Map<String, Object> response = new HashMap<>();
227        Collection<ModelItem> serviceParameterDefinitions = service.getParameters().values();
228        ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters();
229
230        Map<String, Object> values = _parametersManager.getParametersValues(serviceParameterDefinitions, dataHolder, "");
231        values.putAll(_getServiceViewParametersValues(zoneItem));
232        
233        response.put("values", values);
234        
235        List<Map<String, Object>> repeaters = _parametersManager.getRepeatersValues(serviceParameterDefinitions, dataHolder, "");
236        response.put("repeaters", repeaters);
237        
238        return response;
239    }
240    
241    /**
242     * Get the service view parameters values
243     * @param zoneItem the zone item
244     * @return the values
245     */
246    protected Map<String, Object> _getServiceViewParametersValues(ZoneItem zoneItem)
247    {
248        Map<String, Object> values = new HashMap<>();
249        
250        String skinId = zoneItem.getZone()
251                .getSitemapElement()
252                .getSite()
253                .getSkinId();
254        
255        Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(skinId, zoneItem.getServiceId());
256        for (String viewName : serviceViewParametersModels.keySet())
257        {
258            ViewParametersModel serviceViewParameters = serviceViewParametersModels.get(viewName);
259            ModelAwareDataHolder serviceViewParametersHolder = zoneItem.getServiceViewParametersHolder(viewName);
260            
261            Map<String, Object> serviceValues = _parametersManager.getParametersValues(serviceViewParameters.getModelItems(), serviceViewParametersHolder, "");
262            String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR;
263            values.putAll(_parametersManager.addPrefixToParameters(serviceValues, prefix));
264        }
265        
266        return values;
267    }
268    
269    /**
270     * Add the service to the given zone on given page
271     * @param pageId The page identifier
272     * @param zoneName The zone name
273     * @param serviceId The identifier of the service to add
274     * @param parameterValues the service parameter values. Can be empty
275     * @return The result with the identifiers of updated page, zone and zone item
276     * @throws IOException if an error occurred while saving parameters
277     */
278    @Callable(rights = "Web_Rights_Page_AddService", rightContext = PageRightAssignmentContext.ID, paramIndex = 0)
279    public Map<String, Object> addService(String pageId, String zoneName, String serviceId, Map<String, Object> parameterValues) throws IOException
280    {
281        if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(pageId) || StringUtils.isEmpty(zoneName))
282        {
283            throw new IllegalArgumentException("ServiceId, PageId or ZoneName is missing");
284        }
285        
286        // Check the service
287        Service service = null;
288        try
289        {
290            service = _serviceExtensionPoint.getExtension(serviceId);
291        }
292        catch (IllegalArgumentException e)
293        {
294            throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e);
295        }
296        
297        try
298        {
299            SitemapElement sitemapElement = _resolver.resolveById(pageId);
300            if (!(sitemapElement instanceof ModifiableSitemapElement modifiableSitemapElement))
301            {
302                throw new IllegalArgumentException("Can not affect service on a non-modifiable page " + pageId);
303            }
304            
305            if (sitemapElement instanceof LockablePage lockablePage && lockablePage.isLocked())
306            {
307                throw new IllegalArgumentException("Can not affect service on a locked page " + pageId);
308            }
309            
310            if (sitemapElement.getTemplate() == null)
311            {
312                throw new IllegalArgumentException("Can not affect service on a non-container page " + pageId);
313            }
314            
315            if (!_getAllowedServicesId(pageId, zoneName).contains(serviceId))
316            {
317                throw new IllegalArgumentException("Can not affect service '" + serviceId + "' on a zone '" + zoneName + "'");
318            }
319         
320            ModifiableZone zone;
321            if (modifiableSitemapElement.hasZone(zoneName))
322            {
323                zone = modifiableSitemapElement.getZone(zoneName);
324            }
325            else
326            {
327                zone = modifiableSitemapElement.createZone(zoneName);
328            }
329            
330            ModifiableZoneItem zoneItem = zone.addZoneItem();
331            zoneItem.setType(ZoneType.SERVICE);
332            zoneItem.setServiceId(serviceId);
333            
334            Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, zoneItem);
335            
336            Map<String, Object> results = new HashMap<>();
337            if (!allErrors.isEmpty())
338            {
339                results.put("errors", allErrors);
340                return results;
341            }
342            
343            modifiableSitemapElement.saveChanges();
344            
345            Map<String, Object> eventParams = new HashMap<>();
346            eventParams.put(ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement);
347            eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId());
348            eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE);
349            eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId);
350            _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _currentUserProvider.getUser(), eventParams));
351            
352            results.put("id", sitemapElement.getId());
353            results.put("zoneitem-id", zoneItem.getId());
354            results.put("zone-name", zone.getName());
355            
356            return results;
357        }
358        catch (UnknownAmetysObjectException e)
359        {
360            throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + pageId + "'", e);
361        }
362    }
363
364    private List<String> _getAllowedServicesId(String pageId, String zoneName)
365    {
366        return _pageDAO.getAvailableServices(pageId, zoneName).stream()
367        .map(info -> (String) info.get("id"))
368        .collect(Collectors.toList());
369    }
370    
371    /**
372     * Edit the parameter values of the given service
373     * @param zoneItemId The identifier of the zone item holding the service
374     * @param serviceId The service identifier
375     * @param parameterValues the service parameter values to update
376     * @return The result with the identifiers of updated page, zone and zone item
377     * @throws IOException if an error occurs while saving parameters
378     */
379    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
380    public Map<String, Object> editServiceParameterValues(String zoneItemId, String serviceId, Map<String, Object> parameterValues) throws IOException
381    {
382        Service service = null;
383        try
384        {
385            service = _serviceExtensionPoint.getExtension(serviceId);
386        }
387        catch (IllegalArgumentException e)
388        {
389            throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e);
390        }
391        
392        ZoneItem zoneItem = _resolver.resolveById(zoneItemId);
393        if (!(zoneItem instanceof ModifiableZoneItem modifiableZoneItem))
394        {
395            throw new IllegalArgumentException("Can not configure service on a non-modifiable zone item " + zoneItemId);
396        }
397        
398        SitemapElement sitemapElement = zoneItem.getZone().getSitemapElement();
399        if (_rightManager.currentUserHasRight("Web_Rights_Page_ConfigureService", sitemapElement) != RightResult.RIGHT_ALLOW)
400        {
401            throw new AccessDeniedException(_currentUserProvider.getUser() + " tried to set service parameters values of zone item " + zoneItemId + " without sufficient right");
402        }
403        if (sitemapElement instanceof LockablePage lockablePage && lockablePage.isLocked())
404        {
405            throw new IllegalArgumentException("Can not configure service on a locked page '/" + lockablePage.getSitemapName() + "/" + lockablePage.getPathInSitemap() + "'");
406        }
407        
408        Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, modifiableZoneItem);
409        
410        Map<String, Object> results = new HashMap<>();
411        if (!allErrors.isEmpty())
412        {
413            results.put("errors", allErrors);
414            return results;
415        }
416        
417        modifiableZoneItem.saveChanges();
418        
419        Map<String, Object> eventParams = new HashMap<>();
420        eventParams.put(ObservationConstants.ARGS_ZONE_ITEM, zoneItem);
421        eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId());
422        eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId);
423        _observationManager.notify(new Event(ObservationConstants.EVENT_SERVICE_MODIFIED, _currentUserProvider.getUser(), eventParams));
424        
425        results.put("id", sitemapElement.getId());
426        results.put("zoneitem-id", zoneItem.getId());
427        results.put("zone-name", zoneItem.getZone().getName());
428        
429        return results;
430    }
431
432    /**
433     * Set the parameter values for the service (with view parameters)
434     * @param parameterValues the parameter values
435     * @param service the service
436     * @param zoneItem the zone item
437     * @return the map of error
438     */
439    protected Map<String, List<I18nizableText>> _setParameterValues(Map<String, Object> parameterValues, Service service, ModifiableZoneItem zoneItem)
440    {
441        ModifiableModelAwareDataHolder serviceDataHolder = zoneItem.getServiceParameters();
442        Map<String, ModelItem> definitions = service.getParameters();
443        Map<String, List<I18nizableText>> allErrors = _parametersManager.setParameterValues(serviceDataHolder, definitions.values(), parameterValues);
444        
445        if (parameterValues.containsKey(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME))
446        {
447            String viewName = (String) parameterValues.get(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
448            
449            String skinId = zoneItem.getZone()
450                    .getSitemapElement()
451                    .getSite()
452                    .getSkinId();
453            Optional<ViewParametersModel> serviceViewParametersModel = _viewParametersManager.getServiceViewParametersModel(skinId, service.getId(), viewName);
454            
455            if (serviceViewParametersModel.isPresent())
456            {
457                ModifiableModelAwareDataHolder serviceParametersHolder = zoneItem.getServiceViewParametersHolder(viewName);
458                String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR;
459                Map<String, Object> viewParametersValues = _parametersManager.getParametersStartWithPrefix(parameterValues, prefix);
460                allErrors.putAll(_parametersManager.setParameterValues(serviceParametersHolder, serviceViewParametersModel.get().getModelItems(), viewParametersValues));
461            }
462        }
463        
464        return allErrors;
465    }
466    
467    /**
468     * Paste the given service in the given zone
469     * @param srcZoneItemId The identifier of the zone item holding the original service
470     * @param serviceId The service identifier
471     * @param targetPageId The identifier of the page where to paste the service
472     * @param targetZoneName The zone name  where to paste the service
473     * @return The result with the identifiers of updated page, zone and zone item
474     * @throws IOException if an error occurs while saving parameters
475     */
476    @Callable(rights = "Web_Rights_Page_CopyService", rightContext = PageRightAssignmentContext.ID, paramIndex = 2)
477    public Map<String, Object> pasteService(String srcZoneItemId, String serviceId, String targetPageId, String targetZoneName) throws IOException
478    {
479        if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(targetPageId) || StringUtils.isEmpty(targetZoneName))
480        {
481            throw new IllegalArgumentException("ServiceId, PageId or ZoneName is missing");
482        }
483        
484        // Check the service
485        try
486        {
487            _serviceExtensionPoint.getExtension(serviceId);
488        }
489        catch (IllegalArgumentException e)
490        {
491            throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e);
492        }
493        
494        try
495        {
496            SitemapElement sitemapElement = _resolver.resolveById(targetPageId);
497            if (!(sitemapElement instanceof ModifiableSitemapElement modifiableSitemapElement))
498            {
499                throw new IllegalArgumentException("Can not affect service on a non-modifiable page " + targetPageId);
500            }
501            
502            if (sitemapElement instanceof LockablePage lockablePage && lockablePage.isLocked())
503            {
504                throw new IllegalArgumentException("Can not affect service on a locked page " + targetPageId);
505            }
506            
507            if (sitemapElement.getTemplate() == null)
508            {
509                throw new IllegalArgumentException("Can not affect service on a non-container page " + targetPageId);
510            }
511            
512            if (!_getAllowedServicesId(targetPageId, targetZoneName).contains(serviceId))
513            {
514                throw new IllegalArgumentException("Can not affect service '" + serviceId + "' on a zone '" + targetZoneName + "'");
515            }
516         
517            ModifiableZone zone;
518            if (modifiableSitemapElement.hasZone(targetZoneName))
519            {
520                zone = modifiableSitemapElement.getZone(targetZoneName);
521            }
522            else
523            {
524                zone = modifiableSitemapElement.createZone(targetZoneName);
525            }
526            
527            ModifiableZoneItem zoneItem = zone.addZoneItem();
528            zoneItem.setType(ZoneType.SERVICE);
529            zoneItem.setServiceId(serviceId);
530            
531            ZoneItem srcService = _resolver.resolveById(srcZoneItemId);
532            ModelAwareDataHolder serviceDataHolder = srcService.getServiceParameters();
533            serviceDataHolder.copyTo(zoneItem.getServiceParameters());
534            
535            if (serviceDataHolder.hasDefinition(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME))
536            {
537                String viewName = serviceDataHolder.getValue(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME);
538                
539                // Copy of view parameters
540                srcService.getServiceViewParametersHolder(viewName).copyTo(zoneItem.getServiceViewParametersHolder(viewName));
541            }
542            Map<String, Object> results = new HashMap<>();
543            
544            modifiableSitemapElement.saveChanges();
545            
546            Map<String, Object> eventParams = new HashMap<>();
547            eventParams.put(ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement);
548            eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId());
549            eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE);
550            eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId);
551            _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _currentUserProvider.getUser(), eventParams));
552            
553            results.put("id", sitemapElement.getId());
554            results.put("zoneitem-id", zoneItem.getId());
555            results.put("zone-name", zone.getName());
556            
557            return results;
558        }
559        catch (UnknownAmetysObjectException e)
560        {
561            throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + targetPageId + "'", e);
562        }
563    }
564}