com.celements.photo.plugin.CelementsPhotoPlugin.java Source code

Java tutorial

Introduction

Here is the source code for com.celements.photo.plugin.CelementsPhotoPlugin.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package com.celements.photo.plugin;

import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.celements.photo.container.ImageDimensions;
import com.celements.photo.container.ImageLibStrings;
import com.celements.photo.container.ImageStrings;
import com.celements.photo.container.Metadate;
import com.celements.photo.container.PhotoAlbumClass;
import com.celements.photo.container.PhotoImageClass;
import com.celements.photo.container.PhotoMetainfoClass;
import com.celements.photo.image.GenerateThumbnail;
import com.celements.photo.image.Image;
import com.celements.photo.image.Thumbnail;
import com.celements.photo.metadata.Metainfo;
import com.celements.photo.plugin.cmd.ComputeImageCommand;
import com.celements.photo.service.IImageService;
import com.celements.photo.utilities.AddAttachmentToDoc;
import com.celements.photo.utilities.ImportFileObject;
import com.celements.photo.utilities.Unzip;
import com.drew.metadata.MetadataException;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.api.Api;
import com.xpn.xwiki.doc.XWikiAttachment;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.classes.BaseClass;
import com.xpn.xwiki.plugin.XWikiDefaultPlugin;
import com.xpn.xwiki.plugin.XWikiPluginInterface;
import com.xpn.xwiki.web.Utils;

public class CelementsPhotoPlugin extends XWikiDefaultPlugin {

    private static final Log LOGGER = LogFactory.getFactory().getInstance(CelementsPhotoPlugin.class);

    /**
     * The image formats supported by the image plugin
     */
    public enum SupportedFormat {
        JPG(1, "image/jpg"), JPEG(1, "image/jpeg"), PNG(2, "image/png"), GIF(3, "image/gif"), BMP(4, "image/bmp");

        /**
         * The mime type associated to the supported format
         */
        private String mimeType;

        /**
         * A integer code used to generate the image cache key
         */
        private int code;

        SupportedFormat(int code, String mimeType) {
            this.mimeType = mimeType;
            this.code = code;
        }

        public int getCode() {
            return this.code;
        }

        public String getMimeType() {
            return this.mimeType;
        }
    }

    private Image image;
    private Thumbnail thumbnail;
    private Metainfo metainfo;

    private ComputeImageCommand computeImgCmd;

    // PLUGIN .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.
    public CelementsPhotoPlugin(String name, String className, XWikiContext context) {
        super(name, className, context);
        image = new Image();
        thumbnail = new Thumbnail();
        metainfo = new Metainfo();
        init(context);
    }

    public String getName() {
        return "celementsphoto";
    }

    public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context) {
        return new CelementsPhotoPluginAPI((CelementsPhotoPlugin) plugin, context);
    }

    public void virtualInit(XWikiContext context) {
        //TODO move to where it's first needed, not all are using these classes
        // set up the wiki for the photo plugin
        try {
            generatePhotoMetainfoClass(context);
            generatePhotoAlbumClass(context);
            generatePhotoImageClass(context);
        } catch (XWikiException xe) {
            //no problem, class can be generated later or manually
            LOGGER.error(xe);
        }
    }

    public void init(XWikiContext context) {
        super.init(context);
    }

    public void flushCache() {
        //DO NOT FLUSH IMAGE CACHE, BECAUSE IT IS ON DISK AND DOES NOT HELP TO FREE MEMORY!
    }

    public void flushImageCache() {
        getComputeImgCmd().flushCache();
    }

    public XWikiAttachment downloadAttachment(XWikiAttachment attachment, XWikiContext context) {
        if (this.isSupportedImageFormat(attachment.getMimeType(context))) {
            String sheight = context.getRequest().getParameter("celheight");
            String swidth = context.getRequest().getParameter("celwidth");
            String copyright = context.getRequest().getParameter("copyright");
            String watermark = context.getRequest().getParameter("watermark");
            Color defaultBg = null;
            String defaultBgString = context.getRequest().getParameter("background");
            return getComputeImgCmd().computeImage(attachment, context, attachment, sheight, swidth, copyright,
                    watermark, defaultBg, defaultBgString);
        }
        return attachment;
    }

    ComputeImageCommand getComputeImgCmd() {
        if (computeImgCmd == null) {
            computeImgCmd = new ComputeImageCommand();
        }
        return computeImgCmd;
    }

    /**
     * @return true if the passed mime type is supported by the plugin, false otherwise.
     */
    public boolean isSupportedImageFormat(String mimeType) {
        for (SupportedFormat f : SupportedFormat.values()) {
            if (f.getMimeType().equals(mimeType)) {
                return true;
            }
        }
        return false;
    }

    // IMAGE .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:
    /**
     * Returns an array of ImageStrings for all images in the specified album.
     * 
     * @see com.celements.photo.plugin.container.ImageStrings
     * @param doc XWikiDocument of the album.
     * @param width Desired maximum width of the thumbnails (aspect ratio is maintained).
     * @param height Desired maximum height of the thumbnails (aspect ratio is maintained).
     * @param context XWikiContext
     * @return Array of ImageStrings.
     * @throws XWikiException
     * @throws IOException
     */
    public ImageStrings[] getImageList(XWikiDocument doc, int width, int height, XWikiContext context)
            throws XWikiException, IOException {
        return image.getImageList(doc, width, height, thumbnail, context);
    }

    /**
     * Returns an array of ImageStrings for all images in the specified album,
     * excluding the link to a thumbnail. This method's primar use is to get
     * the image's id.
     * 
     * @see com.celements.photo.plugin.container.ImageStrings
     * @param doc XWikiDocument of the album.
     * @param context XWikiContext
     * @return Array of ImageStrings.
     * @throws XWikiException
     * @throws IOException
     */
    public ImageStrings[] getImageListExclThumbs(XWikiDocument doc, XWikiContext context)
            throws XWikiException, IOException {
        return image.getImageListExclThumbs(doc, context);
    }

    /**
     * Returns wether the specified image is marked as deleted or not.
     * 
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param context XWikiContext
     * @return true if the image is tagged as deleted.
     * @throws XWikiException
     */
    public boolean isImageDeleted(XWikiDocument doc, String id, XWikiContext context) throws XWikiException {
        return this.image.isDeleted(doc, id, context);
    }

    /**
     * Set the "deleted" tag for the image to the specified value.
     * 
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param deleted true to tag the image as deleted.
     * @param XWikiContext
     * @throws XWikiException
     */
    public void setImageDeleted(XWikiDocument doc, String id, boolean deleted, XWikiContext context)
            throws XWikiException {
        this.image.setDeleted(doc, id, deleted, context);
    }

    // THUMBNAIL .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:  
    /**
     * Returns the URL to the thumbnail of a certain image in the specified
     * size. If the thumbnail does not exist it is created.
     * 
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param width Desired width for the thumb
     * @param height Desired height for the thumb
     * @param context XWikiContext
     * @return The download URL for the thumb
     * @throws XWikiException
     * @throws IOException
     */
    public String getThumbnailUrl(XWikiDocument doc, String id, int width, int height, XWikiContext context)
            throws XWikiException, IOException {
        return thumbnail.getUrl(doc, id, width, height, context);
    }

    // METADATA .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.
    /**
     * Returns the specified metatag. If there is no metatag with the specified
     * name an empty metadate is returned.
     * 
     * @see com.celements.photo.plugin.container.Metadate
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param tag The name of the desired metatag.
     * @param context XWikiContext
     * @return The specified metatag as a Metadate object.
     * @throws XWikiException
     * @throws MetadataException
     * @throws IOException
     */
    public Metadate getMetatag(XWikiDocument doc, String id, String tag, XWikiContext context)
            throws XWikiException, MetadataException, IOException {
        return metainfo.getTag(doc, id, tag, context);
    }

    /**
     * Returns all metatags contained in the image, excluding "Unknown tag" tags.
     * 
     * @see com.celements.photo.plugin.container.Metadate
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param XWikiContext
     * @return Array of Metadate objects.
     * @throws XWikiException
     * @throws MetadataException
     * @throws IOException
     */
    public Metadate[] getMetadata(XWikiDocument doc, String id, XWikiContext context)
            throws XWikiException, MetadataException, IOException {
        return metainfo.getMetadataWithCondition(doc, id, ImageLibStrings.METATAG_UNKNOWN_TAG, context);
    }

    /**
     * Returns all metatags contained in the image, including "Unknown tag" tags.
     * 
     * @see com.celements.photo.plugin.container.Metadate
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param XWikiContext
     * @return Array of Metadate objects.
     * @throws XWikiException
     * @throws MetadataException
     * @throws IOException
     */
    public Metadate[] getMetadataFull(XWikiDocument doc, String id, XWikiContext context)
            throws XWikiException, MetadataException, IOException {
        return metainfo.getMetadataWithCondition(doc, id, "", context);
    }

    // DATA MANIPULATION .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:
    /**
     * Deletes the document with the metadata attached (only the metadata 
     * extracted from the image is deleted, but not the data generated by 
     * CelementsPhotoPlugin).
     * 
     * @param doc XWikiDocument of the album.
     * @param id Id of the image.
     * @param context XWikiContext
     * @throws XWikiException
     * @throws IOException
     */
    public void forceClearMetadata(XWikiDocument doc, String id, XWikiContext context)
            throws XWikiException, IOException {
        XWikiDocument metadataDoc = context.getWiki().getDocument(ImageLibStrings.getPhotoSpace(doc),
                doc.getName() + ImageLibStrings.DOCUMENT_SEPARATOR_IMAGE + id, context);
        context.getWiki().deleteDocument(metadataDoc, context);
    }

    // CLASSES .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:
    /**
     * Generates the PhotoMetainfoClass with the fields 'name' and 'description'.
     * 
     * @param context The XWikiContext used to get the xwiki and save.
     * @return A BaseObject of the PhotoMetainfoClass
     * @throws XWikiException
     */
    public BaseClass generatePhotoMetainfoClass(XWikiContext context) throws XWikiException {
        return (new PhotoMetainfoClass()).getNewPhotoMetainfoClass(context);
    }

    /**
     * Generates the PhotoAlbumClass with fields for the data space, watermark 
     * and copyright.
     * 
     * @param context The XWikiContext used to get the xwiki and save.
     * @return A BaseObject of the PhotoAlbumClass
     * @throws XWikiException
     */
    public BaseClass generatePhotoAlbumClass(XWikiContext context) throws XWikiException {
        return (new PhotoAlbumClass()).getNewPhotoAlbumClass(context);
    }

    /**
     * Generates the PhotoImageClass with fields for different fields to cash
     * information about the image.
     * 
     * @param context The XWikiContext used to get the xwiki and save.
     * @return A BaseObject of the PhotoImageClass
     * @throws XWikiException
     */
    public BaseClass generatePhotoImageClass(XWikiContext context) throws XWikiException {
        return (new PhotoImageClass()).getNewPhotoImageClass(context);
    }

    /**
     * Get a List of all attachments in the specified archive and the suggested 
     * action when importing.
     * 
     * @param importFile Zip archive to check the files for the existence in the gallery.
     * @param galleryDoc Gallery Document to check for the files.
     * @param context XWikiContext
     * @return List of {@link ImportFileObject} for each file.
     * @throws XWikiException
     */
    public List<ImportFileObject> getAttachmentFileListWithActions(XWikiAttachment importFile,
            XWikiDocument galleryDoc, XWikiContext context) throws XWikiException {
        List<ImportFileObject> resultList = new ArrayList<ImportFileObject>();

        if (importFile != null) {
            if (isZipFile(importFile, context)) {
                List<String> fileList = (new Unzip()).getZipContentList(importFile.getContent(context));

                String fileSep = System.getProperty("file.separator");
                for (Iterator<String> fileIterator = fileList.iterator(); fileIterator.hasNext();) {
                    String fileName = (String) fileIterator.next();
                    if (!fileName.endsWith(fileSep) && !fileName.startsWith(".")
                            && !fileName.contains(fileSep + ".")) {
                        ImportFileObject file = new ImportFileObject(fileName,
                                getActionForFile(fileName, galleryDoc, context));
                        resultList.add(file);
                    }
                }
            } else if (isImgFile(importFile, context)) {
                ImportFileObject file = new ImportFileObject(importFile.getFilename(),
                        getActionForFile(importFile.getFilename(), galleryDoc, context));
                resultList.add(file);
            }
        } else {
            LOGGER.error("zipFile='null' - galleryDoc='" + galleryDoc.getFullName() + "'");
        }

        return resultList;
    }

    /**
     * For a given filename return if, in the specified gallery, its import 
     * should be added, overwritten or skiped.
     * 
     * @param fileName Filename of the file to check.
     * @param galleryDoc Document of the gallery to check if the file already exists.
     * @return action when importing: -1 skip, 0 overwrite, 1 add
     */
    private short getActionForFile(String fileName, XWikiDocument galleryDoc, XWikiContext context) {
        short action = ImportFileObject.ACTION_SKIP;
        if (isImgFile(fileName)) {
            fileName = fileName.replace(System.getProperty("file.separator"), ".");
            fileName = context.getWiki().clearName(fileName, false, true, context);
            XWikiAttachment attachment = galleryDoc.getAttachment(fileName);
            if (attachment == null) {
                action = ImportFileObject.ACTION_ADD;
            } else {
                action = ImportFileObject.ACTION_OVERWRITE;
            }
        }

        return action;
    }

    /**
     * Get a specified image file in a zip archive, extract it, change it to the 
     * desired size and save it as an attachment to the given page.
     * 
     * @param zipFile File containing the image to extract.
     * @param unzipFileName Filename of the image to extract.
     * @param attachToDoc Document to attach the extracted and resized image.
     * @param width Width (max - aspect ratio is maintained) to resize the image to.
     * @param height Height (max - aspect ratio is maintained) to resize the image to.
     * @param context XWikiContezt
     * @throws XWikiException
     */
    public void unzipFileToAttachment(XWikiAttachment zipFile, String unzipFileName, XWikiDocument attachToDoc,
            int width, int height, XWikiContext context) throws XWikiException {
        LOGGER.info("START: zip='" + zipFile.getFilename() + "' file='" + unzipFileName + "' gallery='"
                + attachToDoc + "' " + "width='" + width + "' height='" + height + "'");
        try {
            ByteArrayInputStream imgFullSize = null;
            if (isZipFile(zipFile, context)) {
                imgFullSize = new ByteArrayInputStream(
                        (new Unzip()).getFile(zipFile.getContent(context), unzipFileName).toByteArray());
            } else if (isImgFile(zipFile, context)) {
                imgFullSize = new ByteArrayInputStream(zipFile.getContent(context));
            }
            //TODO is there a better way to find the mime type of the file in the in stream?
            //      -> look at http://tika.apache.org/ or maybe in image magic?
            String mimeType = "png";
            if ((unzipFileName.lastIndexOf('.') > -1) && (!unzipFileName.endsWith("."))) {
                mimeType = unzipFileName.substring(unzipFileName.lastIndexOf('.') + 1);
            }
            LOGGER.debug("unzip mimetype is " + mimeType);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageDimensions id = (new GenerateThumbnail()).createThumbnail(imgFullSize, out, width, height, null,
                    null, mimeType, null);
            LOGGER.info("width='" + id.width + "' height='" + id.height + "'");
            LOGGER.info("output stream size: " + out.size());
            unzipFileName = unzipFileName.replace(System.getProperty("file.separator"), ".");
            unzipFileName = context.getWiki().clearName(unzipFileName, false, true, context);
            XWikiAttachment att = (new AddAttachmentToDoc()).addAtachment(attachToDoc, out.toByteArray(),
                    unzipFileName, context);
            LOGGER.info("attachment='" + att.getFilename() + "', gallery='" + att.getDoc().getFullName()
                    + "' size='" + att.getFilesize() + "'");
        } catch (IOException e) {
            LOGGER.error(e);
        }
        LOGGER.info("END file='" + unzipFileName + "'");
    }

    private boolean isZipFile(XWikiAttachment file, XWikiContext context) {
        return file.getMimeType(context).equalsIgnoreCase(ImageLibStrings.MIME_ZIP)
                || file.getMimeType(context).equalsIgnoreCase(ImageLibStrings.MIME_ZIP_MICROSOFT);
    }

    private boolean isImgFile(XWikiAttachment file, XWikiContext context) {
        return file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_BMP)
                || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_GIF)
                || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_JPE)
                || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_JPG)
                || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_JPEG)
                || file.getMimeType(context).equalsIgnoreCase("image/" + ImageLibStrings.MIME_PNG);
    }

    private boolean isImgFile(String fileName) {
        return fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_BMP)
                || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_GIF)
                || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_JPE)
                || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_JPG)
                || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_JPEG)
                || fileName.toLowerCase().endsWith("." + ImageLibStrings.MIME_PNG);
    }

    private IImageService getImageService() {
        return Utils.getComponent(IImageService.class);
    }

    /**
     * @deprecated instead use getDimension from ImageService.
     */
    @Deprecated
    public ImageDimensions getDimension(String imageFullName, XWikiContext context) throws XWikiException {
        LOGGER.warn("deprecated getDimension used!");
        return getImageService().getDimension(imageFullName);
    }

}