001    package de.deepamehta.plugins.images;
002    
003    import java.io.File;
004    import java.util.HashSet;
005    import java.util.Set;
006    import java.util.logging.Logger;
007    
008    import javax.ws.rs.Consumes;
009    import javax.ws.rs.GET;
010    import javax.ws.rs.HeaderParam;
011    import javax.ws.rs.POST;
012    import javax.ws.rs.Path;
013    import javax.ws.rs.Produces;
014    import javax.ws.rs.QueryParam;
015    import javax.ws.rs.WebApplicationException;
016    import javax.ws.rs.core.Context;
017    import javax.ws.rs.core.MediaType;
018    import javax.ws.rs.core.UriInfo;
019    
020    import de.deepamehta.core.ResultSet;
021    import de.deepamehta.core.osgi.PluginActivator;
022    import de.deepamehta.core.service.ClientState;
023    import de.deepamehta.core.service.PluginService;
024    import de.deepamehta.core.service.annotation.ConsumesService;
025    import de.deepamehta.plugins.files.DirectoryListing.FileItem;
026    import de.deepamehta.plugins.files.ItemKind;
027    import de.deepamehta.plugins.files.ResourceInfo;
028    import de.deepamehta.plugins.files.StoredFile;
029    import de.deepamehta.plugins.files.UploadedFile;
030    import de.deepamehta.plugins.files.service.FilesService;
031    
032    /**
033     * CKEditor compatible resources for image upload and browse.
034     */
035    @Path("/images")
036    public class ImagePlugin extends PluginActivator {
037    
038        public static final String IMAGES = "images";
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        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        public String upload(//
066                UploadedFile image,//
067                @QueryParam("CKEditorFuncNum") Long func,//
068                @HeaderParam("Cookie") ClientState cookie) {
069            log.info("upload image " + image.getName());
070            try {
071                StoredFile file = fileService.storeFile(image, IMAGES, cookie);
072                String path = "/" + IMAGES + "/" + 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 ResultSet<Image> browse() {
088            log.info("browse images");
089            Set<Image> images = new HashSet<Image>();
090            for (FileItem image : fileService.getDirectoryListing(IMAGES).getFileItems()) {
091                String src = getRepoUri(image.getPath());
092                images.add(new Image(src, image.getMediaType(), image.getSize()));
093            }
094            return new ResultSet<Image>(images.size(), images);
095        }
096    
097        /**
098         * Nullify file service reference.
099         */
100        @Override
101        public void serviceGone(PluginService service) {
102            if (service == fileService) {
103                fileService = null;
104            }
105        }
106    
107        /**
108         * Reference the file service and create the repository path if necessary.
109         */
110        @Override
111        @ConsumesService("de.deepamehta.plugins.files.service.FilesService")
112        public void serviceArrived(PluginService service) {
113            if (service instanceof FilesService) {
114                log.fine("file service arrived");
115                fileService = (FilesService) service;
116                postInstallMigration();
117            }
118        }
119    
120        private void postInstallMigration() {
121            // TODO move the initialization to migration "0"
122            try {
123                // check image file repository
124                ResourceInfo resourceInfo = fileService.getResourceInfo(IMAGES);
125                if (resourceInfo.getItemKind() != ItemKind.DIRECTORY) {
126                    String message = "image storage directory " + FILE_REPOSITORY_PATH + File.separator
127                            + IMAGES + " can not be used";
128                    throw new IllegalStateException(message);
129                }
130            } catch (WebApplicationException e) {
131                // catch fileService info request error
132                if (e.getResponse().getStatus() != 404) {
133                    throw e;
134                } else {
135                    log.info("create image directory");
136                    fileService.createFolder(IMAGES, "/");
137                }
138            }
139        }
140    
141        /**
142         * Returns a in-line JavaScript snippet that calls the parent CKEditor.
143         * 
144         * @param func
145         *            CKEDITOR function number.
146         * @param uri
147         *            Resource URI.
148         * @param error
149         *            Error message.
150         * @return JavaScript snippet that calls CKEditor
151         */
152        private String getCkEditorCall(Long func, String uri, String error) {
153            return "<script type='text/javascript'>" + "window.parent.CKEDITOR.tools.callFunction("
154                    + func + ", '" + uri + "', '" + error + "')" + "</script>";
155        }
156    
157        /**
158         * Returns an external accessible file repository URI of path based on
159         * actual request URI.
160         * 
161         * @param path
162         *            Relative path of a file repository resource.
163         * @return URI
164         */
165        private String getRepoUri(String path) {
166            return uriInfo.getBaseUri() + "filerepo" + path;
167        }
168    }