001/* 002 * (C) Copyright 2010-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.rendition.service; 020 021import static org.nuxeo.ecm.platform.rendition.Constants.FILES_FILES_PROPERTY; 022import static org.nuxeo.ecm.platform.rendition.Constants.FILES_SCHEMA; 023import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_FACET; 024import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_NAME_PROPERTY; 025import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_ID_PROPERTY; 026import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_MODIFICATION_DATE_PROPERTY; 027import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY; 028import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_VARIANT_PROPERTY; 029 030import java.io.Serializable; 031import java.util.ArrayList; 032import java.util.Calendar; 033import java.util.Map; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.nuxeo.ecm.core.api.Blob; 038import org.nuxeo.ecm.core.api.DocumentModel; 039import org.nuxeo.ecm.core.api.DocumentModelList; 040import org.nuxeo.ecm.core.api.DocumentRef; 041import org.nuxeo.ecm.core.api.IdRef; 042import org.nuxeo.ecm.core.api.LifeCycleConstants; 043import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 044import org.nuxeo.ecm.core.api.VersioningOption; 045import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 046import org.nuxeo.ecm.core.api.blobholder.DocumentStringBlobHolder; 047import org.nuxeo.ecm.core.query.sql.NXQL; 048import org.nuxeo.ecm.core.versioning.VersioningService; 049import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry; 050import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 051import org.nuxeo.runtime.api.Framework; 052 053/** 054 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 055 */ 056public class RenditionCreator extends UnrestrictedSessionRunner { 057 058 private static final Log log = LogFactory.getLog(RenditionCreator.class); 059 060 public static final String FILE = "File"; 061 062 protected DocumentModel detachedRendition; 063 064 protected String liveDocumentId; 065 066 protected String versionDocumentId; 067 068 protected Blob renditionBlob; 069 070 protected String renditionName; 071 072 /** 073 * @since 8.1 074 */ 075 protected RenditionDefinition renditionDefinition; 076 077 /** 078 * @since 8.1 079 */ 080 protected final String renditionVariant; 081 082 /** 083 * @since 8.1 084 */ 085 public RenditionCreator(DocumentModel liveDocument, DocumentModel versionDocument, Blob renditionBlob, 086 RenditionDefinition renditionDefinition) { 087 super(liveDocument.getCoreSession()); 088 liveDocumentId = liveDocument.getId(); 089 versionDocumentId = versionDocument == null ? null : versionDocument.getId(); 090 this.renditionBlob = renditionBlob; 091 this.renditionDefinition = renditionDefinition; 092 renditionName = renditionDefinition.getName(); 093 renditionVariant = renditionDefinition.getProvider().getVariant(liveDocument, renditionDefinition); 094 } 095 096 public DocumentModel getDetachedRendition() { 097 return detachedRendition; 098 } 099 100 /** 101 * @deprecated since 7.10, misspelled, use {@link #getDetachedRendition} instead. 102 */ 103 @Deprecated 104 public DocumentModel getDetachedDendition() { 105 return detachedRendition; 106 } 107 108 @Override 109 public void run() { 110 DocumentModel liveDocument = session.getDocument(new IdRef(liveDocumentId)); 111 DocumentModel sourceDocument = liveDocument.isVersionable() ? session.getDocument(new IdRef(versionDocumentId)) 112 : liveDocument; 113 DocumentModel rendition = createRenditionDocument(sourceDocument); 114 removeBlobs(rendition); 115 updateMainBlob(rendition); 116 updateIconField(rendition); 117 118 // create a copy of the doc 119 if (rendition.getId() == null) { 120 rendition = session.createDocument(rendition); 121 } 122 if (sourceDocument.isVersionable()) { 123 // be sure to have the same version info 124 setCorrectVersion(rendition, sourceDocument); 125 } 126 // do not apply default versioning to rendition 127 rendition.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.NONE); 128 rendition = session.saveDocument(rendition); 129 130 if (sourceDocument.isVersionable()) { 131 // rendition is checked out: check it in 132 DocumentRef renditionRef = rendition.checkIn(VersioningOption.NONE, null); 133 rendition = session.getDocument(renditionRef); 134 } 135 session.save(); 136 137 rendition.detach(true); 138 detachedRendition = rendition; 139 } 140 141 protected DocumentModel createRenditionDocument(DocumentModel sourceDocument) { 142 String doctype = sourceDocument.getType(); 143 String renditionMimeType = renditionBlob.getMimeType(); 144 BlobHolder blobHolder = sourceDocument.getAdapter(BlobHolder.class); 145 if (blobHolder == null || (blobHolder instanceof DocumentStringBlobHolder 146 && !(renditionMimeType.startsWith("text/") || renditionMimeType.startsWith("application/xhtml")))) { 147 // We have a document type unable to hold blobs, or 148 // We have a Note or other blob holder that can only hold strings, but the rendition is not a string-related 149 // MIME type. 150 // In either case, we'll have to create a File to hold it. 151 doctype = FILE; 152 } 153 154 boolean isVersionable = sourceDocument.isVersionable(); 155 String liveDocProp = isVersionable ? RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY : RENDITION_SOURCE_ID_PROPERTY; 156 StringBuilder query = new StringBuilder(); 157 query.append("SELECT * FROM Document WHERE ecm:isProxy = 0 AND "); 158 query.append(RENDITION_NAME_PROPERTY); 159 query.append(" = '"); 160 query.append(NXQL.escapeStringInner(renditionName)); 161 query.append("' AND "); 162 if (renditionVariant != null) { 163 query.append(RENDITION_VARIANT_PROPERTY); 164 query.append(" = '"); 165 query.append(NXQL.escapeStringInner(renditionVariant)); 166 query.append("' AND "); 167 } 168 query.append(liveDocProp); 169 query.append(" = '"); 170 query.append(liveDocumentId); 171 query.append("'"); 172 DocumentModelList existingRenditions = session.query(query.toString()); 173 String modificationDatePropertyName = getSourceDocumentModificationDatePropertyName(); 174 Calendar sourceLastModified = (Calendar) sourceDocument.getPropertyValue(modificationDatePropertyName); 175 DocumentModel rendition; 176 if (existingRenditions.size() > 0) { 177 rendition = session.getDocument(existingRenditions.get(0).getRef()); 178 if (!isVersionable) { 179 Calendar renditionSourceLastModified = (Calendar) rendition.getPropertyValue( 180 RENDITION_SOURCE_MODIFICATION_DATE_PROPERTY); 181 if (renditionSourceLastModified != null && !renditionSourceLastModified.before(sourceLastModified)) { 182 this.renditionBlob = (Blob) rendition.getPropertyValue("file:content"); 183 return rendition; 184 } 185 } 186 if (rendition.isVersion()) { 187 String sid = rendition.getVersionSeriesId(); 188 rendition = session.getDocument(new IdRef(sid)); 189 } 190 } else { 191 rendition = session.createDocumentModel(null, sourceDocument.getName(), doctype); 192 } 193 194 rendition.copyContent(sourceDocument); 195 rendition.putContextData(LifeCycleConstants.INITIAL_LIFECYCLE_STATE_OPTION_NAME, 196 sourceDocument.getCurrentLifeCycleState()); 197 198 rendition.addFacet(RENDITION_FACET); 199 rendition.setPropertyValue(RENDITION_SOURCE_ID_PROPERTY, sourceDocument.getId()); 200 if (isVersionable) { 201 rendition.setPropertyValue(RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY, liveDocumentId); 202 } 203 if (sourceLastModified != null) { 204 rendition.setPropertyValue(RENDITION_SOURCE_MODIFICATION_DATE_PROPERTY, sourceLastModified); 205 } 206 if (renditionVariant != null) { 207 rendition.setPropertyValue(RENDITION_VARIANT_PROPERTY, renditionVariant); 208 } 209 rendition.setPropertyValue(RENDITION_NAME_PROPERTY, renditionName); 210 211 return rendition; 212 } 213 214 protected void removeBlobs(DocumentModel rendition) { 215 if (rendition.hasSchema(FILES_SCHEMA)) { 216 rendition.setPropertyValue(FILES_FILES_PROPERTY, new ArrayList<Map<String, Serializable>>()); 217 } 218 } 219 220 protected void updateMainBlob(DocumentModel rendition) { 221 BlobHolder bh = rendition.getAdapter(BlobHolder.class); 222 bh.setBlob(renditionBlob); 223 } 224 225 private void updateIconField(DocumentModel rendition) { 226 if (!rendition.hasSchema("common")) { 227 return; 228 } 229 MimetypeRegistry mimetypeService; 230 try { 231 mimetypeService = Framework.getService(MimetypeRegistry.class); 232 } catch (Exception e) { 233 log.error("Cannot fetch Mimetype service when updating icon and file size rendition", e); 234 return; 235 } 236 MimetypeEntry mimetypeEntry = mimetypeService.getMimetypeEntryByMimeType(renditionBlob.getMimeType()); 237 if (mimetypeEntry != null && mimetypeEntry.getIconPath() != null) { 238 rendition.setPropertyValue("common:icon", "/icons/" + mimetypeEntry.getIconPath()); 239 } 240 } 241 242 protected void setCorrectVersion(DocumentModel rendition, DocumentModel versionDocument) { 243 Long minorVersion = (Long) versionDocument.getPropertyValue("uid:minor_version"); 244 rendition.setPropertyValue("uid:minor_version", minorVersion); 245 rendition.setPropertyValue("uid:major_version", versionDocument.getPropertyValue("uid:major_version")); 246 } 247 248 protected String getSourceDocumentModificationDatePropertyName() { 249 return renditionDefinition.getSourceDocumentModificationDatePropertyName(); 250 } 251 252}