///<inheritdoc> public async Task UnlockAsync() { if (MsOfficeHelper.IsMsOfficeLocked(UserFileSystemPath)) // Required for PowerPoint. It does not block the for writing. { throw new ClientLockFailedException("The file is blocked for writing."); } ExternalDataManager customDataManager = Engine.CustomDataManager(UserFileSystemPath, Logger); LockManager lockManager = customDataManager.LockManager; // Set pending icon, so the user has a feedback as unlock operation may take some time. await customDataManager.SetLockPendingIconAsync(true); // Read lock-token from lock-info file. string lockToken = (await lockManager.GetLockInfoAsync()).LockToken; // Unlock the item in the remote storage. try { await Program.DavClient.UnlockAsync(new Uri(RemoteStoragePath), lockToken); } catch (ITHit.WebDAV.Client.Exceptions.ConflictException) { // The item is already unlocked. } // Delete lock-mode and lock-token info. lockManager.DeleteLock(); // Remove lock icon and lock info in custom columns. await customDataManager.SetLockInfoAsync(null); Logger.LogMessage("Unlocked in the remote storage succesefully", UserFileSystemPath); }
///<inheritdoc> public async Task LockAsync(LockMode lockMode) { Logger.LogMessage($"{nameof(ILock)}.{nameof(LockAsync)}()", UserFileSystemPath); ExternalDataManager customDataManager = Engine.CustomDataManager(UserFileSystemPath, Logger); LockManager lockManager = customDataManager.LockManager; if (!await lockManager.IsLockedAsync() && !Engine.CustomDataManager(UserFileSystemPath).IsNew) { // Set pending icon, so the user has a feedback as lock operation may take some time. await customDataManager.SetLockPendingIconAsync(true); // Call your remote storage here to lock the item. // Save the lock token and other lock info received from the remote storage on the client. // Supply the lock-token as part of each remote storage update in File.WriteAsync() method. // For demo purposes we just fill some generic data. ServerLockInfo lockInfo = new ServerLockInfo() { LockToken = "ServerToken", Owner = "You", Exclusive = true, LockExpirationDateUtc = DateTimeOffset.Now.AddMinutes(30) }; // Save lock-token and lock-mode. await lockManager.SetLockInfoAsync(lockInfo); await lockManager.SetLockModeAsync(lockMode); // Set lock icon and lock info in custom columns. await customDataManager.SetLockInfoAsync(lockInfo); Logger.LogMessage("Locked in remote storage succesefully.", UserFileSystemPath); } }
/// <inheritdoc/> public async Task <byte[]> CreateFolderAsync(IFolderMetadata folderMetadata) { string userFileSystemNewItemPath = Path.Combine(UserFileSystemPath, folderMetadata.Name); Logger.LogMessage($"{nameof(IFolder)}.{nameof(CreateFolderAsync)}()", userFileSystemNewItemPath); DirectoryInfo remoteStorageItem = new DirectoryInfo(Path.Combine(RemoteStoragePath, folderMetadata.Name)); remoteStorageItem.Create(); // Update remote storage folder metadata. remoteStorageItem.Attributes = folderMetadata.Attributes; remoteStorageItem.CreationTimeUtc = folderMetadata.CreationTime.UtcDateTime; remoteStorageItem.LastWriteTimeUtc = folderMetadata.LastWriteTime.UtcDateTime; remoteStorageItem.LastAccessTimeUtc = folderMetadata.LastAccessTime.UtcDateTime; remoteStorageItem.LastWriteTimeUtc = folderMetadata.LastWriteTime.UtcDateTime; // Get ETag from server here and save it on the client. string newEtag = "1234567890"; ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemNewItemPath); customDataManager.IsNew = false; await customDataManager.ETagManager.SetETagAsync(newEtag); await customDataManager.SetCustomColumnsAsync(new[] { new FileSystemItemPropertyData((int)CustomColumnIds.ETag, newEtag) }); return(null); }
/// <inheritdoc/> public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext) { // This method has a 60 sec timeout. // To process longer requests and reset the timout timer call one of the following: // - resultContext.ReturnChildren() method. // - resultContext.ReportProgress() method. Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath); IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(RemoteStoragePath).EnumerateFileSystemInfos(pattern); List <IFileSystemItemMetadata> userFileSystemChildren = new List <IFileSystemItemMetadata>(); foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren) { IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem); string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name); // Filtering existing files/folders. This is only required to avoid extra errors in the log. if (!FsPath.Exists(userFileSystemItemPath)) { Logger.LogMessage("Creating", userFileSystemItemPath); userFileSystemChildren.Add(itemInfo); } ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath); // Mark this item as not new, which is required for correct MS Office saving opertions. customDataManager.IsNew = false; // Save ETag on the client side, to be sent to the remote storage as part of the update. await customDataManager.ETagManager.SetETagAsync("1234567890"); } // To signal that the children enumeration is completed // always call ReturnChildren(), even if the folder is empty. resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count()); // Show some custom column in file manager for demo purposes. foreach (IFileSystemItemMetadata itemInfo in userFileSystemChildren) { string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name); FileSystemItemPropertyData eTagColumn = new FileSystemItemPropertyData((int)CustomColumnIds.ETag, "1234567890"); await Engine.CustomDataManager(userFileSystemItemPath).SetCustomColumnsAsync(new [] { eTagColumn }); } }
/// <inheritdoc/> public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null) { if (MsOfficeHelper.IsMsOfficeLocked(UserFileSystemPath)) // Required for PowerPoint. It does not block the for writing. { throw new ClientLockFailedException("The file is blocked for writing."); } Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", UserFileSystemPath); ExternalDataManager customDataManager = Engine.CustomDataManager(UserFileSystemPath); // Send the ETag to the server as part of the update to ensure the file in the remote storge is not modified since last read. string oldEtag = await customDataManager.ETagManager.GetETagAsync(); // Send the lock-token to the server as part of the update. string lockToken = (await customDataManager.LockManager.GetLockInfoAsync())?.LockToken; if (content != null) { long contentLength = content != null ? content.Length : 0; IWebRequestAsync request = await Program.DavClient.GetFileWriteRequestAsync( new Uri(RemoteStoragePath), null, contentLength, 0, -1, lockToken, oldEtag); // Update remote storage file content. using (Stream davContentStream = await request.GetRequestStreamAsync()) { if (content != null) { await content.CopyToAsync(davContentStream); } // Get the new ETag returned by the server (if any). IWebResponseAsync response = await request.GetResponseAsync(); string eTagNew = response.Headers["ETag"]; response.Close(); // Store ETag unlil the next update. // This will also mark the item as not new, which is required for correct MS Office saving opertions. await customDataManager.ETagManager.SetETagAsync(eTagNew); // Update ETag in custom column displayed in file manager. await customDataManager.SetCustomColumnsAsync(new[] { new FileSystemItemPropertyData((int)CustomColumnIds.ETag, eTagNew) }); } } }
/// <inheritdoc/> public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null) { if (MsOfficeHelper.IsMsOfficeLocked(UserFileSystemPath)) // Required for PowerPoint. It does not block the for writing. { throw new ClientLockFailedException("The file is blocked for writing."); } Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", UserFileSystemPath); ExternalDataManager customDataManager = Engine.CustomDataManager(UserFileSystemPath); // Send the ETag to the server as part of the update to ensure the file in the remote storge is not modified since last read. string oldEtag = await customDataManager.ETagManager.GetETagAsync(); // Send the lock-token to the server as part of the update. string lockToken = (await customDataManager.LockManager.GetLockInfoAsync())?.LockToken; FileInfo remoteStorageItem = new FileInfo(RemoteStoragePath); if (content != null) { // Upload remote storage file content. await using (FileStream remoteStorageStream = remoteStorageItem.Open(FileMode.Open, FileAccess.Write, FileShare.Delete)) { await content.CopyToAsync(remoteStorageStream); remoteStorageStream.SetLength(content.Length); } } // Update remote storage file metadata. remoteStorageItem.Attributes = fileMetadata.Attributes; remoteStorageItem.CreationTimeUtc = fileMetadata.CreationTime.UtcDateTime; remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime; remoteStorageItem.LastAccessTimeUtc = fileMetadata.LastAccessTime.UtcDateTime; remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime; // Get the new ETag from server here as part of the update and save it on the client. string newEtag = "1234567890"; await customDataManager.ETagManager.SetETagAsync(newEtag); await customDataManager.SetCustomColumnsAsync(new[] { new FileSystemItemPropertyData((int)CustomColumnIds.ETag, newEtag) }); }
/// <inheritdoc/> public async Task <byte[]> CreateFileAsync(IFileMetadata fileMetadata, Stream content = null) { string userFileSystemNewItemPath = Path.Combine(UserFileSystemPath, fileMetadata.Name); Logger.LogMessage($"{nameof(IFolder)}.{nameof(CreateFileAsync)}()", userFileSystemNewItemPath); FileInfo remoteStorageItem = new FileInfo(Path.Combine(RemoteStoragePath, fileMetadata.Name)); // Upload remote storage file content. await using (FileStream remoteStorageStream = remoteStorageItem.Open(FileMode.CreateNew, FileAccess.Write, FileShare.Delete)) { if (content != null) { await content.CopyToAsync(remoteStorageStream); remoteStorageStream.SetLength(content.Length); } } // Update remote storage file metadata. remoteStorageItem.Attributes = fileMetadata.Attributes; remoteStorageItem.CreationTimeUtc = fileMetadata.CreationTime.UtcDateTime; remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime; remoteStorageItem.LastAccessTimeUtc = fileMetadata.LastAccessTime.UtcDateTime; remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime; // Get ETag from server here and save it on the client. string newEtag = "1234567890"; ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemNewItemPath); // Mark this item as not new, which is required for correct MS Office saving opertions. customDataManager.IsNew = false; await customDataManager.ETagManager.SetETagAsync(newEtag); // Update ETag in custom column displayed in file manager. await customDataManager.SetCustomColumnsAsync(new[] { new FileSystemItemPropertyData((int)CustomColumnIds.ETag, newEtag) }); return(null); }
/// <inheritdoc/> public async Task <byte[]> CreateFileAsync(IFileMetadata fileMetadata, Stream content = null) { string userFileSystemNewItemPath = Path.Combine(UserFileSystemPath, fileMetadata.Name); Logger.LogMessage($"{nameof(IFolder)}.{nameof(CreateFileAsync)}()", userFileSystemNewItemPath); Uri newFileUri = new Uri(new Uri(RemoteStoragePath), fileMetadata.Name); long contentLength = content != null ? content.Length : 0; IWebRequestAsync request = await Program.DavClient.GetFileWriteRequestAsync(newFileUri, null, contentLength); // Update remote storage file content. using (Stream davContentStream = await request.GetRequestStreamAsync()) { if (content != null) { await content.CopyToAsync(davContentStream); } // Get the new ETag returned by the server (if any). IWebResponseAsync response = await request.GetResponseAsync(); string eTagNew = response.Headers["ETag"]; response.Close(); ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemNewItemPath); // Store ETag unlil the next update. // This will also mark the item as not new, which is required for correct MS Office saving opertions. await customDataManager.ETagManager.SetETagAsync(eTagNew); customDataManager.IsNew = false; // Mark file as not new just in case the server did not return the ETag. // Update ETag in custom column displayed in file manager. await customDataManager.SetCustomColumnsAsync(new[] { new FileSystemItemPropertyData((int)CustomColumnIds.ETag, eTagNew) }); } return(null); }
///<inheritdoc> public async Task LockAsync(LockMode lockMode) { Logger.LogMessage($"{nameof(ILock)}.{nameof(LockAsync)}()", UserFileSystemPath); ExternalDataManager customDataManager = Engine.CustomDataManager(UserFileSystemPath, Logger); LockManager lockManager = customDataManager.LockManager; if (!await lockManager.IsLockedAsync() && !Engine.CustomDataManager(UserFileSystemPath).IsNew) { // Set pending icon, so the user has a feedback as lock operation may take some time. await customDataManager.SetLockPendingIconAsync(true); // Call your remote storage here to lock the item. // Save the lock token and other lock info received from the remote storage on the client. // Supply the lock-token as part of each remote storage update in IFile.WriteAsync() method. LockInfo lockInfo = await Program.DavClient.LockAsync(new Uri(RemoteStoragePath), LockScope.Exclusive, false, null, TimeSpan.MaxValue); ServerLockInfo serverLockInfo = new ServerLockInfo { LockToken = lockInfo.LockToken.LockToken, Exclusive = lockInfo.LockScope == LockScope.Exclusive, Owner = lockInfo.Owner, LockExpirationDateUtc = DateTimeOffset.Now.Add(lockInfo.TimeOut) }; // Save lock-token and lock-mode. await lockManager.SetLockInfoAsync(serverLockInfo); await lockManager.SetLockModeAsync(lockMode); // Set lock icon and lock info in custom columns. await customDataManager.SetLockInfoAsync(serverLockInfo); Logger.LogMessage("Locked in remote storage succesefully.", UserFileSystemPath); } }
/// <inheritdoc/> public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext) { // This method has a 60 sec timeout. // To process longer requests and reset the timout timer call one of the following: // - resultContext.ReturnChildren() method. // - resultContext.ReportProgress() method. Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath); IHierarchyItemAsync[] remoteStorageChildren = null; // Retry the request in case the log-in dialog is shown. try { remoteStorageChildren = await Program.DavClient.GetChildrenAsync(new Uri(RemoteStoragePath), false); } catch (ITHit.WebDAV.Client.Exceptions.Redirect302Exception) { remoteStorageChildren = await Program.DavClient.GetChildrenAsync(new Uri(RemoteStoragePath), false); } List <FileSystemItemMetadataExt> userFileSystemChildren = new List <FileSystemItemMetadataExt>(); foreach (IHierarchyItemAsync remoteStorageItem in remoteStorageChildren) { FileSystemItemMetadataExt itemInfo = Mapping.GetUserFileSystemItemMetadata(remoteStorageItem); string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name); // Filtering existing files/folders. This is only required to avoid extra errors in the log. if (!FsPath.Exists(userFileSystemItemPath)) { Logger.LogMessage("Creating", userFileSystemItemPath); userFileSystemChildren.Add(itemInfo); } ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath); // Mark this item as not new, which is required for correct MS Office saving opertions. customDataManager.IsNew = false; } // To signal that the children enumeration is completed // always call ReturnChildren(), even if the folder is empty. resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count()); // Save ETags, the read-only attribute and all custom columns data. foreach (FileSystemItemMetadataExt child in userFileSystemChildren) { string userFileSystemItemPath = Path.Combine(UserFileSystemPath, child.Name); ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath); // Save ETag on the client side, to be sent to the remote storage as part of the update. // Setting ETag also marks an item as not new. // ETags must correspond with a server file/folder, NOT with a client placeholder. // It should NOT be moved/deleted/updated when a placeholder in the user file system is moved/deleted/updated. // It should be moved/deleted when a file/folder in the remote storage is moved/deleted. await customDataManager.ETagManager.SetETagAsync(child.ETag); // Set the read-only attribute and all custom columns data. await customDataManager.SetLockedByAnotherUserAsync(child.LockedByAnotherUser); await customDataManager.SetCustomColumnsAsync(child.CustomProperties); } }