001/*
002 *  Copyright 2010 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.actions;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.Map;
022
023import org.apache.avalon.framework.parameters.Parameters;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Redirector;
028import org.apache.cocoon.environment.SourceResolver;
029import org.apache.commons.lang.StringUtils;
030
031import org.ametys.core.observation.AbstractNotifierAction;
032import org.ametys.core.observation.Event;
033import org.ametys.plugins.repository.AmetysObjectResolver;
034import org.ametys.plugins.repository.UnknownAmetysObjectException;
035import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
036import org.ametys.runtime.parameter.ParameterHelper;
037import org.ametys.web.ObservationConstants;
038import org.ametys.web.repository.page.ModifiablePage;
039import org.ametys.web.repository.page.ModifiableZone;
040import org.ametys.web.repository.page.ModifiableZoneItem;
041import org.ametys.web.repository.page.Page.PageType;
042import org.ametys.web.repository.page.ZoneItem.ZoneType;
043import org.ametys.web.service.Service;
044import org.ametys.web.service.ServiceExtensionPoint;
045import org.ametys.web.service.ServiceParameter;
046import org.ametys.web.service.ServiceParameterOrRepeater;
047import org.ametys.web.service.ServiceParameterRepeater;
048
049import com.fasterxml.jackson.core.JsonFactory;
050import com.fasterxml.jackson.core.JsonParser;
051import com.fasterxml.jackson.databind.ObjectMapper;
052
053/**
054 * This action affects a service to a zone of a page
055 * TODO To remove => use PageDAO#setService instead 
056 */
057@Deprecated
058public class ServiceAffectAction extends AbstractNotifierAction
059{
060    /** Constant for untouched binary metadata. */
061    public static final String UNTOUCHED_BINARY = "untouched";
062    
063    /** The list of content types */
064    protected ServiceExtensionPoint _serviceExtensionPoint;
065    /** The Ametys object resolver */
066    protected AmetysObjectResolver _resolver;
067    
068    @Override
069    public void service(ServiceManager serviceManager) throws ServiceException
070    {
071        super.service(serviceManager);
072        _serviceExtensionPoint = (ServiceExtensionPoint) serviceManager.lookup(ServiceExtensionPoint.ROLE);
073        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
074    }
075    
076    @Override
077    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
078    {
079        @SuppressWarnings("unchecked")
080        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
081
082        String serviceId = (String) jsParameters.get("service-org.ametys.web.service$id");
083        String pageId = (String) jsParameters.get("service-org.ametys.web.service$pageId");
084        String zoneName = (String) jsParameters.get("service-org.ametys.web.service$zoneName");
085
086        if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(pageId) || StringUtils.isEmpty(zoneName))
087        {
088            throw new IllegalArgumentException("ServiceId, PageId or ZoneName is not specified");
089        }
090        
091        // Check the service
092        try
093        {
094            _serviceExtensionPoint.getExtension(serviceId);
095        }
096        catch (IllegalArgumentException e)
097        {
098            throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e);
099        }
100        
101        // Get the page
102        
103        try
104        {
105            // FIXME API test if this is not modifiable
106            ModifiablePage page = _resolver.resolveById(pageId);
107            
108            if (page.getType() != PageType.CONTAINER)
109            {
110                throw new IllegalArgumentException("Page '" + pageId + "' is not of type Container");
111            }
112         
113            ModifiableZone zone;
114            if (page.hasZone(zoneName))
115            {
116                zone = page.getZone(zoneName);
117            }
118            else
119            {
120                zone = page.createZone(zoneName);
121            }
122            
123            ModifiableZoneItem zoneItem = zone.addZoneItem();
124            zoneItem.setType(ZoneType.SERVICE);
125            zoneItem.setServiceId(serviceId);
126            
127            ModifiableCompositeMetadata serviceMetadata = zoneItem.getServiceParameters();
128            Service service = _serviceExtensionPoint.getExtension(serviceId);
129            
130            storeParameterValues(serviceMetadata, service, jsParameters);
131            
132            page.saveChanges();
133            
134            Map<String, Object> eventParams = new HashMap<>();
135            eventParams.put(ObservationConstants.ARGS_PAGE, page);
136            eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId());
137            eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE);
138            _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _getCurrentUser(), eventParams));
139            
140            Map<String, String> results = new HashMap<>();
141            results.put("zoneitem-id", zoneItem.getId());
142            return results;
143        }
144        catch (UnknownAmetysObjectException e)
145        {
146            throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + pageId + "'", e);
147        }
148                
149    }
150    
151    /**
152     * Store the service parameter values.
153     * @param serviceMetadata the service instance composite metadata.
154     * @param service the service definition
155     * @param values the parameter values.
156     * @throws IOException if an error occurred.
157     */
158    protected void storeParameterValues(ModifiableCompositeMetadata serviceMetadata, Service service, Map<String, Object> values) throws IOException
159    {
160        JsonFactory jsonFactory = new JsonFactory();
161        ObjectMapper objectMapper = new ObjectMapper();
162        
163        for (ServiceParameterOrRepeater param : service.getParameters().values())
164        {
165            String parameterId = param.getId();
166            Object objectValue = values.get("service-org.ametys.web.service$" + parameterId);
167            
168            if (objectValue != null)
169            {
170                if (param instanceof ServiceParameter)
171                {
172                    if (!UNTOUCHED_BINARY.equals(objectValue))
173                    {
174                        serviceMetadata.setMetadata(parameterId, ParameterHelper.valueToString(objectValue));
175                    }
176                }
177                else if (param instanceof ServiceParameterRepeater)
178                {
179                    JsonParser jParser = jsonFactory.createParser((String) objectValue);
180                    ArrayList<Map<String, Object>> entries = objectMapper.readValue(jParser, ArrayList.class);
181                    
182                    if (serviceMetadata.hasMetadata(parameterId))
183                    {
184                        serviceMetadata.removeMetadata(parameterId);
185                    }
186                    
187                    ModifiableCompositeMetadata repeaterMeta = serviceMetadata.getCompositeMetadata(parameterId, true);
188                    int count = 1;
189                    
190                    for (Map<String, Object> entry : entries)
191                    {
192                        ModifiableCompositeMetadata entryMeta = repeaterMeta.getCompositeMetadata(Integer.toString(count++), true);
193                        
194                        for (String childParamId : entry.keySet())
195                        {
196                            Object childParamValue = entry.get(childParamId);
197                            if (!UNTOUCHED_BINARY.equals(childParamValue))
198                            {
199                                entryMeta.setMetadata(childParamId, ParameterHelper.valueToString(childParamValue));
200                            }
201                        }
202                    }
203                }
204            }
205        }
206    }
207}