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("file", blob);
189        ListDiff listDiff = new ListDiff();
190        listDiff.add(props);
191        doc.setProperty("files", "files", listDiff);
192        documentManager.saveDocument(doc);
193        documentManager.save();
194        imageUrl = DocumentModelFunctions.complexFileUrl("downloadFile", doc, fileIndex, blob.getFilename());
195        isImageUploaded = true;
196        return "editor_image_upload";
197    }
198
199    @Override
200    public boolean getInCreationMode() {
201        DocumentModel doc = navigationContext.getChangeableDocument();
202        if (doc == null || doc.getRef() != null) {
203            // if changeableDocument is null or has an existing ref, assume we
204            // are not in creation and use the currentDocument instead
205            doc = navigationContext.getCurrentDocument();
206        }
207        if (doc == null) {
208            return false;
209        }
210        if (doc.getId() == null) {
211            return true;
212        } else {
213            return !doc.hasSchema(FILES_SCHEMA);
214        }
215    }
216
217    @Override
218    public boolean getHasSearchResults() {
219        return hasSearchResults;
220    }
221
222    @Override
223    public boolean getHasSearchVideosResults() {
224        return hasSearchVideosResults;
225    }
226
227    @Override
228    public List<DocumentModel> getSearchImageResults() {
229        return resultDocuments;
230    }
231
232    @Override
233    public List<DocumentModel> getSearchVideosResults() {
234        return resultVideos;
235    }
236
237    @Override
238    public String getSearchKeywords() {
239        return searchKeywords;
240    }
241
242    @Override
243    public String searchImages() {
244        // Init the list of results
245        resultDocuments = null;
246        // Search the images
247        resultDocuments = searchMedia("Picture");
248        hasSearchResults = !resultDocuments.isEmpty();
249        log.debug("query result contains: " + resultDocuments.size() + " docs.");
250        return "editor_image_upload";
251    }
252
253    /**
254     * @since 5.9.5
255     */
256    @Override
257    public String searchVideos() {
258        // Init the list of results
259        resultVideos = null;
260        // Search the videos
261        resultVideos = searchMedia("Video");
262        hasSearchVideosResults = !resultVideos.isEmpty();
263
264        log.debug("query result contains: " + resultVideos.size() + " videos.");
265        return "editor_image_upload";
266    }
267
268    /**
269     * Generic method to search a media.
270     *
271     * @param typeDocument The type of document to search.
272     * @since 5.9.5
273     */
274    @SuppressWarnings("unchecked")
275    private List<DocumentModel> searchMedia(String typeDocument) {
276        log.debug("Entering searchDocuments with keywords: " + searchKeywords);
277
278        // use page providers
279        PageProviderService ppService = Framework.getService(PageProviderService.class);
280        Map<String, Serializable> props = new HashMap<String, Serializable>();
281        props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY, (Serializable) documentManager);
282        PageProvider<DocumentModel> pp = null;
283        if (searchKeywords != null) {
284            searchKeywords = searchKeywords.trim();
285            if (searchKeywords.length() > 0) {
286                if (!searchKeywords.equals("*")) {
287                    // full text search
288                    pp = (PageProvider<DocumentModel>) ppService.getPageProvider(PP_SEARCH_MEDIA_BY_TITLE, null, null,
289                            null, props, new Object[] { typeDocument, searchKeywords });
290                }
291            }
292        }
293
294        // If the pageprovider is null, we search all medias for the specific type
295        if (pp == null) {
296            pp = (PageProvider<DocumentModel>) ppService.getPageProvider(PP_SEARCH_MEDIA_ALL, null, null, null, props,
297                    new Object[] { typeDocument });
298        }
299        return pp.getCurrentPage();
300    }
301
302    @Override
303    public void setSearchKeywords(final String searchKeywords) {
304        this.searchKeywords = searchKeywords;
305    }
306
307    @Override
308    public List<Map<String, String>> getSizes() {
309        return SIZES;
310    }
311
312    @Override
313    public String getSelectedSize() {
314        return selectedSize;
315    }
316
317    @Override
318    public void setSelectedSize(final String selectedSize) {
319        this.selectedSize = selectedSize;
320    }
321
322    @Override
323    public String getImageProperty() {
324        return selectedSize + ":content";
325    }
326
327    @Override
328    public String getURLVideo(DocumentModel video, String type) {
329
330        if (video == null || type == null) {
331            return null;
332        }
333
334        @SuppressWarnings("unchecked")
335        List<Map<String, Serializable>> transcodedVideos = (List<Map<String, Serializable>>) video.getPropertyValue("vid:transcodedVideos");
336        int position = 0;
337        for (Map<String, Serializable> prop : transcodedVideos) {
338            if (type.equals(prop.get("name"))) {
339                Blob content = (Blob) prop.get("content");
340                String blobPropertyName = "vid:transcodedVideos/" + position + "/content";
341                return DocumentModelFunctions.bigFileUrl(video, blobPropertyName, content.getFilename());
342            }
343            position++;
344        }
345
346        return null;
347    }
348
349}