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