001/*
002 *  Copyright 2012 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.core.upload;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.util.HashMap;
021import java.util.LinkedHashMap;
022import java.util.Map;
023
024import org.apache.avalon.framework.parameters.Parameters;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.cocoon.acting.Action;
027import org.apache.cocoon.environment.ObjectModelHelper;
028import org.apache.cocoon.environment.Redirector;
029import org.apache.cocoon.environment.Request;
030import org.apache.cocoon.environment.SourceResolver;
031import org.apache.cocoon.servlet.multipart.Part;
032import org.apache.commons.lang.exception.ExceptionUtils;
033
034import org.ametys.core.cocoon.JSonReader;
035import org.ametys.core.upload.Upload;
036import org.ametys.core.upload.UploadManager;
037import org.ametys.core.util.cocoon.AbstractCurrentUserProviderServiceableAction;
038
039/**
040 * {@link Action} for uploading a file and store it using the {@link UploadManager}.
041 */
042public class UploadAction extends AbstractCurrentUserProviderServiceableAction
043{
044    /** The Upload Manager */
045    protected UploadManager _uploadManager;
046    
047    @Override
048    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
049    {
050        Request request = ObjectModelHelper.getRequest(objectModel);
051        
052        _lookupComponents();
053        
054        Map<String, Object> result = new LinkedHashMap<>();
055        _doUpload(request, parameters, result);
056        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
057
058        return EMPTY_MAP;
059    }
060    
061    /**
062     * Lookup components (lazy initialize)
063     * @throws ServiceException If some components cannot be looked up
064     */
065    protected void _lookupComponents() throws ServiceException
066    {
067        // Lazy initialize the upload manager because it cannot be found if the config is not complete  
068        if (_uploadManager == null)
069        {
070            _uploadManager = (UploadManager) manager.lookup(UploadManager.ROLE);
071        }
072    }
073    
074    /**
075     * Do upload the file and fill the result map.
076     * @param request The request
077     * @param parameters The parameters
078     * @param result The result map to fill
079     * @throws Exception if an error occurs.
080     */
081    protected void _doUpload(Request request, Parameters parameters, Map<String, Object> result) throws Exception
082    {
083        Part partUploaded = _getPart(request);
084        
085        if (partUploaded.isRejected())
086        {
087            result.put("success", false);
088            result.put("error", "rejected");
089        }
090        else
091        {
092            try (InputStream is = partUploaded.getInputStream())
093            {
094                _storeUpload(is, partUploaded.getFileName(), result);
095            }
096            catch (IOException e)
097            {
098                _handleStoreUploadException(e, partUploaded, result);
099            }
100        }
101    }
102    
103    /**
104     * Gets the {@link Part} object which is trying to be uploaded
105     * @param request The request
106     * @return The {@link Part} object which is trying to be uploaded
107     * @throws Exception if an error occurs.
108     */
109    protected Part _getPart(Request request) throws Exception
110    {
111        Part partUploaded = (Part) request.get("file");
112        
113        if (partUploaded == null)
114        {
115            throw new Exception("Missing request parameter file");
116        }
117        
118        return partUploaded;
119    }
120    
121    /**
122     * Stores the given upload data (as an {@link InputStream}) into the {@link UploadManager} system and fill the result map.
123     * @param is The upload data
124     * @param filename The file name
125     * @param result The result map to fill
126     * @throws IOException if an I/O error occurs
127     */
128    protected void _storeUpload(InputStream is, String filename, Map<String, Object> result) throws IOException
129    {
130        Upload upload = _uploadManager.storeUpload(_getCurrentUser(), filename, is);
131        _fillSuccess(upload, result);
132    }
133    
134    /**
135     * Fill the result map.
136     * @param upload The upload
137     * @param result The result map to fill
138     */
139    protected void _fillSuccess(Upload upload, Map<String, Object> result)
140    {
141        result.put("success", true);
142        result.put("id", upload.getId());
143        result.put("filename", upload.getFilename());
144        result.put("size", upload.getLength());
145        result.put("viewHref", _getUrlForView(upload));
146        result.put("downloadHref", _getUrlForDownload(upload));
147    }
148    
149    /**
150     * Handles a store upload exception by filling the result map with information about the exception.
151     * @param e The exception
152     * @param obj The object representing the upload data (for logging purpose)
153     * @param result The result map to fill
154     */
155    protected void _handleStoreUploadException(IOException e, Object obj, Map<String, Object> result)
156    {
157        getLogger().error("Unable to store uploaded file: " + obj, e);
158        
159        result.put("success", false);
160        
161        Map<String, String> ex = new HashMap<>();
162        ex.put("message", e.getMessage());
163        ex.put("stacktrace", ExceptionUtils.getFullStackTrace(e));
164        
165        result.put("error", ex);
166    }
167    
168    /**
169     * Get the url for view the uploaded file
170     * @param upload The file uploaded
171     * @return The url for view
172     */
173    protected String _getUrlForView(Upload upload)
174    {
175        return "/plugins/core/upload/file?id=" + upload.getId();
176    }
177    
178    /**
179     * Get the url for download the uploaded file
180     * @param upload The file uploaded
181     * @return The url for view
182     */
183    protected String _getUrlForDownload(Upload upload)
184    {
185        return "/plugins/core/upload/file?id=" + upload.getId() + "&download=true";
186    }
187}