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