001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     troger
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.webapp.note;
021
022import static org.jboss.seam.ScopeType.CONVERSATION;
023
024import java.io.IOException;
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import javax.servlet.http.Part;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.jboss.seam.annotations.In;
036import org.jboss.seam.annotations.Name;
037import org.jboss.seam.annotations.Scope;
038import org.jboss.seam.annotations.web.RequestParameter;
039import org.nuxeo.ecm.core.api.Blob;
040import org.nuxeo.ecm.core.api.CoreSession;
041import org.nuxeo.ecm.core.api.DocumentModel;
042import org.nuxeo.ecm.core.api.ListDiff;
043import org.nuxeo.ecm.core.api.NuxeoException;
044import org.nuxeo.ecm.platform.query.api.PageProvider;
045import org.nuxeo.ecm.platform.query.api.PageProviderService;
046import org.nuxeo.ecm.platform.query.nxql.CoreQueryDocumentPageProvider;
047import org.nuxeo.ecm.platform.ui.web.tag.fn.DocumentModelFunctions;
048import org.nuxeo.ecm.platform.ui.web.util.files.FileUtils;
049import org.nuxeo.ecm.webapp.base.InputController;
050import org.nuxeo.runtime.api.Framework;
051
052/**
053 * Seam component implementing actions related to inserting an image in a Note document.
054 * <p>
055 * The uploaded image is stored in the <code>files</code> schema of the document.
056 * <p>
057 * After uploading an image, the REST URL for this image can be retrieve through the appropriate method.
058 * <p>
059 * The search method retrieves only the Picture document of the repository.
060 *
061 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
062 */
063@Name("editorImageActions")
064@Scope(CONVERSATION)
065public class EditorImageActionsBean extends InputController implements EditorImageActions, Serializable {
066
067    private static final String FILES_SCHEMA = "files";
068
069    private static final List<Map<String, String>> SIZES;
070
071    /** @since 5.9.5 */
072    private static final String PP_SEARCH_MEDIA_BY_TITLE = "search_media_by_title";
073
074    /** @since 5.9.5 */
075    private static final String PP_SEARCH_MEDIA_ALL = "search_media_all";
076
077    static {
078        SIZES = new ArrayList<Map<String, String>>();
079        Map<String, String> m = new HashMap<String, String>();
080        m.put("label", "label.imageUpload.originalSize");
081        m.put("value", "OriginalJpeg");
082        SIZES.add(m);
083        m = new HashMap<String, String>();
084        m.put("label", "label.imageUpload.mediumSize");
085        m.put("value", "Medium");
086        SIZES.add(m);
087        m = new HashMap<String, String>();
088        m.put("label", "label.imageUpload.thumbnailSize");
089        m.put("value", "Thumbnail");
090        SIZES.add(m);
091    }
092
093    private static final long serialVersionUID = 8716548847393060676L;
094
095    private static final Log log = LogFactory.getLog(EditorImageActionsBean.class);
096
097    @In(create = true, required = false)
098    private transient CoreSession documentManager;
099
100    @RequestParameter
101    private String selectedTab;
102
103    private String oldSelectedTab;
104
105    private Part uploadedImage;
106
107    /**
108     * @deprecated since 7.1
109     */
110    @Deprecated
111    private String uploadedImageName;
112
113    private String imageUrl;
114
115    private boolean isImageUploaded = false;
116
117    private List<DocumentModel> resultDocuments;
118
119    private List<DocumentModel> resultVideos;
120
121    private boolean hasSearchResults = false;
122
123    private boolean hasSearchVideosResults = false;
124
125    private String searchKeywords;
126
127    private String selectedSize = "OriginalJpeg";
128
129    @Override
130    public String getSelectedTab() {
131        if (selectedTab != null) {
132            oldSelectedTab = selectedTab;
133        } else if (oldSelectedTab == null) {
134            oldSelectedTab = "UPLOAD";
135        }
136        return oldSelectedTab;
137    }
138
139    @Override
140    public String getUrlForImage() {
141        isImageUploaded = false;
142        return imageUrl;
143    }
144
145    @Override
146    public boolean getIsImageUploaded() {
147        return isImageUploaded;
148    }
149
150    @Override
151    public void setUploadedImage(Part uploadedImage) {
152        this.uploadedImage = uploadedImage;
153    }
154
155    @Override
156    public Part getUploadedImage() {
157        return uploadedImage;
158    }
159
160    @Override
161    public String getUploadedImageName() {
162        return uploadedImageName;
163    }
164
165    @Override
166    public void setUploadedImageName(String uploadedImageName) {
167        this.uploadedImageName = uploadedImageName;
168    }
169
170    @Override
171    @SuppressWarnings("unchecked")
172    public String uploadImage() {
173        if (uploadedImage == null) {
174            return null;
175        }
176        DocumentModel doc = navigationContext.getCurrentDocument();
177        List<Map<String, Object>> filesList = (List<Map<String, Object>>) doc.getProperty("files", "files");
178        int fileIndex = filesList == null ? 0 : filesList.size();
179        Map<String, Object> props = new HashMap<String, Object>();
180        Blob blob;
181        try {
182            blob = FileUtils.createBlob(uploadedImage);
183        } catch (IOException e) {
184            throw new NuxeoException(e);
185        }
186        props.put("filename", blob.getFilename());
187        props.put("file", blob);
188        ListDiff listDiff = new ListDiff();
189        listDiff.add(props);
190        doc.setProperty("files", "files", listDiff);
191        documentManager.saveDocument(doc);
192        documentManager.save();
193        imageUrl = DocumentModelFunctions.complexFileUrl("downloadFile", doc, fileIndex, blob.getFilename());
194        isImageUploaded = true;
195        return "editor_image_upload";
196    }
197
198    @Override
199    public boolean getInCreationMode() {
200        DocumentModel doc = navigationContext.getChangeableDocument();
201        if (doc == null || doc.getRef() != null) {
202            // if changeableDocument is null or has an existing ref, assume we
203            // are not in creation and use the currentDocument instead
204            doc = navigationContext.getCurrentDocument();
205        }
206        if (doc == null) {
207            return false;
208        }
209        if (doc.getId() == null) {
210            return true;
211        } else {
212            return !doc.hasSchema(FILES_SCHEMA);
213        }
214    }
215
216    @Override
217    public boolean getHasSearchResults() {
218        return hasSearchResults;
219    }
220
221    @Override
222    public boolean getHasSearchVideosResults() {
223        return hasSearchVideosResults;
224    }
225
226    @Override
227    public List<DocumentModel> getSearchImageResults() {
228        return resultDocuments;
229    }
230
231    @Override
232    public List<DocumentModel> getSearchVideosResults() {
233        return resultVideos;
234    }
235
236    @Override
237    public String getSearchKeywords() {
238        return searchKeywords;
239    }
240
241    @Override
242    public String searchImages() {
243        // Init the list of results
244        resultDocuments = null;
245        // Search the images
246        resultDocuments = searchMedia("Picture");
247        hasSearchResults = !resultDocuments.isEmpty();
248        log.debug("query result contains: " + resultDocuments.size() + " docs.");
249        return "editor_image_upload";
250    }
251
252    /**
253     * @since 5.9.5
254     */
255    @Override
256    public String searchVideos() {
257        // Init the list of results
258        resultVideos = null;
259        // Search the videos
260        resultVideos = searchMedia("Video");
261        hasSearchVideosResults = !resultVideos.isEmpty();
262
263        log.debug("query result contains: " + resultVideos.size() + " videos.");
264        return "editor_image_upload";
265    }
266
267    /**
268     * Generic method to search a media.
269     *
270     * @param typeDocument The type of document to search.
271     * @since 5.9.5
272     */
273    @SuppressWarnings("unchecked")
274    private List<DocumentModel> searchMedia(String typeDocument) {
275        log.debug("Entering searchDocuments with keywords: " + searchKeywords);
276
277        // use page providers
278        PageProviderService ppService = Framework.getService(PageProviderService.class);
279        Map<String, Serializable> props = new HashMap<String, Serializable>();
280        props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY, (Serializable) documentManager);
281        PageProvider<DocumentModel> pp = null;
282        if (searchKeywords != null) {
283            searchKeywords = searchKeywords.trim();
284            if (searchKeywords.length() > 0) {
285                if (!searchKeywords.equals("*")) {
286                    // full text search
287                    pp = (PageProvider<DocumentModel>) ppService.getPageProvider(PP_SEARCH_MEDIA_BY_TITLE, null, null,
288                            null, props, new Object[] { typeDocument, searchKeywords });
289                }
290            }
291        }
292
293        // If the pageprovider is null, we search all medias for the specific type
294        if (pp == null) {
295            pp = (PageProvider<DocumentModel>) ppService.getPageProvider(PP_SEARCH_MEDIA_ALL, null, null, null, props,
296                    new Object[] { typeDocument });
297        }
298        return pp.getCurrentPage();
299    }
300
301    @Override
302    public void setSearchKeywords(final String searchKeywords) {
303        this.searchKeywords = searchKeywords;
304    }
305
306    @Override
307    public List<Map<String, String>> getSizes() {
308        return SIZES;
309    }
310
311    @Override
312    public String getSelectedSize() {
313        return selectedSize;
314    }
315
316    @Override
317    public void setSelectedSize(final String selectedSize) {
318        this.selectedSize = selectedSize;
319    }
320
321    @Override
322    public String getImageProperty() {
323        return selectedSize + ":content";
324    }
325
326    @Override
327    public String getURLVideo(DocumentModel video, String type) {
328
329        if (video == null || type == null) {
330            return null;
331        }
332
333        @SuppressWarnings("unchecked")
334        List<Map<String, Serializable>> transcodedVideos = (List<Map<String, Serializable>>) video.getPropertyValue("vid:transcodedVideos");
335        int position = 0;
336        for (Map<String, Serializable> prop : transcodedVideos) {
337            if (type.equals(prop.get("name"))) {
338                Blob content = (Blob) prop.get("content");
339                String blobPropertyName = "vid:transcodedVideos/" + position + "/content";
340                return DocumentModelFunctions.bigFileUrl(video, blobPropertyName, content.getFilename());
341            }
342            position++;
343        }
344
345        return null;
346    }
347
348}