001/* 002 * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * Contributors: 014 * Nuxeo - initial API and implementation 015 */ 016package org.nuxeo.ecm.platform.rendition.service; 017 018import static org.nuxeo.ecm.platform.rendition.Constants.FILES_FILES_PROPERTY; 019import static org.nuxeo.ecm.platform.rendition.Constants.FILES_SCHEMA; 020import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_FACET; 021import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_NAME_PROPERTY; 022import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_ID_PROPERTY; 023import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_MODIFICATION_DATE_PROPERTY; 024import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY; 025 026import java.io.Serializable; 027import java.util.ArrayList; 028import java.util.Calendar; 029import java.util.Map; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.ecm.core.api.Blob; 034import org.nuxeo.ecm.core.api.CoreInstance; 035import org.nuxeo.ecm.core.api.CoreSession; 036import org.nuxeo.ecm.core.api.DocumentModel; 037import org.nuxeo.ecm.core.api.DocumentModelList; 038import org.nuxeo.ecm.core.api.DocumentRef; 039import org.nuxeo.ecm.core.api.IdRef; 040import org.nuxeo.ecm.core.api.LifeCycleConstants; 041import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 042import org.nuxeo.ecm.core.api.VersioningOption; 043import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 044import org.nuxeo.ecm.core.api.blobholder.DocumentStringBlobHolder; 045import org.nuxeo.ecm.core.api.security.ACE; 046import org.nuxeo.ecm.core.api.security.ACL; 047import org.nuxeo.ecm.core.api.security.ACP; 048import org.nuxeo.ecm.core.api.security.SecurityConstants; 049import org.nuxeo.ecm.core.api.security.impl.ACLImpl; 050import org.nuxeo.ecm.core.api.security.impl.ACPImpl; 051import org.nuxeo.ecm.core.versioning.VersioningService; 052import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry; 053import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 054import org.nuxeo.runtime.api.Framework; 055 056/** 057 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 058 */ 059public class RenditionCreator extends UnrestrictedSessionRunner { 060 061 private static final Log log = LogFactory.getLog(RenditionCreator.class); 062 063 public static final String FILE = "File"; 064 065 protected DocumentModel detachedRendition; 066 067 protected String liveDocumentId; 068 069 protected String versionDocumentId; 070 071 protected Blob renditionBlob; 072 073 protected String renditionName; 074 075 public RenditionCreator(CoreSession session, DocumentModel liveDocument, DocumentModel versionDocument, 076 Blob renditionBlob, String renditionName) { 077 super(session); 078 liveDocumentId = liveDocument.getId(); 079 versionDocumentId = versionDocument == null ? null : versionDocument.getId(); 080 this.renditionBlob = renditionBlob; 081 this.renditionName = renditionName; 082 } 083 084 public DocumentModel getDetachedRendition() { 085 return detachedRendition; 086 } 087 088 /** 089 * @deprecated since 7.10, misspelled, use {@link #getDetachedRendition} instead. 090 */ 091 @Deprecated 092 public DocumentModel getDetachedDendition() { 093 return detachedRendition; 094 } 095 096 @Override 097 public void run() { 098 DocumentModel liveDocument = session.getDocument(new IdRef(liveDocumentId)); 099 DocumentModel sourceDocument = liveDocument.isVersionable() ? session.getDocument(new IdRef(versionDocumentId)) 100 : liveDocument; 101 DocumentModel rendition = createRenditionDocument(sourceDocument); 102 removeBlobs(rendition); 103 updateMainBlob(rendition); 104 updateIconAndSizeFields(rendition); 105 106 // create a copy of the doc 107 if (rendition.getId() == null) { 108 rendition = session.createDocument(rendition); 109 } 110 if (sourceDocument.isVersionable()) { 111 // be sure to have the same version info 112 setCorrectVersion(rendition, sourceDocument); 113 } else { 114 // set ACL 115 giveReadRightToUser(rendition); 116 } 117 // do not apply default versioning to rendition 118 rendition.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.NONE); 119 rendition = session.saveDocument(rendition); 120 121 if (sourceDocument.isVersionable()) { 122 // rendition is checked out: check it in 123 DocumentRef renditionRef = rendition.checkIn(VersioningOption.NONE, null); 124 rendition = session.getDocument(renditionRef); 125 } 126 session.save(); 127 128 rendition.detach(true); 129 detachedRendition = rendition; 130 } 131 132 protected DocumentModel createRenditionDocument(DocumentModel sourceDocument) { 133 String doctype = sourceDocument.getType(); 134 String renditionMimeType = renditionBlob.getMimeType(); 135 BlobHolder blobHolder = sourceDocument.getAdapter(BlobHolder.class); 136 if (blobHolder == null || (blobHolder instanceof DocumentStringBlobHolder 137 && !(renditionMimeType.startsWith("text/") || renditionMimeType.startsWith("application/xhtml")))) { 138 // We have a document type unable to hold blobs, or 139 // We have a Note or other blob holder that can only hold strings, but the rendition is not a string-related 140 // MIME type. 141 // In either case, we'll have to create a File to hold it. 142 doctype = FILE; 143 } 144 145 boolean isVersionable = sourceDocument.isVersionable(); 146 String liveDocProp = isVersionable ? RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY : RENDITION_SOURCE_ID_PROPERTY; 147 DocumentModelList existingRenditions; 148 try (CoreSession userSession = CoreInstance.openCoreSession(session.getRepositoryName(), 149 getOriginatingUsername())) { 150 StringBuilder query = new StringBuilder(); 151 query.append("SELECT * FROM Document WHERE ecm:isProxy = 0 AND "); 152 query.append(RENDITION_NAME_PROPERTY); 153 query.append(" = '"); 154 query.append(renditionName); 155 query.append("' AND "); 156 query.append(liveDocProp); 157 query.append(" = '"); 158 query.append(liveDocumentId); 159 query.append("'"); 160 existingRenditions = userSession.query(query.toString()); 161 } 162 DocumentModel rendition; 163 String modificationDatePropertyName = getSourceDocumentModificationDatePropertyName(); 164 Calendar sourceLastModified = (Calendar) sourceDocument.getPropertyValue(modificationDatePropertyName); 165 if (existingRenditions.size() > 0) { 166 rendition = session.getDocument(existingRenditions.get(0).getRef()); 167 if (!isVersionable) { 168 Calendar renditionSourceLastModified = (Calendar) rendition.getPropertyValue( 169 RENDITION_SOURCE_MODIFICATION_DATE_PROPERTY); 170 if (renditionSourceLastModified != null && !renditionSourceLastModified.before(sourceLastModified)) { 171 this.renditionBlob = (Blob) rendition.getPropertyValue("file:content"); 172 return rendition; 173 } 174 } 175 if (rendition.isVersion()) { 176 String sid = rendition.getVersionSeriesId(); 177 rendition = session.getDocument(new IdRef(sid)); 178 } 179 } else { 180 rendition = session.createDocumentModel(null, sourceDocument.getName(), doctype); 181 } 182 183 rendition.copyContent(sourceDocument); 184 rendition.getContextData().putScopedValue(LifeCycleConstants.INITIAL_LIFECYCLE_STATE_OPTION_NAME, 185 sourceDocument.getCurrentLifeCycleState()); 186 187 rendition.addFacet(RENDITION_FACET); 188 rendition.setPropertyValue(RENDITION_SOURCE_ID_PROPERTY, sourceDocument.getId()); 189 if (isVersionable) { 190 rendition.setPropertyValue(RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY, liveDocumentId); 191 } 192 if (sourceLastModified != null) { 193 rendition.setPropertyValue(RENDITION_SOURCE_MODIFICATION_DATE_PROPERTY, sourceLastModified); 194 } 195 rendition.setPropertyValue(RENDITION_NAME_PROPERTY, renditionName); 196 197 return rendition; 198 } 199 200 protected void removeBlobs(DocumentModel rendition) { 201 if (rendition.hasSchema(FILES_SCHEMA)) { 202 rendition.setPropertyValue(FILES_FILES_PROPERTY, new ArrayList<Map<String, Serializable>>()); 203 } 204 } 205 206 protected void updateMainBlob(DocumentModel rendition) { 207 BlobHolder bh = rendition.getAdapter(BlobHolder.class); 208 bh.setBlob(renditionBlob); 209 } 210 211 private void updateIconAndSizeFields(DocumentModel rendition) { 212 if (!rendition.hasSchema("common")) { 213 return; 214 } 215 MimetypeRegistry mimetypeService; 216 try { 217 mimetypeService = Framework.getService(MimetypeRegistry.class); 218 } catch (Exception e) { 219 log.error("Cannot fetch Mimetype service when updating icon and file size rendition", e); 220 return; 221 } 222 MimetypeEntry mimetypeEntry = mimetypeService.getMimetypeEntryByMimeType(renditionBlob.getMimeType()); 223 if (mimetypeEntry != null && mimetypeEntry.getIconPath() != null) { 224 rendition.setPropertyValue("common:icon", "/icons/" + mimetypeEntry.getIconPath()); 225 } 226 rendition.setPropertyValue("common:size", renditionBlob.getLength()); 227 } 228 229 protected void giveReadRightToUser(DocumentModel rendition) { 230 ACP acp = new ACPImpl(); 231 ACL acl = new ACLImpl(); 232 acp.addACL(acl); 233 ACE ace = new ACE(getOriginatingUsername(), SecurityConstants.READ, true); 234 acl.add(ace); 235 rendition.setACP(acp, true); 236 } 237 238 protected void setCorrectVersion(DocumentModel rendition, DocumentModel versionDocument) { 239 Long minorVersion = (Long) versionDocument.getPropertyValue("uid:minor_version"); 240 rendition.setPropertyValue("uid:minor_version", minorVersion); 241 rendition.setPropertyValue("uid:major_version", versionDocument.getPropertyValue("uid:major_version")); 242 } 243 244 protected String getSourceDocumentModificationDatePropertyName() { 245 RenditionService rs = Framework.getService(RenditionService.class); 246 RenditionDefinition def = ((RenditionServiceImpl) rs).getRenditionDefinition(renditionName); 247 return def.getSourceDocumentModificationDatePropertyName(); 248 } 249 250}