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