001/* 002 * (C) Copyright 2002 - 2006 Nuxeo SARL <http://nuxeo.com> and others 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Nuxeo - initial API and implementation 011 * 012 * 013 * $Id: AbstractPlugin.java 4105 2006-10-15 12:29:25Z sfermigier $ 014 */ 015 016package org.nuxeo.ecm.platform.filemanager.service.extension; 017 018import static org.nuxeo.ecm.core.api.security.SecurityConstants.ADD_CHILDREN; 019import static org.nuxeo.ecm.core.api.security.SecurityConstants.READ_PROPERTIES; 020 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.regex.Pattern; 025 026import org.nuxeo.ecm.core.api.Blob; 027import org.nuxeo.ecm.core.api.CoreSession; 028import org.nuxeo.ecm.core.api.DocumentModel; 029import org.nuxeo.ecm.core.api.DocumentSecurityException; 030import org.nuxeo.ecm.core.api.NuxeoException; 031import org.nuxeo.ecm.core.api.PathRef; 032import org.nuxeo.ecm.core.api.VersioningOption; 033import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 034import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService; 035import org.nuxeo.ecm.core.versioning.VersioningService; 036import org.nuxeo.ecm.platform.filemanager.service.FileManagerService; 037import org.nuxeo.ecm.platform.filemanager.utils.FileManagerUtils; 038import org.nuxeo.ecm.platform.types.Type; 039import org.nuxeo.ecm.platform.types.TypeManager; 040import org.nuxeo.runtime.api.Framework; 041 042/** 043 * File importer abstract class. 044 * <p> 045 * Default file importer behavior. 046 * 047 * @see FileImporter 048 * @author <a href="mailto:akalogeropoulos@nuxeo.com">Andreas Kalogeropolos</a> 049 */ 050public abstract class AbstractFileImporter implements FileImporter { 051 052 private static final long serialVersionUID = 1L; 053 054 protected String name = ""; 055 056 protected String docType; 057 058 protected List<String> filters = new ArrayList<String>(); 059 060 protected List<Pattern> patterns; 061 062 protected boolean enabled = true; 063 064 protected Integer order = 0; 065 066 public static final String SKIP_UPDATE_AUDIT_LOGGING = "org.nuxeo.filemanager.skip.audit.logging.forupdates"; 067 068 // duplicated from Audit module to avoid circular dependency 069 public static final String DISABLE_AUDIT_LOGGER = "disableAuditLogger"; 070 071 // to be used by plugin implementation to gain access to standard file 072 // creation utility methods without having to lookup the service 073 protected FileManagerService fileManagerService; 074 075 public List<String> getFilters() { 076 return filters; 077 } 078 079 public void setFilters(List<String> filters) { 080 this.filters = filters; 081 patterns = new ArrayList<Pattern>(); 082 for (String filter : filters) { 083 patterns.add(Pattern.compile(filter)); 084 } 085 } 086 087 public boolean matches(String mimeType) { 088 for (Pattern pattern : patterns) { 089 if (pattern.matcher(mimeType).matches()) { 090 return true; 091 } 092 } 093 return false; 094 } 095 096 public String getName() { 097 return name; 098 } 099 100 public void setName(String name) { 101 this.name = name; 102 } 103 104 public String getDocType() { 105 return docType; 106 } 107 108 public void setDocType(String docType) { 109 this.docType = docType; 110 } 111 112 /** 113 * Gets the doc type to use in the given container. 114 */ 115 public String getDocType(DocumentModel container) { 116 return getDocType(); // use XML configuration 117 } 118 119 /** 120 * Default document type to use when the plugin XML configuration does not specify one. 121 * <p> 122 * To implement when the default {@link #create} method is used. 123 */ 124 public String getDefaultDocType() { 125 throw new UnsupportedOperationException(); 126 } 127 128 /** 129 * Whether document overwrite is detected by checking title or filename. 130 * <p> 131 * To implement when the default {@link #create} method is used. 132 */ 133 public boolean isOverwriteByTitle() { 134 throw new UnsupportedOperationException(); 135 } 136 137 /** 138 * Creates the document (sets its properties). {@link #updateDocument} will be called after this. 139 * <p> 140 * Default implementation sets the title. 141 */ 142 public void createDocument(DocumentModel doc, Blob content, String title) { 143 doc.setPropertyValue("dc:title", title); 144 } 145 146 /** 147 * Tries to update the document <code>doc</code> with the blob <code>content</code>. 148 * <p> 149 * Returns <code>true</code> if the document is really updated. 150 * 151 * @since 7.1 152 */ 153 public boolean updateDocumentIfPossible(DocumentModel doc, Blob content) { 154 updateDocument(doc, content); 155 return true; 156 } 157 158 /** 159 * Updates the document (sets its properties). 160 * <p> 161 * Default implementation sets the content. 162 */ 163 public void updateDocument(DocumentModel doc, Blob content) { 164 doc.getAdapter(BlobHolder.class).setBlob(content); 165 } 166 167 public Blob getBlob(DocumentModel doc) { 168 return doc.getAdapter(BlobHolder.class).getBlob(); 169 } 170 171 @Override 172 public DocumentModel create(CoreSession session, Blob content, String path, boolean overwrite, String fullname, 173 TypeManager typeService) throws IOException { 174 path = getNearestContainerPath(session, path); 175 DocumentModel container = session.getDocument(new PathRef(path)); 176 String docType = getDocType(container); // from override or descriptor 177 if (docType == null) { 178 docType = getDefaultDocType(); 179 } 180 doSecurityCheck(session, path, docType, typeService); 181 String filename = FileManagerUtils.fetchFileName(fullname); 182 String title = FileManagerUtils.fetchTitle(filename); 183 content.setFilename(filename); 184 // look for an existing document with same title or filename 185 DocumentModel doc; 186 if (isOverwriteByTitle()) { 187 doc = FileManagerUtils.getExistingDocByTitle(session, path, title); 188 } else { 189 doc = FileManagerUtils.getExistingDocByFileName(session, path, filename); 190 } 191 boolean skipCheckInAfterAdd = false; 192 if (overwrite && doc != null) { 193 // make sure we save any existing data 194 if (!skipCheckInForBlob(getBlob(doc))) { 195 checkIn(doc); 196 } 197 // update data 198 boolean isDocumentUpdated = updateDocumentIfPossible(doc, content); 199 if (!isDocumentUpdated) { 200 return null; 201 } 202 if (Framework.isBooleanPropertyTrue(SKIP_UPDATE_AUDIT_LOGGING)) { 203 // skip the update event if configured to do so 204 doc.putContextData(DISABLE_AUDIT_LOGGER, true); 205 } 206 // save 207 doc = doc.getCoreSession().saveDocument(doc); 208 } else { 209 // create document model 210 doc = session.createDocumentModel(docType); 211 createDocument(doc, content, title); 212 // set path 213 PathSegmentService pss = Framework.getLocalService(PathSegmentService.class); 214 doc.setPathInfo(path, pss.generatePathSegment(doc)); 215 // update data 216 updateDocument(doc, content); 217 skipCheckInAfterAdd = skipCheckInForBlob(content); 218 // create 219 doc = session.createDocument(doc); 220 } 221 // check in if requested 222 if (!skipCheckInAfterAdd) { 223 checkInAfterAdd(doc); 224 } 225 session.save(); 226 return doc; 227 } 228 229 /** 230 * Avoid checkin for a 0-length blob. Microsoft-WebDAV-MiniRedir first creates a 0-length file and then locks it 231 * before putting the real file. But we don't want this first placeholder to cause a versioning event. 232 */ 233 protected boolean skipCheckInForBlob(Blob blob) { 234 return blob == null || blob.getLength() == 0; 235 } 236 237 public FileManagerService getFileManagerService() { 238 return fileManagerService; 239 } 240 241 public void setFileManagerService(FileManagerService fileManagerService) { 242 this.fileManagerService = fileManagerService; 243 } 244 245 public void setEnabled(boolean enabled) { 246 this.enabled = enabled; 247 } 248 249 public boolean isEnabled() { 250 return enabled; 251 } 252 253 public Integer getOrder() { 254 return order; 255 } 256 257 public void setOrder(Integer order) { 258 this.order = order; 259 } 260 261 public int compareTo(FileImporter other) { 262 Integer otherOrder = other.getOrder(); 263 if (order == null && otherOrder == null) { 264 return 0; 265 } else if (order == null) { 266 return 1; 267 } else if (otherOrder == null) { 268 return -1; 269 } 270 return order.compareTo(otherOrder); 271 } 272 273 /** 274 * Returns nearest container path 275 * <p> 276 * If given path points to a folderish document, return it. Else, return parent path. 277 */ 278 protected String getNearestContainerPath(CoreSession documentManager, String path) { 279 DocumentModel currentDocument = documentManager.getDocument(new PathRef(path)); 280 if (!currentDocument.isFolder()) { 281 path = path.substring(0, path.lastIndexOf('/')); 282 } 283 return path; 284 } 285 286 protected void checkIn(DocumentModel doc) { 287 VersioningOption option = fileManagerService.getVersioningOption(); 288 if (option != null && option != VersioningOption.NONE) { 289 if (doc.isCheckedOut()) { 290 doc.checkIn(option, null); 291 } 292 } 293 } 294 295 protected void checkInAfterAdd(DocumentModel doc) { 296 if (fileManagerService.doVersioningAfterAdd()) { 297 checkIn(doc); 298 } 299 } 300 301 /** 302 * @deprecated use {@link #checkIn} instead, noting that it does not save the document 303 */ 304 @Deprecated 305 protected DocumentModel overwriteAndIncrementversion(CoreSession documentManager, DocumentModel doc) 306 { 307 doc.putContextData(VersioningService.VERSIONING_OPTION, fileManagerService.getVersioningOption()); 308 return documentManager.saveDocument(doc); 309 } 310 311 protected void doSecurityCheck(CoreSession documentManager, String path, String typeName, TypeManager typeService) 312 throws DocumentSecurityException { 313 // perform the security checks 314 PathRef containerRef = new PathRef(path); 315 if (!documentManager.hasPermission(containerRef, READ_PROPERTIES) 316 || !documentManager.hasPermission(containerRef, ADD_CHILDREN)) { 317 throw new DocumentSecurityException("Not enough rights to create folder"); 318 } 319 DocumentModel container = documentManager.getDocument(containerRef); 320 321 Type containerType = typeService.getType(container.getType()); 322 if (containerType == null) { 323 return; 324 } 325 326 if (!typeService.isAllowedSubType(typeName, container.getType(), container)) { 327 throw new NuxeoException(String.format("Cannot create document of type %s in container with type %s", 328 typeName, containerType.getId())); 329 } 330 } 331 332}