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