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}