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