001package de.deepamehta.plugins.images; 002 003import de.deepamehta.core.osgi.PluginActivator; 004import de.deepamehta.core.service.Inject; 005import de.deepamehta.core.Topic; 006import de.deepamehta.core.service.ResultList; 007import de.deepamehta.core.service.Transactional; 008import de.deepamehta.plugins.files.DirectoryListing.FileItem; 009import de.deepamehta.plugins.files.*; 010 011import javax.ws.rs.*; 012import javax.ws.rs.core.Context; 013import javax.ws.rs.core.MediaType; 014import javax.ws.rs.core.UriInfo; 015import javax.ws.rs.ext.RuntimeDelegate; 016import java.io.File; 017import java.util.ArrayList; 018import java.util.logging.Logger; 019 020/** 021 * CKEditor compatible resources for image upload and browse. 022 */ 023@Path("/images") 024public class ImagePlugin extends PluginActivator { 025 026 private static Logger log = Logger.getLogger(ImagePlugin.class.getName()); 027 028 public static final String FILEREPO_BASE_URI_NAME = "filerepo"; 029 public static final String FILEREPO_IMAGES_SUBFOLDER = "images"; 030 // public static final String FILE_REPOSITORY_PATH = System.getProperty("dm4.filerepo.path"); 031 032 @Inject 033 private FilesService fileService; 034 035 @Context 036 private UriInfo uriInfo; 037 038 /** 039 * CKEditor image upload integration, see 040 * CKEDITOR.config.filebrowserImageBrowseUrl 041 * 042 * @param image 043 * Uploaded file resource. 044 * @param func 045 * CKEDITOR function number to call. 046 * @return JavaScript snippet that calls CKEditor 047 */ 048 @POST 049 @Path("/upload") 050 @Consumes(MediaType.MULTIPART_FORM_DATA) 051 @Produces(MediaType.TEXT_HTML) 052 @Transactional 053 public String upload(UploadedFile image, @QueryParam("CKEditorFuncNum") Long func) { 054 log.info("upload image " + image.getName()); 055 createImagesDirectoryInFileRepo(); 056 try { 057 StoredFile file = fileService.storeFile(image, prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER); 058 String path = prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER + File.separator + file.getFileName(); 059 return getCkEditorCall(func, getRepoUri(path), ""); 060 } catch (Exception e) { 061 log.severe(e.getMessage() + ", caused by " + e.getCause().getMessage()); 062 return getCkEditorCall(func, "", e.getMessage()); 063 } 064 } 065 066 /** 067 * Returns a set of all image source URLs. 068 * 069 * @return all image sources 070 */ 071 @GET 072 @Path("/browse") 073 @Produces(MediaType.APPLICATION_JSON) 074 public ResultList<Image> browse() { 075 log.info("browse images in repository path " + prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER); 076 createImagesDirectoryInFileRepo(); 077 try { 078 ArrayList<Image> images = new ArrayList<Image>(); 079 DirectoryListing imagesDirectory= fileService.getDirectoryListing(prefix() + File.separator + 080 FILEREPO_IMAGES_SUBFOLDER); 081 for (FileItem image : imagesDirectory.getFileItems()) { 082 String src = getRepoUri(image.getPath()); 083 images.add(new Image(src, image.getMediaType(), image.getSize(), image.getName())); 084 } 085 return new ResultList<Image>(images.size(), images); 086 } catch (WebApplicationException e) { // fileService.getDirectoryListing 087 throw e; // do not wrap it again 088 } catch (Exception e) { 089 throw new RuntimeException(e); 090 } 091 } 092 093 private void createImagesDirectoryInFileRepo() { 094 try { 095 // check image file repository 096 ResourceInfo resourceInfo = fileService.getResourceInfo(prefix() + File.separator + 097 FILEREPO_IMAGES_SUBFOLDER); 098 // depending on prefix() we check for an "images" folder in the global or workspace filerepo 099 if (resourceInfo.getItemKind() != ItemKind.DIRECTORY) { 100 String message = "ImagePlugin: \"images\" storage directory in repo path " + fileService.getFile("/") + 101 prefix() + File.separator + ImagePlugin.FILEREPO_IMAGES_SUBFOLDER + " can not be used"; 102 throw new IllegalStateException(message); 103 } 104 } catch (WebApplicationException e) { 105 log.info("Created the \"images\" subfolder on the fly for new filerepo in " + fileService.getFile("/") + 106 prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER + "!"); 107 try { 108 fileService.createFolder(FILEREPO_IMAGES_SUBFOLDER, prefix()); 109 } catch (RuntimeException ex) { 110 log.warning("RuntimeException caught during folder creation, presumably the folder " + 111 "must already EXIST, so OK:" + ex.getMessage()); 112 } 113 // catch fileService create request failed because of: folder exists 114 } catch (Exception e) { 115 throw new RuntimeException(e); 116 } 117 } 118 119 /** 120 * Returns a in-line JavaScript snippet that calls the parent CKEditor. 121 * 122 * @param func 123 * CKEDITOR function number. 124 * @param uri 125 * Resource URI. 126 * @param error 127 * Error message. 128 * @return JavaScript snippet that calls CKEditor 129 */ 130 private String getCkEditorCall(Long func, String uri, String error) { 131 return "<script type='text/javascript'>" + "window.parent.CKEDITOR.tools.callFunction(" 132 + func + ", '" + uri + "', '" + error + "')" + "</script>"; 133 } 134 135 /** 136 * Returns an external accessible file repository URI of path based on 137 * actual request URI. 138 * 139 * @param path 140 * Relative path of a file repository resource. 141 * @return URI 142 */ 143 private String getRepoUri(String path) { 144 // ### in some cases the uriInfo (Context) may be empty! 145 return uriInfo.getBaseUri() + FILEREPO_BASE_URI_NAME + path; 146 } 147 148 private String prefix() { 149 File repo = fileService.getFile("/"); 150 return ((FilesPlugin) fileService).repoPath(repo); 151 } 152 153}