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 DM4_HOST_URL = System.getProperty("dm4.host.url"); 031 // public static final String FILE_REPOSITORY_PATH = System.getProperty("dm4.filerepo.path"); 032 033 @Inject 034 private FilesService fileService; 035 036 @Context 037 private UriInfo uriInfo; 038 039 /** 040 * CKEditor image upload integration, see 041 * CKEDITOR.config.filebrowserImageBrowseUrl 042 * 043 * @param image 044 * Uploaded file resource. 045 * @param func 046 * CKEDITOR function number to call. 047 * @return JavaScript snippet that calls CKEditor 048 */ 049 @POST 050 @Path("/upload/ckeditor") 051 @Consumes(MediaType.MULTIPART_FORM_DATA) 052 @Produces(MediaType.TEXT_HTML) 053 @Transactional 054 public String uploadCKEditor(UploadedFile image, @QueryParam("CKEditorFuncNum") Long func) { 055 log.info("upload image " + image.getName()); 056 createImagesDirectoryInFileRepo(); 057 try { 058 StoredFile file = fileService.storeFile(image, prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER); 059 String path = prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER + File.separator + file.getFileName(); 060 return getCkEditorCall(func, getRepoUri(path), ""); 061 } catch (Exception e) { 062 log.severe(e.getMessage() + ", caused by " + e.getCause().getMessage()); 063 return getCkEditorCall(func, "", e.getMessage()); 064 } 065 } 066 067 /** 068 * Standard image upload integration. 069 * @param image Uploaded file resource. 070 * @return topic File Topic 071 */ 072 @POST 073 @Path("/upload") 074 @Consumes(MediaType.MULTIPART_FORM_DATA) 075 @Produces(MediaType.APPLICATION_JSON) 076 @Transactional 077 public Topic upload(UploadedFile image, @QueryParam("CKEditorFuncNum") Long func) { 078 log.info("upload image " + image.getName()); 079 createImagesDirectoryInFileRepo(); 080 try { 081 StoredFile file = fileService.storeFile(image, prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER); 082 return fileService.getFileTopic(prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER + File.separator + file.getFileName()); 083 } catch (Exception e) { 084 log.severe(e.getMessage() + ", caused by " + e.getCause().getMessage()); 085 return null; 086 } 087 } 088 089 /** 090 * Returns a set of all image source URLs. 091 * 092 * @return all image sources 093 */ 094 @GET 095 @Path("/browse") 096 @Produces(MediaType.APPLICATION_JSON) 097 public ResultList<Image> browse() { 098 log.info("browse images in repository path " + prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER); 099 createImagesDirectoryInFileRepo(); 100 try { 101 ArrayList<Image> images = new ArrayList<Image>(); 102 DirectoryListing imagesDirectory= fileService.getDirectoryListing(prefix() + File.separator + 103 FILEREPO_IMAGES_SUBFOLDER); 104 for (FileItem image : imagesDirectory.getFileItems()) { 105 String src = getRepoUri(image.getPath()); 106 images.add(new Image(src, image.getMediaType(), image.getSize(), image.getName())); 107 } 108 return new ResultList<Image>(images.size(), images); 109 } catch (WebApplicationException e) { // fileService.getDirectoryListing 110 throw e; // do not wrap it again 111 } catch (Exception e) { 112 throw new RuntimeException(e); 113 } 114 } 115 116 private void createImagesDirectoryInFileRepo() { 117 try { 118 // check image file repository 119 ResourceInfo resourceInfo = fileService.getResourceInfo(prefix() + File.separator + 120 FILEREPO_IMAGES_SUBFOLDER); 121 // depending on prefix() we check for an "images" folder in the global or workspace filerepo 122 if (resourceInfo.getItemKind() != ItemKind.DIRECTORY) { 123 String message = "ImagePlugin: \"images\" storage directory in repo path " + fileService.getFile("/") + 124 prefix() + File.separator + ImagePlugin.FILEREPO_IMAGES_SUBFOLDER + " can not be used"; 125 throw new IllegalStateException(message); 126 } 127 } catch (WebApplicationException e) { 128 log.info("Created the \"images\" subfolder on the fly for new filerepo in " + fileService.getFile("/") + 129 prefix() + File.separator + FILEREPO_IMAGES_SUBFOLDER + "!"); 130 try { 131 fileService.createFolder(FILEREPO_IMAGES_SUBFOLDER, prefix()); 132 } catch (RuntimeException ex) { 133 log.warning("RuntimeException caught during folder creation, presumably the folder " + 134 "must already EXIST, so OK:" + ex.getMessage()); 135 } 136 // catch fileService create request failed because of: folder exists 137 } catch (Exception e) { 138 throw new RuntimeException(e); 139 } 140 } 141 142 /** 143 * Returns a in-line JavaScript snippet that calls the parent CKEditor. 144 * 145 * @param func 146 * CKEDITOR function number. 147 * @param uri 148 * Resource URI. 149 * @param error 150 * Error message. 151 * @return JavaScript snippet that calls CKEditor 152 */ 153 private String getCkEditorCall(Long func, String uri, String error) { 154 return "<script type='text/javascript'>" + "window.parent.CKEDITOR.tools.callFunction(" 155 + func + ", '" + uri + "', '" + error + "')" + "</script>"; 156 } 157 158 /** 159 * Returns an external accessible file repository URI of path based on the 160 * <code>dm4.host.url</code> platform configuration option. 161 * 162 * @param path 163 * Relative path of a file repository resource. 164 * @return URI 165 */ 166 private String getRepoUri(String path) { 167 return DM4_HOST_URL + FILEREPO_BASE_URI_NAME + path; 168 } 169 170 private String prefix() { 171 File repo = fileService.getFile("/"); 172 return ((FilesPlugin) fileService).repoPath(repo); 173 } 174 175}