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