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