001/* 002 * (C) Copyright 2012-2018 Nuxeo (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 * Antoine Taillefer <ataillefer@nuxeo.com> 018 */ 019package org.nuxeo.drive.service.impl; 020 021import java.util.Calendar; 022import java.util.Map; 023 024import org.apache.commons.lang3.StringUtils; 025import org.apache.logging.log4j.LogManager; 026import org.apache.logging.log4j.Logger; 027import org.nuxeo.drive.adapter.FileSystemItem; 028import org.nuxeo.drive.adapter.FolderItem; 029import org.nuxeo.drive.adapter.impl.DocumentBackedFileItem; 030import org.nuxeo.drive.adapter.impl.DocumentBackedFolderItem; 031import org.nuxeo.drive.service.FileSystemItemFactory; 032import org.nuxeo.drive.service.NuxeoDriveManager; 033import org.nuxeo.drive.service.VersioningFileSystemItemFactory; 034import org.nuxeo.ecm.collections.api.CollectionConstants; 035import org.nuxeo.ecm.core.api.Blob; 036import org.nuxeo.ecm.core.api.DocumentModel; 037import org.nuxeo.ecm.core.api.NuxeoException; 038import org.nuxeo.ecm.core.api.NuxeoPrincipal; 039import org.nuxeo.ecm.core.api.VersioningOption; 040import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 041import org.nuxeo.ecm.core.blob.BlobManager; 042import org.nuxeo.ecm.core.blob.BlobProvider; 043import org.nuxeo.runtime.api.Framework; 044 045/** 046 * Default implementation of a {@link FileSystemItemFactory}. It is {@link DocumentModel} backed and is the one used by 047 * Nuxeo Drive. 048 * 049 * @author Antoine Taillefer 050 */ 051public class DefaultFileSystemItemFactory extends AbstractFileSystemItemFactory 052 implements VersioningFileSystemItemFactory { 053 054 private static final Logger log = LogManager.getLogger(DefaultFileSystemItemFactory.class); 055 056 /** 057 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 058 */ 059 @Deprecated 060 protected static final String VERSIONING_DELAY_PARAM = "versioningDelay"; 061 062 /** 063 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 064 */ 065 @Deprecated 066 protected static final String VERSIONING_OPTION_PARAM = "versioningOption"; 067 068 /** 069 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 070 */ 071 @Deprecated 072 // Versioning delay in seconds, default value: 1 hour 073 protected double versioningDelay = 3600; 074 075 /** 076 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 077 */ 078 @Deprecated 079 // Versioning option, default value: MINOR 080 protected VersioningOption versioningOption = VersioningOption.MINOR; 081 082 /*--------------------------- AbstractFileSystemItemFactory -------------------------*/ 083 @Override 084 public void handleParameters(Map<String, String> parameters) { 085 String versioningDelayParam = parameters.get(VERSIONING_DELAY_PARAM); 086 if (!StringUtils.isEmpty(versioningDelayParam)) { 087 versioningDelay = Double.parseDouble(versioningDelayParam); 088 } 089 String versioningOptionParam = parameters.get(DefaultFileSystemItemFactory.VERSIONING_OPTION_PARAM); 090 if (!StringUtils.isEmpty(versioningOptionParam)) { 091 versioningOption = VersioningOption.valueOf(versioningOptionParam); 092 } 093 } 094 095 /** 096 * The default factory considers that a {@link DocumentModel} is adaptable as a {@link FileSystemItem} if: 097 * <ul> 098 * <li>It is not a version</li> 099 * <li>AND it is not HiddenInNavigation</li> 100 * <li>AND it is not in the trash, unless {@code includeDeleted} is true</li> 101 * <li>AND it is Folderish or it can be adapted as a {@link BlobHolder} with a blob</li> 102 * <li>AND its blob is not backed by an extended blob provider</li> 103 * <li>AND it is not a synchronization root registered for the current user, unless {@code relaxSyncRootConstraint} 104 * is true</li> 105 * </ul> 106 */ 107 @Override 108 public boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint) { 109 // Check version 110 if (doc.isVersion()) { 111 log.debug("Document {} is a version, it cannot be adapted as a FileSystemItem.", doc::getId); 112 return false; 113 } 114 // Check Collections 115 if (CollectionConstants.COLLECTIONS_TYPE.equals(doc.getType())) { 116 log.debug( 117 "Document {} is the collection root folder (type={}, path={}), it cannot be adapted as a FileSystemItem.", 118 doc::getId, () -> CollectionConstants.COLLECTIONS_TYPE, doc::getPathAsString); 119 return false; 120 } 121 // Check HiddenInNavigation 122 if (doc.hasFacet("HiddenInNavigation")) { 123 log.debug("Document {} is HiddenInNavigation, it cannot be adapted as a FileSystemItem.", doc::getId); 124 return false; 125 } 126 // Check if document is in the trash 127 if (!includeDeleted && doc.isTrashed()) { 128 log.debug("Document {} is trashed, it cannot be adapted as a FileSystemItem.", doc::getId); 129 return false; 130 } 131 // Try to fetch blob 132 Blob blob = null; 133 try { 134 blob = getBlob(doc); 135 } catch (NuxeoException e) { 136 log.error("Error while fetching blob for document {}, it cannot be adapted as a FileSystemItem.", 137 doc::getId, () -> e); 138 return false; 139 } 140 141 // Check Folderish or BlobHolder with a blob 142 if (!doc.isFolder()) { 143 if (blob == null) { 144 log.debug( 145 "Document {} is not Folderish nor a BlobHolder with a blob, it cannot be adapted as a FileSystemItem.", 146 doc::getId); 147 return false; 148 } 149 150 // Check for blobs backed by extended blob providers (ex: Google Drive) 151 BlobManager blobManager = Framework.getService(BlobManager.class); 152 BlobProvider blobProvider = blobManager.getBlobProvider(blob); 153 if (blobProvider != null && !blobProvider.supportsSync()) { 154 log.debug( 155 "Blob for Document {} is backed by a BlobProvider preventing sync, it cannot be adapted as a FileSystemItem.", 156 doc::getId); 157 return false; 158 } else if (blobProvider == null && !doc.getType().equals("Note")) { 159 log.debug( 160 "Document {} has no BlobProvider and is not a Note, it cannot be adapted as a FileSystemItem.", 161 doc::getId); 162 return false; 163 } 164 } 165 166 if (!relaxSyncRootConstraint && doc.isFolder()) { 167 // Check not a synchronization root registered for the current user 168 NuxeoDriveManager nuxeoDriveManager = Framework.getService(NuxeoDriveManager.class); 169 NuxeoPrincipal principal = doc.getPrincipal(); 170 boolean isSyncRoot = nuxeoDriveManager.isSynchronizationRoot(principal, doc); 171 if (isSyncRoot) { 172 log.debug( 173 "Document {} is a registered synchronization root for user {}, it cannot be adapted as a DefaultFileSystemItem.", 174 doc::getId, principal::getName); 175 return false; 176 } 177 } 178 return true; 179 } 180 181 @Override 182 protected FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentItem, FolderItem parentItem, 183 boolean relaxSyncRootConstraint, boolean getLockInfo) { 184 // Doc is either Folderish 185 if (doc.isFolder()) { 186 if (forceParentItem) { 187 return new DocumentBackedFolderItem(name, parentItem, doc, relaxSyncRootConstraint, getLockInfo); 188 } else { 189 return new DocumentBackedFolderItem(name, doc, relaxSyncRootConstraint, getLockInfo); 190 } 191 } 192 // or a BlobHolder with a blob 193 else { 194 if (forceParentItem) { 195 return new DocumentBackedFileItem(this, parentItem, doc, relaxSyncRootConstraint, getLockInfo); 196 } else { 197 return new DocumentBackedFileItem(this, doc, relaxSyncRootConstraint, getLockInfo); 198 } 199 } 200 } 201 202 /*--------------------------- FileSystemItemVersioning -------------------------*/ 203 /** 204 * Need to version the doc if the current contributor is different from the last contributor or if the last 205 * modification was done more than {@link #versioningDelay} seconds ago. 206 * 207 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 208 * drive level, this method is not used anymore 209 */ 210 @Override 211 @Deprecated 212 public boolean needsVersioning(DocumentModel doc) { 213 214 String lastContributor = (String) doc.getPropertyValue("dc:lastContributor"); 215 NuxeoPrincipal principal = doc.getPrincipal(); 216 boolean contributorChanged = !principal.getName().equals(lastContributor); 217 if (contributorChanged) { 218 log.debug( 219 "Contributor {} is different from the last contributor {} => will create a version of the document.", 220 principal, lastContributor); 221 return true; 222 } 223 Calendar lastModificationDate = (Calendar) doc.getPropertyValue("dc:modified"); 224 if (lastModificationDate == null) { 225 log.debug("Last modification date is null => will create a version of the document."); 226 return true; 227 } 228 long lastModified = System.currentTimeMillis() - lastModificationDate.getTimeInMillis(); 229 long versioningDelayMillis = (long) getVersioningDelay() * 1000; 230 if (lastModified > versioningDelayMillis) { 231 log.debug( 232 "Last modification was done {} milliseconds ago, this is more than the versioning delay {} milliseconds => will create a version of the document.", 233 lastModified, versioningDelayMillis); 234 return true; 235 } 236 log.debug( 237 "Contributor {} is the last contributor and last modification was done {} milliseconds ago, this is less than the versioning delay {} milliseconds => will not create a version of the document.", 238 principal, lastModified, versioningDelayMillis); 239 return false; 240 } 241 242 /** 243 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 244 */ 245 @Override 246 @Deprecated 247 public double getVersioningDelay() { 248 return versioningDelay; 249 } 250 251 /** 252 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 253 */ 254 @Override 255 @Deprecated 256 public void setVersioningDelay(double versioningDelay) { 257 this.versioningDelay = versioningDelay; 258 } 259 260 /** 261 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 262 */ 263 @Override 264 @Deprecated 265 public VersioningOption getVersioningOption() { 266 return versioningOption; 267 } 268 269 /** 270 * @deprecated since 9.1 automatic versioning is directly done by versioning system which holds the policies 271 */ 272 @Override 273 @Deprecated 274 public void setVersioningOption(VersioningOption versioningOption) { 275 this.versioningOption = versioningOption; 276 } 277 278 /*--------------------------- Protected ---------------------------------*/ 279 protected Blob getBlob(DocumentModel doc) { 280 BlobHolder bh = doc.getAdapter(BlobHolder.class); 281 if (bh == null) { 282 log.debug("Document {} is not a BlobHolder.", doc::getId); 283 return null; 284 } 285 Blob blob = bh.getBlob(); 286 if (blob == null) { 287 log.debug("Document {} is a BlobHolder without a blob.", doc::getId); 288 } 289 return blob; 290 } 291 292}