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.plugins.repository.workspace;
017
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.InputStream;
021import java.text.ParseException;
022import java.text.SimpleDateFormat;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.Date;
026import java.util.HashMap;
027import java.util.Map;
028
029import org.apache.avalon.framework.parameters.ParameterException;
030import org.apache.avalon.framework.parameters.Parameters;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.cocoon.ProcessingException;
034import org.apache.cocoon.acting.ServiceableAction;
035import org.apache.cocoon.environment.ObjectModelHelper;
036import org.apache.cocoon.environment.Redirector;
037import org.apache.cocoon.environment.Request;
038import org.apache.cocoon.environment.SourceResolver;
039import org.apache.cocoon.servlet.multipart.PartOnDisk;
040import org.apache.commons.lang3.exception.ExceptionUtils;
041
042import org.ametys.core.cocoon.JSonReader;
043import org.ametys.core.util.LambdaUtils;
044import org.ametys.plugins.repository.AmetysObject;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType;
047import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
048import org.ametys.plugins.repository.metadata.ModifiableFile;
049import org.ametys.plugins.repository.metadata.ModifiableFolder;
050import org.ametys.plugins.repository.metadata.ModifiableMetadataAwareAmetysObject;
051import org.ametys.plugins.repository.metadata.ModifiableRichText;
052import org.ametys.runtime.i18n.I18nizableText;
053
054/**
055 * Set value for a metadata
056 */
057public class SetMetadataAction extends ServiceableAction
058{
059    static SimpleDateFormat _DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
060    private AmetysObjectResolver _resolver;
061    
062    @Override
063    public void service(ServiceManager serviceManager) throws ServiceException
064    {
065        super.service(serviceManager);
066        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
067    }
068    
069    public Map act(Redirector redirector, SourceResolver sourceResolver, Map objectModel, String source, Parameters parameters) throws Exception
070    {
071        Request request = ObjectModelHelper.getRequest(objectModel);
072        
073        String id = null; 
074        String name = null;
075        String compositePath = null;
076        String richtextPath = null;
077        boolean multiple;
078        
079        String type = null;
080        String fileFieldId = null;
081    
082        try
083        {
084            id = parameters.getParameter("id");
085            name = parameters.getParameter("name");
086            compositePath = parameters.getParameter("cPath");
087            richtextPath = parameters.getParameter("rtPath");
088            type = parameters.getParameter("type");
089            fileFieldId = parameters.getParameter("filefieldid");
090            multiple = parameters.getParameterAsBoolean("multiple", false);
091        }
092        catch (ParameterException e)
093        {
094            throw new ProcessingException("Missing at least one parameter", e);
095        }
096        
097        if (getLogger().isInfoEnabled())
098        {
099            getLogger().info("Setting metadata '" + name + "' of the AmetysObject of id '" + id + "'");
100        }
101        
102        AmetysObject ao = null;
103        
104        if (id.equals("/"))
105        {
106            ao = _resolver.resolveByPath("/");
107        }
108        else
109        {
110            ao = _resolver.resolveById(id);
111        }
112
113        String[] values = request.getParameterValues("value");
114        
115        Map<String, Object> result = new HashMap<>();
116        
117        try
118        {
119            ModifiableCompositeMetadata holder = _getHolderInComposite((ModifiableMetadataAwareAmetysObject) ao, compositePath);
120            
121            //changing value of a metadata in a composite
122            if (richtextPath.isEmpty())
123            {
124                _setMetadataOutsideRT(holder, type, name, values, multiple);          
125            }
126            //changing a value inside a richtext
127            else if (richtextPath.split("/").length == 1)
128            {
129                _setMetadataInsideRT(request, holder, name, values[0], fileFieldId, richtextPath);        
130            }
131            else //TODO this part is not tested !!
132            {
133                _setMetadataInsideRTDataFolder(request, holder, name, values[0], fileFieldId, richtextPath);
134            }
135    
136            ((ModifiableMetadataAwareAmetysObject) ao).saveChanges();
137            
138            result.put("success", true);
139        }
140        catch (Exception e)
141        {
142            getLogger().error("Unable to set value " + values[0] + " for metadata '" + name + "'", e);
143            result.put("success", false);
144            
145            Throwable rootCause = ExceptionUtils.getRootCause(e);
146            
147            if (rootCause instanceof ParseException)
148            {
149                Map<String, I18nizableText> params = Collections.singletonMap("error", new I18nizableText(e.getMessage()));
150                result.put("message", new I18nizableText("plugin.repositoryapp", "PLUGINS_REPOSITORYAPP_CREATE_PROP_ERROR_DATE", params));
151            }
152            else
153            {
154                result.put("message", e.toString());
155            }
156        }
157
158        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
159        return EMPTY_MAP;
160    }
161    
162    private ModifiableCompositeMetadata _getHolderInComposite(ModifiableMetadataAwareAmetysObject ao, String compositePath)
163    {
164        ModifiableCompositeMetadata holder = ao.getMetadataHolder();
165
166        // deep search in composite metadata
167        if (!compositePath.isEmpty())
168        {
169            String[] tokens = compositePath.split("/");        
170            for (int i = 0; i < tokens.length; i++)
171            {
172                holder = holder.getCompositeMetadata(tokens[i]);
173            }
174        }
175        
176        return holder;
177    }
178    
179    private void _setMetadataOutsideRT(ModifiableCompositeMetadata holder, String type, String name, String[] values, boolean multiple) throws Exception
180    {
181        //switch according to type
182        //convert value to correct type if not string and set new value          
183        MetadataType mdType = MetadataType.valueOf(type.toUpperCase());
184        switch (mdType)
185        {
186            case DATE:
187                if (multiple)
188                {
189                    Date[] dateValues = Arrays.stream(values).map(LambdaUtils.wrap(_DATE_FORMAT::parse)).toArray(Date[]::new);
190                    holder.setMetadata(name, dateValues);
191                }
192                else
193                {
194                    Date dateVal = _DATE_FORMAT.parse(values[0]);
195                    holder.setMetadata(name, dateVal);
196                }
197                break;
198            case STRING:
199                if (multiple)
200                {
201                    holder.setMetadata(name, values);
202                }
203                else
204                {
205                    holder.setMetadata(name, values[0]);
206                }
207                break;
208            case LONG:
209                if (multiple)
210                {
211                    long[] longValues = Arrays.stream(values).mapToLong(Long::parseLong).toArray();
212                    holder.setMetadata(name, longValues);
213                }
214                else
215                {
216                    Long longVal = Long.parseLong(values[0]);
217                    holder.setMetadata(name, longVal);
218                }
219                break;
220            case DOUBLE:
221                if (multiple)
222                {
223                    double[] doubleValues = Arrays.stream(values).mapToDouble(Double::parseDouble).toArray();
224                    holder.setMetadata(name, doubleValues);
225                }
226                else
227                {
228                    Double doubleVal = Double.parseDouble(values[0]);
229                    holder.setMetadata(name, doubleVal);
230                }
231                
232                break;
233            case BOOLEAN:
234                Boolean booleanVal = Boolean.parseBoolean(values[0]);
235                holder.setMetadata(name, booleanVal);
236                break;
237            case COMPOSITE:
238
239                break;
240            case BINARY:
241
242                break;
243            case RICHTEXT:
244
245                break;
246            case OBJECT_COLLECTION:
247                // TODO?
248                break;
249            default:
250                break;
251        }
252    }
253    
254    private void _setMetadataInsideRT(Request request, ModifiableCompositeMetadata holder, String name, String value, String fileFieldId, String richtextPath) throws Exception
255    {
256        String[] rtTokens = richtextPath.split("/");
257        ModifiableRichText rt = holder.getRichText(rtTokens[0]);
258        
259        // update correct property
260        if (name.equals("mime-type"))
261        {
262            rt.setMimeType(value);
263        }
264        else if (name.equals("last-modified-date"))
265        {
266            Date dateVal = _DATE_FORMAT.parse(value);
267            rt.setLastModified(dateVal);
268        }
269        else if (name.equals("encoding"))
270        {
271            rt.setEncoding(value);
272        }
273        else if (name.equals("data"))
274        {
275            PartOnDisk uploadedFilePart = (PartOnDisk) request.get(fileFieldId);
276            File uploadedFile = (uploadedFilePart != null) ? uploadedFilePart.getFile() : null;
277            
278            if (uploadedFile != null)
279            {
280                InputStream is = new FileInputStream(uploadedFile);
281                rt.setInputStream(is);
282            }
283        }
284        else if (name.equals("length"))
285        {
286            //not possible to change length
287        }
288    }
289    
290    private void _setMetadataInsideRTDataFolder(Request request, ModifiableCompositeMetadata holder, String name, String value, String fileFieldId, String richtextPath) throws Exception
291    {
292        String[] rtTokens = richtextPath.split("/");
293        ModifiableRichText rt = holder.getRichText(rtTokens[0]);
294        ModifiableFolder folder = rt.getAdditionalDataFolder();
295        
296        for (int i = 2; i < rtTokens.length - 1; i++)
297        {
298            folder = folder.getFolder(rtTokens[i]);
299        }
300        
301        ModifiableFile file = folder.getFile(rtTokens[rtTokens.length - 1]);
302        
303        // update correct property
304        if (name.equals("mime-type"))
305        {
306            file.getResource().setMimeType(value);
307        }
308        else if (name.equals("last-modified-date"))
309        {
310            Date dateVal = _DATE_FORMAT.parse(value);
311            file.getResource().setLastModified(dateVal);
312        }
313        else if (name.equals("encoding"))
314        {
315            file.getResource().setEncoding(value);
316        }
317        else if (name.equals("data"))
318        {
319            PartOnDisk uploadedFilePart = (PartOnDisk) request.get(fileFieldId);
320            File uploadedFile = (uploadedFilePart != null) ? uploadedFilePart.getFile() : null;
321            InputStream is = new FileInputStream(uploadedFile);
322            file.getResource().setInputStream(is);
323        }
324        else if (name.equals("length"))
325        {
326            //not possible to change length
327        }
328    }
329}