001/*
002 * (C) Copyright 2008-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 */
019package org.nuxeo.ecm.platform.filemanager.core.listener;
020
021import java.io.Serializable;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.nuxeo.ecm.core.api.Blob;
026import org.nuxeo.ecm.core.api.DocumentModel;
027import org.nuxeo.ecm.core.api.PropertyException;
028import org.nuxeo.ecm.core.api.model.Property;
029import org.nuxeo.ecm.core.event.Event;
030import org.nuxeo.ecm.core.event.EventContext;
031import org.nuxeo.ecm.core.event.EventListener;
032import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
033import org.nuxeo.ecm.core.schema.FacetNames;
034import org.nuxeo.ecm.core.utils.BlobsExtractor;
035import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry;
036import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;
037import org.nuxeo.ecm.platform.types.Type;
038import org.nuxeo.ecm.platform.types.TypeManager;
039import org.nuxeo.runtime.api.Framework;
040
041/**
042 * Listener responsible for computing the mimetype of a new or edited blob and the common:icon field if necessary.
043 * <p>
044 * The logic of this event listener is divided into static public methods to make it easy to override this event
045 * listener with a custom implementation.
046 *
047 * @author ogrisel
048 */
049public class MimetypeIconUpdater implements EventListener {
050
051    protected Log log = LogFactory.getLog(MimetypeIconUpdater.class);
052
053    public static final String ICON_SCHEMA = "common";
054
055    public static final String ICON_FIELD = ICON_SCHEMA + ":" + "icon";
056
057    public static final String MAIN_BLOB_FIELD = "file:content";
058
059    public static final String MAIN_BLOB_SCHEMA = "file";
060
061    protected static final String OCTET_STREAM_MT = "application/octet-stream";
062
063    public final BlobsExtractor blobExtractor = new BlobsExtractor();
064
065    MimetypeRegistry mimetypeService;
066
067    public MimetypeRegistry getMimetypeRegistry() {
068        if (mimetypeService == null) {
069            mimetypeService = Framework.getService(MimetypeRegistry.class);
070        }
071
072        return mimetypeService;
073    }
074
075    public void handleEvent(Event event) {
076
077        EventContext ctx = event.getContext();
078        if (ctx instanceof DocumentEventContext) {
079
080            DocumentEventContext docCtx = (DocumentEventContext) ctx;
081            DocumentModel doc = docCtx.getSourceDocument();
082
083            // Don't update icon for immutable documents
084            if (doc.hasFacet(FacetNames.IMMUTABLE)) {
085                return;
086            }
087
088            try {
089                // ensure the document main icon is not null
090                setDefaultIcon(doc);
091
092                // update mimetypes of blobs in the document
093                for (Property prop : blobExtractor.getBlobsProperties(doc)) {
094                    if (prop.isDirty()) {
095                        updateBlobProperty(doc, getMimetypeRegistry(), prop);
096                    }
097                }
098
099                // update the document icon and size according to the main blob
100                if (doc.hasSchema(MAIN_BLOB_SCHEMA) && doc.getProperty(MAIN_BLOB_FIELD).isDirty()) {
101                    updateIconAndSizeFields(doc, getMimetypeRegistry(),
102                            doc.getProperty(MAIN_BLOB_FIELD).getValue(Blob.class));
103                }
104            } catch (PropertyException e) {
105                e.addInfo("Error in MimetypeIconUpdater listener");
106                throw e;
107            }
108        }
109    }
110
111    /**
112     * Update the mimetype of a blob along with the icon and size fields of the document if the blob is the main blob of
113     * the document.
114     */
115    public void updateBlobProperty(DocumentModel doc, MimetypeRegistry mimetypeService, Property dirtyProperty) {
116        String fieldPath = dirtyProperty.getXPath();
117        if (!fieldPath.contains(":")) {
118            // for schema without prefix: we need to add schema name as prefix
119            fieldPath = dirtyProperty.getSchema().getName() + ":" + fieldPath;
120        }
121
122        Blob blob = dirtyProperty.getValue(Blob.class);
123        if (blob != null && (blob.getMimeType() == null || blob.getMimeType().equals(OCTET_STREAM_MT))) {
124            // update the mimetype (if not set) using the the mimetype registry
125            // service
126            blob = mimetypeService.updateMimetype(blob);
127            doc.setPropertyValue(fieldPath, (Serializable) blob);
128        }
129    }
130
131    private void updateIconAndSizeFields(DocumentModel doc, MimetypeRegistry mimetypeService, Blob blob)
132            throws PropertyException {
133        // update the icon field of the document
134        if (blob != null && !doc.isFolder()) {
135            MimetypeEntry mimetypeEntry = mimetypeService.getMimetypeEntryByMimeType(blob.getMimeType());
136            updateIconField(mimetypeEntry, doc);
137        } else {
138            // reset to document type icon
139            updateIconField(null, doc);
140        }
141    }
142
143    /**
144     * If the icon field is empty, initialize it to the document type icon
145     */
146    public void setDefaultIcon(DocumentModel doc) {
147        if (doc.hasSchema(ICON_SCHEMA) && doc.getProperty(ICON_FIELD).getValue(String.class) == null) {
148            updateIconField(null, doc);
149        }
150    }
151
152    /**
153     * Compute the main icon of a Nuxeo document based on the mimetype of the main attached blob with of fallback on the
154     * document type generic icon.
155     */
156    public void updateIconField(MimetypeEntry mimetypeEntry, DocumentModel doc) {
157        String iconPath = null;
158        if (mimetypeEntry != null && mimetypeEntry.getIconPath() != null) {
159            iconPath = "/icons/" + mimetypeEntry.getIconPath();
160        } else {
161            TypeManager typeManager = Framework.getService(TypeManager.class);
162            if (typeManager == null) {
163                return;
164            }
165            Type uiType = typeManager.getType(doc.getType());
166            if (uiType != null) {
167                iconPath = uiType.getIcon();
168            }
169        }
170        if (iconPath != null && doc.hasSchema(ICON_SCHEMA)) {
171            doc.setPropertyValue(ICON_FIELD, iconPath);
172        }
173    }
174
175}