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.POST;
009 import javax.ws.rs.Path;
010 import javax.ws.rs.Produces;
011 import javax.ws.rs.QueryParam;
012 import javax.ws.rs.WebApplicationException;
013 import javax.ws.rs.core.Context;
014 import javax.ws.rs.core.MediaType;
015 import javax.ws.rs.core.UriInfo;
016
017 import de.deepamehta.core.osgi.PluginActivator;
018 import de.deepamehta.core.service.Inject;
019 import de.deepamehta.core.service.PluginService;
020 import de.deepamehta.core.service.ResultList;
021 import de.deepamehta.core.service.Transactional;
022 import de.deepamehta.plugins.files.DirectoryListing.FileItem;
023 import de.deepamehta.plugins.files.ItemKind;
024 import de.deepamehta.plugins.files.ResourceInfo;
025 import de.deepamehta.plugins.files.StoredFile;
026 import de.deepamehta.plugins.files.UploadedFile;
027 import de.deepamehta.plugins.files.service.FilesService;
028 import java.util.ArrayList;
029
030 /**
031 * CKEditor compatible resources for image upload and browse.
032 */
033 @Path("/images")
034 public class ImagePlugin extends PluginActivator {
035
036 public static final String IMAGES = "images";
037
038 public static final String FILEREPO_BASE_URI_NAME = "filerepo";
039
040 private static final String FILE_REPOSITORY_PATH = System.getProperty("dm4.filerepo.path");
041
042 private static Logger log = Logger.getLogger(ImagePlugin.class.getName());
043
044 @Inject
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 @Transactional
067 public String upload(//
068 UploadedFile image,//
069 @QueryParam("CKEditorFuncNum") Long func) {
070 log.info("upload image " + image.getName());
071 try {
072 StoredFile file = fileService.storeFile(image, IMAGES);
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 public void serviceArrived(PluginService service) {
120 if (service instanceof FilesService) {
121 log.fine("file service arrived");
122 fileService = (FilesService) service;
123 postInstallMigration();
124 }
125 }
126
127 private void postInstallMigration() {
128 // TODO move the initialization to migration "0"
129 try {
130 // check image file repository
131 ResourceInfo resourceInfo = fileService.getResourceInfo(IMAGES);
132 if (resourceInfo.getItemKind() != ItemKind.DIRECTORY) {
133 String message = "image storage directory " + FILE_REPOSITORY_PATH + File.separator
134 + IMAGES + " can not be used";
135 throw new IllegalStateException(message);
136 }
137 } catch (WebApplicationException e) {
138 // catch fileService info request error
139 if (e.getResponse().getStatus() != 404) {
140 throw e;
141 } else {
142 log.info("create image directory");
143 fileService.createFolder(IMAGES, "/");
144 }
145 } catch (Exception e) {
146 throw new RuntimeException(e);
147 }
148 }
149
150 /**
151 * Returns a in-line JavaScript snippet that calls the parent CKEditor.
152 *
153 * @param func
154 * CKEDITOR function number.
155 * @param uri
156 * Resource URI.
157 * @param error
158 * Error message.
159 * @return JavaScript snippet that calls CKEditor
160 */
161 private String getCkEditorCall(Long func, String uri, String error) {
162 return "<script type='text/javascript'>" + "window.parent.CKEDITOR.tools.callFunction("
163 + func + ", '" + uri + "', '" + error + "')" + "</script>";
164 }
165
166 /**
167 * Returns an external accessible file repository URI of path based on
168 * actual request URI.
169 *
170 * @param path
171 * Relative path of a file repository resource.
172 * @return URI
173 */
174 private String getRepoUri(String path) {
175 // ### in some cases the uriInfo (Context) may be empty!
176 return uriInfo.getBaseUri() + FILEREPO_BASE_URI_NAME + path;
177 }
178 }