001/* 002 * (C) Copyright 2008-2019 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 static org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry.DEFAULT_MIMETYPE; 022 023import java.io.Serializable; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.nuxeo.ecm.core.api.Blob; 028import org.nuxeo.ecm.core.api.DocumentModel; 029import org.nuxeo.ecm.core.api.PropertyException; 030import org.nuxeo.ecm.core.api.model.Property; 031import org.nuxeo.ecm.core.event.Event; 032import org.nuxeo.ecm.core.event.EventContext; 033import org.nuxeo.ecm.core.event.EventListener; 034import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 035import org.nuxeo.ecm.core.schema.FacetNames; 036import org.nuxeo.ecm.core.utils.BlobsExtractor; 037import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry; 038import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 039import org.nuxeo.ecm.platform.types.Type; 040import org.nuxeo.ecm.platform.types.TypeManager; 041import org.nuxeo.runtime.api.Framework; 042 043/** 044 * Listener responsible for computing the mimetype of a new or edited blob and the {@code common:icon} field if 045 * necessary. 046 * <p> 047 * The logic of this event listener is divided into static public methods to make it easy to override this event 048 * listener with a custom implementation. 049 * 050 * @author ogrisel 051 */ 052public class MimetypeIconUpdater implements EventListener { 053 054 protected Log log = LogFactory.getLog(MimetypeIconUpdater.class); 055 056 public static final String ICON_SCHEMA = "common"; 057 058 public static final String ICON_FIELD = ICON_SCHEMA + ":" + "icon"; 059 060 public static final String MAIN_BLOB_FIELD = "file:content"; 061 062 public static final String MAIN_BLOB_SCHEMA = "file"; 063 064 /** 065 * @deprecated since 11.1. Use {@link MimetypeRegistry#DEFAULT_MIMETYPE} instead. 066 */ 067 @Deprecated(since = "11.1", forRemoval = true) 068 protected static final String OCTET_STREAM_MT = DEFAULT_MIMETYPE; 069 070 /** 071 * @deprecated since 11.1. Create a new instance of {@link BlobsExtractor} when needed. 072 */ 073 @Deprecated(since = "11.1", forRemoval = true) 074 public final BlobsExtractor blobExtractor = new BlobsExtractor(); 075 076 /** 077 * @deprecated since 11.1. Use {@link Framework#getService(Class)} with {@link MimetypeRegistry} instead. 078 */ 079 @Deprecated(since = "11.1", forRemoval = true) 080 MimetypeRegistry mimetypeService; 081 082 /** 083 * @deprecated since 11.1. Use {@link Framework#getService(Class)} with {@link MimetypeRegistry} instead. 084 */ 085 @Deprecated(since = "11.1", forRemoval = true) 086 public MimetypeRegistry getMimetypeRegistry() { 087 if (mimetypeService == null) { 088 mimetypeService = Framework.getService(MimetypeRegistry.class); 089 } 090 091 return mimetypeService; 092 } 093 094 @Override 095 public void handleEvent(Event event) { 096 097 EventContext ctx = event.getContext(); 098 if (ctx instanceof DocumentEventContext) { 099 100 DocumentEventContext docCtx = (DocumentEventContext) ctx; 101 DocumentModel doc = docCtx.getSourceDocument(); 102 103 // Don't update icon for immutable documents 104 if (doc.hasFacet(FacetNames.IMMUTABLE)) { 105 return; 106 } 107 108 try { 109 // ensure the document main icon is not null 110 setDefaultIcon(doc); 111 112 // update mimetypes of blobs in the document 113 114 MimetypeRegistry mimetypeRegistry = Framework.getService(MimetypeRegistry.class); 115 BlobsExtractor extractor = new BlobsExtractor(); 116 for (Property prop : extractor.getBlobsProperties(doc)) { 117 if (prop.isDirty()) { 118 updateBlobProperty(doc, mimetypeRegistry, prop); 119 } 120 } 121 122 // update the document icon and size according to the main blob 123 if (doc.hasSchema(MAIN_BLOB_SCHEMA) && doc.getProperty(MAIN_BLOB_FIELD).isDirty()) { 124 updateIconAndSizeFields(doc, mimetypeRegistry, 125 doc.getProperty(MAIN_BLOB_FIELD).getValue(Blob.class)); 126 } 127 } catch (PropertyException e) { 128 e.addInfo("Error in MimetypeIconUpdater listener"); 129 throw e; 130 } 131 } 132 } 133 134 /** 135 * Updates the mimetype of a blob along with the icon and size fields of the document if the blob is the main blob 136 * of the document. 137 */ 138 public void updateBlobProperty(DocumentModel doc, MimetypeRegistry mimetypeService, Property dirtyProperty) { 139 String fieldPath = dirtyProperty.getXPath(); 140 if (!fieldPath.contains(":")) { 141 // for schema without prefix: we need to add schema name as prefix 142 fieldPath = dirtyProperty.getSchema().getName() + ":" + fieldPath; 143 } 144 145 Blob blob = dirtyProperty.getValue(Blob.class); 146 if (blob == null) { 147 return; 148 } 149 if (blob.getMimeType() == null || blob.getMimeType().startsWith(DEFAULT_MIMETYPE)) { 150 // update the mime type (if not set) using the mimetype registry service 151 blob = mimetypeService.updateMimetype(blob); 152 doc.setPropertyValue(fieldPath, (Serializable) blob); 153 } else if (!mimetypeService.isMimeTypeNormalized(blob.getMimeType())) { 154 // normalize the mime type if not yet normalized 155 mimetypeService.getNormalizedMimeType(blob.getMimeType()).ifPresent(blob::setMimeType); 156 } 157 } 158 159 private void updateIconAndSizeFields(DocumentModel doc, MimetypeRegistry mimetypeService, Blob blob) { 160 // update the icon field of the document 161 if (blob != null && !doc.isFolder()) { 162 MimetypeEntry mimetypeEntry = mimetypeService.getMimetypeEntryByMimeType(blob.getMimeType()); 163 updateIconField(mimetypeEntry, doc); 164 } else { 165 // reset to document type icon 166 updateIconField(null, doc); 167 } 168 } 169 170 /** 171 * If the icon field is empty, initialize it to the document type icon 172 */ 173 public void setDefaultIcon(DocumentModel doc) { 174 if (doc.hasSchema(ICON_SCHEMA) && doc.getProperty(ICON_FIELD).getValue(String.class) == null) { 175 updateIconField(null, doc); 176 } 177 } 178 179 /** 180 * Computes the main icon of a Nuxeo document based on the mime type of the main attached blob with of fallback on 181 * the document type generic icon. 182 */ 183 public void updateIconField(MimetypeEntry mimetypeEntry, DocumentModel doc) { 184 String iconPath = null; 185 if (mimetypeEntry != null && mimetypeEntry.getIconPath() != null) { 186 iconPath = "/icons/" + mimetypeEntry.getIconPath(); 187 } else { 188 TypeManager typeManager = Framework.getService(TypeManager.class); 189 if (typeManager == null) { 190 return; 191 } 192 Type uiType = typeManager.getType(doc.getType()); 193 if (uiType != null) { 194 iconPath = uiType.getIcon(); 195 } 196 } 197 if (iconPath != null && doc.hasSchema(ICON_SCHEMA)) { 198 doc.setPropertyValue(ICON_FIELD, iconPath); 199 } 200 } 201 202}