/// <summary> /// Called when a file or folder is created in the user file system. /// </summary> /// <remarks> /// This method is also called when a file is being moved in user file system, after the IFileSystem.MoveToAsync() call. /// </remarks> private async void CreatedAsync(object sender, FileSystemEventArgs e) { LogMessage($"{e.ChangeType}", e.FullPath); string userFileSystemPath = e.FullPath; try { // When a file/folder is moved this method is also called. The file/folder move is already processed in IFileSystem.MoveToAsync(). if (FsPath.Exists(userFileSystemPath) && !PlaceholderItem.IsPlaceholder(userFileSystemPath)) { // When a new file or folder is created under sync root it is // created as a regular file or folder. Converting to placeholder. PlaceholderItem.ConvertToPlaceholder(userFileSystemPath, false); LogMessage("Converted to placeholder", userFileSystemPath); // Do not create temp MS Office, temp and hidden files in remote storage. if (!FsPath.AvoidSync(userFileSystemPath)) { // Create the file/folder in the remote storage. try { await RemoteStorageRawItem.CreateAsync(userFileSystemPath, this); } catch (IOException ex) { LogError("Creation in remote storage failed. Possibly in use by an application", userFileSystemPath, null, ex); } } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", userFileSystemPath, null, ex); } }
/// <summary> /// Deletes a file or folder placeholder in user file system. /// </summary> /// <remarks>This method failes if the file or folder in user file system is modified (not in sync with the remote storage).</remarks> public async Task DeleteAsync() { // Windows does not provide a function to delete a placeholder file only if it is not modified. // Here we check that the file is not modified in user file system, using GetInSync() call. // To avoid the file modification between GetInSync() call and Delete() call in this method we // open it without FileShare.Write flag. using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.Open(userFileSystemPath, (FileAccess)0, FileMode.Open, FileShare.Read | FileShare.Delete)) { if (PlaceholderItem.GetInSync(userFileSystemWinItem.SafeHandle)) { if (FsPath.IsFile(userFileSystemPath)) { File.Delete(userFileSystemPath); } else { Directory.Delete(userFileSystemPath, true); } } else { throw new IOException("File not in-sync."); } } }
public static string GetOriginalPath(this PlaceholderItem placeholder) { byte[] customDataRaw = placeholder.GetCustomData(); CustomData customData = (customDataRaw.Length > 0) ? CustomData.Deserialize(customDataRaw) : new CustomData(); return(customData.OriginalPath); }
private async Task CreateOrUpdateFolderAsync(string userFileSystemPath, bool create) { try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. DirectoryInfo userFileSystemFolder = new DirectoryInfo(userFileSystemPath); DirectoryInfo remoteStorageFolder = new DirectoryInfo(remoteStoragePath); remoteStorageFolder.Create(); // Update ETag/LastWriteTime in user file system, so the synchronyzation or remote storage monitor would not start the new update. byte[] customData = BitConverter.GetBytes(userFileSystemFolder.LastWriteTime.ToBinary()); PlaceholderItem.GetItem(userFileSystemPath).SetCustomData(customData); // Remove Pinned, Unpinned and Offline flags. remoteStorageFolder.Attributes = userFileSystemFolder.Attributes & (FileAttributes) ~FileAttributesExt.Pinned & (FileAttributes) ~FileAttributesExt.Unpinned & ~FileAttributes.Offline; remoteStorageFolder.CreationTimeUtc = userFileSystemFolder.CreationTimeUtc; remoteStorageFolder.LastWriteTimeUtc = userFileSystemFolder.LastWriteTimeUtc; remoteStorageFolder.LastAccessTimeUtc = userFileSystemFolder.LastAccessTimeUtc; remoteStorageFolder.LastWriteTimeUtc = userFileSystemFolder.LastWriteTimeUtc; PlaceholderItem.GetItem(userFileSystemPath).SetInSync(true); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
///<inheritdoc> public async Task MoveToAsync(string userFileSystemNewPath, IOperationContext operationContext, IConfirmationResultContext resultContext) { // Here we will simply move the file in remote storage and confirm the operation. // In your implementation you may implement a more complex scenario with offline operations support. LogMessage("IFileSystemItem.MoveToAsync()", this.FullPath, userFileSystemNewPath); string userFileSystemOldPath = this.FullPath; try { bool?inSync = null; try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. // When a file is deleted, it is moved to a Recycle Bin, that is why we check for recycle bin here. if (FsPath.Exists(userFileSystemOldPath) && !FsPath.IsRecycleBin(userFileSystemNewPath) && !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath)) { inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync(); string remoteStorageOldPath = Mapping.MapPath(userFileSystemOldPath); string remoteStorageNewPath = Mapping.MapPath(userFileSystemNewPath); FileSystemInfo remoteStorageOldItem = FsPath.GetFileSystemItem(remoteStorageOldPath); if (remoteStorageOldItem is FileInfo) { (remoteStorageOldItem as FileInfo).MoveTo(remoteStorageNewPath); } else { (remoteStorageOldItem as DirectoryInfo).MoveTo(remoteStorageNewPath); } LogMessage("Moved succesefully:", remoteStorageOldPath, remoteStorageNewPath); } } finally { resultContext.ReturnConfirmationResult(); // If a file with content is deleted it is moved to a recycle bin and converted // to a regular file, so placeholder features are not available on it. if ((inSync != null) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem.GetItem(userFileSystemNewPath).SetInSync(inSync.Value); } } } catch (Exception ex) { // remove try-catch when error processing inside CloudProvider is fixed. LogError("Move failed:", $"From: {this.FullPath} to:{userFileSystemNewPath}", ex); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
public static void SetCustomData(Microsoft.Win32.SafeHandles.SafeFileHandle safeHandle, string originalPath) { CustomData customData = new CustomData { OriginalPath = originalPath }; PlaceholderItem.SetCustomData(safeHandle, customData.Serialize()); }
public static void SetCustomData(this PlaceholderItem placeholder, string originalPath) { CustomData customData = new CustomData { OriginalPath = originalPath }; placeholder.SetCustomData(customData.Serialize()); }
public static void SetOriginalPath(this PlaceholderItem placeholder, string originalPath) { byte[] customDataRaw = placeholder.GetCustomData(); CustomData customData = (customDataRaw.Length > 0) ? CustomData.Deserialize(customDataRaw) : new CustomData(); customData.OriginalPath = originalPath; placeholder.SetCustomData(customData.Serialize()); }
/// <summary> /// Recursively updates and creates files and folders in remote storage. /// Synchronizes only folders already loaded into the user file system. /// </summary> /// <param name="userFileSystemFolderPath">Folder path in user file system.</param> internal async Task SyncronizeFolderAsync(string userFileSystemFolderPath) { // In case of on-demand loading the user file system contains only a subset of the server files and folders. // Here we sync folder only if its content already loaded into user file system (folder is not offline). // The folder content is loaded inside IFolder.GetChildrenAsync() method. if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { // LogMessage("Folder offline, skipping:", userFileSystemFolderPath); return; } IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); // LogMessage("Synchronizing:", userFileSystemFolderPath); // Update and create files/folders in remote storage. userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); foreach (string userFileSystemPath in userFileSystemChildren) { try { if (!FsPath.AvoidSync(userFileSystemPath)) { if (PlaceholderItem.GetItem(userFileSystemPath).IsNew(virtualDrive)) { // Create a file/folder in the remote storage. FileSystemItemTypeEnum itemType = FsPath.GetItemType(userFileSystemPath); await virtualDrive.GetRemoteStorageRawItem(userFileSystemPath, itemType, this).CreateAsync(); } else if (!PlaceholderItem.GetItem(userFileSystemPath).IsMoved()) { // Update file/folder in the remote storage. Unlock if auto-locked. FileSystemItemTypeEnum itemType = FsPath.GetItemType(userFileSystemPath); await virtualDrive.GetRemoteStorageRawItem(userFileSystemPath, itemType, this).UpdateAsync(); } } } catch (Exception ex) { LogError("Update failed", userFileSystemPath, null, ex); } // Synchronize subfolders. try { if (Directory.Exists(userFileSystemPath)) { await SyncronizeFolderAsync(userFileSystemPath); } } catch (Exception ex) { LogError("Folder sync failed:", userFileSystemPath, null, ex); } } }
/// <inheritdoc/> protected override int GetPlaceholderCount(Rendering ownerRendering, PlaceholderItem placeholderItem) { if (ownerRendering.Parameters.Contains(SXAPlaceholdersFieldName)) { return(ownerRendering.Parameters[SXAPlaceholdersFieldName].Split(',').Length); } return(1); }
/// <summary> /// Sends contending to the remote storage if the item is modified. /// If Auto-locking is enabled, automatically locks the file if not locked. Unlocks the file after the update if auto-locked. /// </summary> public async Task UpdateAsync() { Lock fileLock = null; try { if (!PlaceholderItem.GetItem(userFileSystemPath).GetInSync()) { // Lock file if auto-locking is enabled. if (Config.Settings.AutoLock) { // Get existing lock or create a new lock. fileLock = await LockAsync(FileMode.OpenOrCreate, LockMode.Auto); } ServerLockInfo lockInfo = fileLock != null ? await fileLock.GetLockInfoAsync() : null; // Update item in remote storage. logger.LogMessage("Sending to remote storage", userFileSystemPath); await CreateOrUpdateAsync(FileMode.Open, lockInfo); //await new UserFileSystemRawItem(userFileSystemPath).ClearStateAsync(); logger.LogMessage("Sent to remote storage succesefully", userFileSystemPath); } // Unlock if auto-locked. if (await Lock.GetLockModeAsync(userFileSystemPath) == LockMode.Auto) { if (fileLock == null) { // Get existing lock. fileLock = await Lock.LockAsync(userFileSystemPath, FileMode.Open, LockMode.None, logger); } await UnlockAsync(fileLock); } } catch (ClientLockFailedException ex) { // Failed to lock file. Possibly blocked from another thread. This is a normal behaviour. logger.LogMessage(ex.Message, userFileSystemPath); } catch (Exception ex) { // Some error when locking, updating or unlocking occured. await new UserFileSystemRawItem(userFileSystemPath).SetUploadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } finally { if (fileLock != null) { fileLock.Dispose(); } } }
/// <summary> /// Recursively restores OriginalPath and the 'locked' icon. /// </summary> /// <param name="userFileSystemNewPath">Path in the user file system to start recursive processing.</param> private async Task MoveToCompletionRecursiveAsync(string userFileSystemNewPath) { if (FsPath.IsFolder(userFileSystemNewPath)) { if (!new DirectoryInfo(userFileSystemNewPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { //LogMessage("Folder offline, skipping:", userFileSystemFolderPath); IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemNewPath, "*"); foreach (string userFileSystemChildPath in userFileSystemChildren) { try { await MoveToCompletionRecursiveAsync(userFileSystemChildPath); } catch (Exception ex) { logger.LogError("Failed to complete move", userFileSystemChildPath, null, ex); } } } } if (FsPath.Exists(userFileSystemNewPath) && // This check is just to avoid extra error in the log. !FsPath.IsRecycleBin(userFileSystemNewPath) && // When a file with content is deleted, it is moved to a Recycle Bin. !FsPath.AvoidAutoLock(userFileSystemNewPath)) // No need to update temp MS Office docs. { // Open file to prevent reads and changes between GetFileDataSizeInfo() call and SetInSync() call. using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemNewPath, FileMode.Open, FileShare.None)) { // If a file with content is deleted it is moved to a recycle bin and converted // to a regular file, so placeholder features are not available on it, checking if a file is a placeholder. if (/*updateTargetOnSuccess &&*/ PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem placeholderNew = PlaceholderItem.GetItem(userFileSystemNewPath); await virtualDrive.GetUserFileSystemRawItem(userFileSystemNewPath, logger).ClearStateAsync(); // Update OriginalPath if the item is not new. // Required for pptx and xlsx files Save As operation. if (!placeholderNew.IsNew(virtualDrive)) { // Update OriginalPath, so the item does not appear as moved. logger.LogMessage("Setting Original Path", userFileSystemNewPath); placeholderNew.SetOriginalPath(userFileSystemNewPath); } // Restore the 'locked' icon. ServerLockInfo existingLock = await virtualDrive.LockManager(userFileSystemNewPath, logger).GetLockInfoAsync(); await virtualDrive.GetUserFileSystemRawItem(userFileSystemNewPath, logger).SetLockInfoAsync(existingLock); } } } }
public void LoadDevice([NotNull] XElement device) { Assert.ArgumentNotNull(device, nameof(device)); items.Clear(); DeviceId = device.GetAttributeValue("id"); var layout = device.GetAttributeValue("l"); if (!string.IsNullOrEmpty(layout)) { LayoutUri = new ItemUri(DatabaseUri, new ItemId(new Guid(layout))); } else { LayoutUri = ItemUri.Empty; } LayoutName = device.GetAttributeValue("ln"); LayoutPath = device.GetAttributeValue("lp"); LayoutSelector.Text = device.GetAttributeValue("lp"); foreach (var element in device.Elements(@"r")) { var renderingItem = new RenderingItem(this, DatabaseUri, element); renderingItem.Modified += SetModified; items.Add(renderingItem); } foreach (var element in device.Elements(@"p")) { var placeholderItem = new PlaceholderItem(DatabaseUri, element); placeholderItem.Modified += SetModified; items.Add(placeholderItem); } if (items.Count > 0 || !string.IsNullOrEmpty(LayoutSelector.Text)) { ListContextMenu.Visibility = Visibility.Visible; NoItems.Visibility = Visibility.Collapsed; List.SelectedIndex = 0; } else { NoItems.Visibility = Visibility.Visible; ListContextMenu.Visibility = Visibility.Collapsed; } }
/// <summary> /// Moves a file or folder placeholder in user file system. /// </summary> /// <param name="userFileSystemNewPath">New path in user file system.</param> /// <remarks> /// This method failes if the file or folder in user file system is modified (not in sync with the remote storage) /// or if the target file exists. /// </remarks> public async Task MoveToAsync(string userFileSystemNewPath) { // Cloud Filter API does not provide a function to move a placeholder file only if it is not modified. // The file may be modified between InSync call, Move() call and SetInSync() in this method. try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { bool inSync = PlaceholderItem.GetItem(userFileSystemPath).GetInSync(); if (inSync) { string eTag = await ETag.GetETagAsync(userFileSystemPath); ETag.DeleteETag(userFileSystemPath); try { Directory.Move(userFileSystemPath, userFileSystemNewPath); } catch { await ETag.SetETagAsync(userFileSystemPath, eTag); throw; } await ETag.SetETagAsync(userFileSystemNewPath, eTag); // The file is marked as not in sync after move/rename. Marking it as in-sync. PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemNewPath); placeholderItem.SetInSync(true); placeholderItem.SetOriginalPath(userFileSystemNewPath); await new UserFileSystemRawItem(userFileSystemNewPath).ClearStateAsync(); } if (!inSync) { throw new ConflictException(Modified.Client, "The item is not in-sync with the cloud."); } } } catch (Exception ex) { string path = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemPath; await new UserFileSystemRawItem(userFileSystemPath).SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } }
/// <summary> /// Returns true if the file was moved in the user file system and /// changes not yet synched to the remote storage. /// </summary> public static bool IsMoved(this PlaceholderItem placeholder) { // If original path was never set, the file was just created, not moved. string originalPath = placeholder.GetOriginalPath(); if (string.IsNullOrEmpty(originalPath)) { return(false); } // Otherwise verify that current file path and original file path are equal. return(!originalPath.TrimEnd(Path.DirectorySeparatorChar).Equals(placeholder.Path.TrimEnd(Path.DirectorySeparatorChar), StringComparison.InvariantCultureIgnoreCase)); }
/// <summary> /// Compares user file system item and remote storage item. Returns true if items are equal. False - otherwise. /// </summary> /// <param name="remoteStorageItem">Remote storage item info.</param> /// <returns>Returns true user file system item and remote storage item are equal. False - otherwise.</returns> public async Task <bool> EqualsAsync(FileSystemInfo remoteStorageItem) { // Here you will typically compare file ETags. // For the sake of simplicity we just compare LastWriteTime. long remoteStorageETag = remoteStorageItem.LastWriteTime.ToBinary(); // We store remote storage LastWriteTime inside custom data when creating and updating files/folders. byte[] userFileSystemItemCustomData = PlaceholderItem.GetItem(userFileSystemPath).GetCustomData(); long userFileSystemETag = BitConverter.ToInt64(userFileSystemItemCustomData); return(remoteStorageETag == userFileSystemETag); }
/// <summary> /// Returns true if the item was created and must be synched to remote storage. /// </summary> /// <returns> /// True if the item was created in the user file system and does not exists /// in the remote storage. False otherwise. /// </returns> public static bool IsNew(this PlaceholderItem placeholder) { // ETag absence signals that the item is new. // However, ETag file may not exists during move operation, // additionally checking OriginalPath presence. // Can not rely on OriginalPath only, // because MS Office files are being deleted and re-created during transactional save. string originalPath = placeholder.GetOriginalPath(); bool eTagFileExists = File.Exists(ETag.GetETagFilePath(placeholder.Path)); return(!eTagFileExists && string.IsNullOrEmpty(originalPath)); }
private void SetAllowed([NotNull] PlaceholderItem item, IEnumerable <ItemId> selectedItems) { var dialog = new SelectRenderingsDialog(); dialog.Initialize("Set Allowed Placeholders", item.DatabaseUri, selectedItems); if (AppHost.Shell.ShowDialog(dialog) != true) { return; } var itemUri = new ItemUri(item.DatabaseUri, new ItemId(new Guid(item.MetaDataItemId))); ItemModifier.Edit(itemUri, "{E391B526-D0C5-439D-803E-17512EAE6222}", string.Join("|", dialog.SelectedRenderings)); }
private async Task CreateOrUpdateFileAsync(string userFileSystemPath, bool create) { try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. FileInfo userFileSystemFile = new FileInfo(userFileSystemPath); FileInfo remoteStorageFile = new FileInfo(remoteStoragePath); FileMode fileMode = create ? FileMode.CreateNew : FileMode.Open; await using (FileStream userFileSystemStream = userFileSystemFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read)) { // If another thread is trying to sync the same file, this call will fail in other threads. // In your implemntation you must lock your remote storage file, or block it for reading and writing by other means. await using (FileStream remoteStorageStream = remoteStorageFile.Open(fileMode, FileAccess.Write, FileShare.None)) { userFileSystemFile.Refresh(); // Ensures LastWriteTimeUtc is in sync with file content after Open() was called. // Update ETag/LastWriteTime in user file system, so the synchronyzation or remote storage monitor would not start the new update. byte[] customData = BitConverter.GetBytes(userFileSystemFile.LastWriteTime.ToBinary()); PlaceholderItem.SetCustomData(userFileSystemStream.SafeFileHandle, customData); // Update remote storage file content. await userFileSystemStream.CopyToAsync(remoteStorageStream); remoteStorageStream.SetLength(userFileSystemStream.Length); // Update remote storage file basic info. WindowsFileSystemItem.SetFileInformation(remoteStorageStream.SafeFileHandle, userFileSystemFile.Attributes & (FileAttributes) ~FileAttributesExt.Pinned, // Remove Pinned flag. userFileSystemFile.CreationTimeUtc, userFileSystemFile.LastWriteTimeUtc, userFileSystemFile.LastAccessTimeUtc, userFileSystemFile.LastWriteTimeUtc); // If you are using ETags, here you will also send to the remote storage a new file ETag. PlaceholderItem.SetInSync(userFileSystemStream.SafeFileHandle, true); } } } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
//$> /// <summary> /// Deletes a file or folder placeholder in user file system. /// </summary> /// <remarks> /// This method throws <see cref="ConflictException"/> if the file or folder or any file or folder /// in the folder hierarchy being deleted in user file system is modified (not in sync with the remote storage). /// </remarks> /// <returns>True if the file was deleted. False - otherwise.</returns> public async Task <bool> DeleteAsync() { // Cloud Filter API does not provide a function to delete a placeholder file only if it is not modified. // Here we check that the file is not modified in user file system, using GetInSync() call. // To avoid the file modification between GetInSync() call and Delete() call we // open it without FileShare.Write flag. try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.Open(userFileSystemPath, (FileAccess)0, FileMode.Open, FileShare.Read | FileShare.Delete)) { if (PlaceholderItem.GetInSync(userFileSystemWinItem.SafeHandle)) { if (FsPath.IsFile(userFileSystemPath)) { File.Delete(userFileSystemPath); } else { Directory.Delete(userFileSystemPath, true); } // Delete ETag ETag.DeleteETag(userFileSystemPath); return(true); } else { throw new ConflictException(Modified.Client, "The item is not in-sync with the cloud."); } } } } catch (Exception ex) { await SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } return(false); }
/// <summary> /// Moves a file or folder placeholder in user file system. /// </summary> /// <param name="userFileSystemNewPath">New path in user file system.</param> /// <remarks> /// This method failes if the file or folder in user file system is modified (not in sync with the remote storage) /// or if the target file exists. /// </remarks> /// <returns>True if the item was moved. False - otherwise.</returns> public async Task <bool> MoveToAsync(string userFileSystemNewPath) { // Cloud Filter API does not provide a function to move a placeholder file only if it is not modified. // The file may be modified between InSync call, Move() call and SetInSync() in this method. bool itemMoved = false; try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { bool inSync = PlaceholderItem.GetItem(userFileSystemPath).GetInSync(); if (inSync) { logger.LogMessage("Moving ETag", userFileSystemPath, userFileSystemNewPath); await eTagManager.MoveToAsync(userFileSystemNewPath); logger.LogMessage("Moving item", userFileSystemPath, userFileSystemNewPath); await virtualDrive.Engine.ServerNotifications(userFileSystemPath).MoveToAsync(userFileSystemNewPath); // The file is marked as not in sync after move/rename. Marking it as in-sync. PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemNewPath); placeholderItem.SetInSync(true); placeholderItem.SetOriginalPath(userFileSystemNewPath); await virtualDrive.GetUserFileSystemRawItem(userFileSystemNewPath, logger).ClearStateAsync(); itemMoved = true; } if (!inSync) { throw new ConflictException(Modified.Client, "The item is not in-sync with the cloud."); } } } catch (Exception ex) { string userFileSystemExPath = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemPath; await virtualDrive.GetUserFileSystemRawItem(userFileSystemExPath, logger).SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } return(itemMoved); }
/// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in the user file system is modified (not in-sync), you will send the file content, // creation time, modification time and attributes to the remote storage. // We also send ETag, to make sure the changes on the server, if any, are not overwritten. Logger.LogMessage("IFile.CloseAsync()", UserFileSystemPath); string userFileSystemFilePath = UserFileSystemPath; // In case the file is moved it does not exist in user file system when CloseAsync() is called. if (Engine.ChangesProcessingEnabled && FsPath.Exists(userFileSystemFilePath) && !FsPath.AvoidSync(userFileSystemFilePath)) { // In case the file is overwritten it is converted to a regular file prior to CloseAsync(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); Logger.LogMessage("Converted to placeholder", userFileSystemFilePath); } try { if (PlaceholderItem.GetItem(userFileSystemFilePath).IsNew(VirtualDrive)) { // Create new file in the remote storage. await new RemoteStorageRawItem <IVirtualFile>(userFileSystemFilePath, VirtualDrive, Logger).CreateAsync(); } else if (!PlaceholderItem.GetItem(userFileSystemFilePath).IsMoved()) { // Send content to remote storage. Unlock if auto-locked. await new RemoteStorageRawItem <IVirtualFile>(userFileSystemFilePath, VirtualDrive, Logger).UpdateAsync(); } } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. Logger.LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
/// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in user file system is modified (not in-sync), we send file content, // creation time, modification time and attributes to remote storage. // We also create new ETag, associate it with a file in user file system and send it to the server. LogMessage("IFile.CloseAsync()", this.FullPath); string userFileSystemFilePath = this.FullPath; // In case the file is moved it does not exist in user file system when Close() is called. if (!FsPath.Exists(userFileSystemFilePath) || FsPath.AvoidSync(userFileSystemFilePath)) { return; } // In case the file is overwritten it is converted to a regular file prior to Close(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); LogMessage("Converted to placeholder:", userFileSystemFilePath); } if (!PlaceholderItem.GetItem(userFileSystemFilePath).GetInSync()) { LogMessage("Changed:", userFileSystemFilePath); string remoteStorageFilePath = Mapping.MapPath(userFileSystemFilePath); try { await new RemoteStorageItem(remoteStorageFilePath).UpdateAsync(userFileSystemFilePath); LogMessage("Updated succesefully:", remoteStorageFilePath); } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
/// <summary> /// Moves a file or folder placeholder in user file system. /// </summary> /// <param name="newUserFileSystemPath">New path in user file system.</param> /// <remarks> /// This method failes if the file or folder in user file system is modified (not in sync with the remote storage) /// or if the target file exists. /// </remarks> public async Task MoveAsync(string newUserFileSystemPath) { // Windows does not provide a function to move a placeholder file only if it is not modified. // The file may be modified between InSync call, Move() call and SetInSync() in this method. PlaceholderItem oldUserFileSystemItem = PlaceholderItem.GetItem(userFileSystemPath); if (oldUserFileSystemItem.GetInSync()) { Directory.Move(userFileSystemPath, newUserFileSystemPath); // The file is marked as not in sync after move/rename. Marking it as in-sync. PlaceholderItem.GetItem(newUserFileSystemPath).SetInSync(true); } else { throw new IOException("File not in-sync."); } }
/// <summary> /// Updates information about the file or folder placeholder in the user file system. /// This method automatically hydrates and dehydrate files. /// </summary> /// <remarks>This method failes if the file or folder in user file system is modified (not in-sync with the remote storage).</remarks> /// <param name="itemInfo">New file or folder info.</param> /// <returns>True if the file was updated. False - otherwise.</returns> public async Task <bool> UpdateAsync(IFileSystemItemMetadata itemInfo) { FileSystemItemMetadataExt itemInfoExt = itemInfo as FileSystemItemMetadataExt ?? throw new NotImplementedException($"{nameof(FileSystemItemMetadataExt)}"); try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemPath); // To be able to update the item we need to remove the read-only attribute. if ((FsPath.GetFileSystemItem(userFileSystemPath).Attributes | System.IO.FileAttributes.ReadOnly) != 0) { FsPath.GetFileSystemItem(userFileSystemPath).Attributes &= ~System.IO.FileAttributes.ReadOnly; } // Dehydrate/hydrate the file, update file size, custom data, creation date, modification date, attributes. await virtualDrive.Engine.ServerNotifications(userFileSystemPath).UpdateAsync(itemInfoExt); // Set ETag. await eTagManager.SetETagAsync(itemInfoExt.ETag); // Clear icon. //await ClearStateAsync(); // Set the read-only attribute and all custom columns data. await SetLockedByAnotherUserAsync(itemInfoExt.LockedByAnotherUser); await SetCustomColumnsDataAsync(itemInfoExt.CustomProperties); return(true); } } catch (Exception ex) { await SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } return(false); }
public void AddPlaceHolder([NotNull] DatabaseUri databaseUri) { Assert.ArgumentNotNull(databaseUri, nameof(databaseUri)); var placeholderItem = new PlaceholderItem(databaseUri) { Id = @"content", Icon = new Icon("Resources/16x16/star_yellow.png"), UniqueId = Guid.NewGuid().ToString(@"B").ToUpperInvariant() }; placeholderItem.Modified += SetModified; items.Add(placeholderItem); List.SelectedItem = placeholderItem; Modified = true; NoItems.Visibility = Visibility.Collapsed; ListContextMenu.Visibility = Visibility.Visible; }
//$> //$<PlaceholderItem.SetItemInfo /// <summary> /// Updates information about the file or folder placeholder in the user file system. /// This method automatically hydrates and dehydrate files. /// </summary> /// <remarks>This method failes if the file or folder in user file system is modified (not in-sync with the remote storage).</remarks> /// <param name="itemInfo">New file or folder info.</param> /// <returns>True if the file was updated. False - otherwise.</returns> public async Task <bool> UpdateAsync(FileSystemItemBasicInfo itemInfo) { try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. if (FsPath.Exists(userFileSystemPath)) { PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemPath); // To be able to update the item we need to remove the read-only attribute. if ((FsPath.GetFileSystemItem(userFileSystemPath).Attributes | System.IO.FileAttributes.ReadOnly) != 0) { FsPath.GetFileSystemItem(userFileSystemPath).Attributes &= ~System.IO.FileAttributes.ReadOnly; } // Dehydrate/hydrate the file, update file size, custom data, creation date, modification date, attributes. placeholderItem.SetItemInfo(itemInfo); // Set ETag. await ETag.SetETagAsync(userFileSystemPath, itemInfo.ETag); // Clear icon. //await ClearStateAsync(); // Set the "locked by another user" icon and all custom columns data. await SetLockedByAnotherUserAsync(itemInfo.LockedByAnotherUser); await SetCustomColumnsDataAsync(itemInfo.CustomProperties); return(true); } } catch (Exception ex) { await SetDownloadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } return(false); }
/// <summary> /// Called when a file or folder is renamed in the user file system. /// </summary> private async void RenamedAsync(object sender, RenamedEventArgs e) { // If the item and was previusly filtered by EngineWindows.FilterAsync(), // for example temp MS Office file was renamed SGE4274H->file.xlsx, // we need to convert the file to a pleaceholder and upload it to the remote storage. // We must also LogMessage("Renamed", e.OldFullPath, e.FullPath); string userFileSystemOldPath = e.OldFullPath; string userFileSystemNewPath = e.FullPath; try { if (System.IO.File.Exists(userFileSystemNewPath) && !MsOfficeHelper.AvoidMsOfficeSync(userFileSystemNewPath)) { if (!PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { if (engine.CustomDataManager(userFileSystemNewPath).IsNew) { await engine.ClientNotifications(userFileSystemNewPath, this).CreateAsync(); } else { LogMessage("Converting to placeholder", userFileSystemNewPath); PlaceholderItem.ConvertToPlaceholder(userFileSystemNewPath, null, null, false); await engine.ClientNotifications(userFileSystemNewPath, this).UpdateAsync(); await engine.CustomDataManager(userFileSystemNewPath).RefreshCustomColumnsAsync(); } } } } catch (Exception ex) { LogError($"{e.ChangeType} failed", userFileSystemOldPath, userFileSystemNewPath, ex); } }
/// <summary> /// Completes move. This method is called by the platform. /// To move item manually use the <see cref="MoveToAsync(string)"/> method instead. /// Sets In-Sync state based on file content changes, /// Updates OriginalPath so the file does not appear as moved. /// </summary> /// <returns></returns> internal async Task MoveToCompletionAsync() { string userFileSystemNewPath = userFileSystemPath; if (FsPath.Exists(userFileSystemNewPath) && // This check is just to avoid extra error in the log. !FsPath.IsRecycleBin(userFileSystemNewPath) && // When a file with content is deleted, it is moved to a Recycle Bin. !FsPath.AvoidAutoLock(userFileSystemNewPath)) // No need to update temp MS Office docs. { // Open file to prevent reads and changes between GetFileDataSizeInfo() call and SetInSync() call. //using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemNewPath, FileMode.Open, FileShare.None)) { // If a file with content is deleted it is moved to a recycle bin and converted // to a regular file, so placeholder features are not available on it, checking if a file is a placeholder. if (/*updateTargetOnSuccess &&*/ PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem placeholderNew = PlaceholderItem.GetItem(userFileSystemNewPath); // Restore In-sync state. /* * if (inSync != null) * { * placeholderNew.SetInSync(inSync.Value); * } * else */ if (((placeholderNew is PlaceholderFile) && ((PlaceholderFile)placeholderNew).GetFileDataSizeInfo().ModifiedDataSize == 0) || (placeholderNew is PlaceholderFolder)) { logger.LogMessage("Setting In-Sync state", userFileSystemNewPath); placeholderNew.SetInSync(true); } } } // Recursively restore OriginalPath and the 'locked' icon. await MoveToCompletionRecursiveAsync(userFileSystemNewPath); } }
/// <summary> /// Gets file system item info from the user file system item. /// Removes Pinned, Unpinned and Offline flags from attributes. /// </summary> /// <param name="userFileSystemItem">User file system item info.</param> /// <returns><see cref="FileSystemItemBasicInfo"/> object.</returns> private static IFileSystemItemBasicInfo GetBasicInfo(FileSystemInfo userFileSystemItem) { FileSystemItemBasicInfo itemInfo; if (userFileSystemItem is FileInfo) { itemInfo = new FileBasicInfo(); } else { itemInfo = new FolderBasicInfo(); } // Remove Pinned, unpinned and offline flags, // so they do not occasionally go into the remote storage. FileAttributes flags = userFileSystemItem.Attributes & (FileAttributes) ~FileAttributesExt.Pinned & (FileAttributes) ~FileAttributesExt.Unpinned & (FileAttributes) ~FileAttributesExt.Offline; itemInfo.Name = userFileSystemItem.Name; itemInfo.Attributes = flags; itemInfo.CreationTime = userFileSystemItem.CreationTime; itemInfo.LastWriteTime = userFileSystemItem.LastWriteTime; itemInfo.LastAccessTime = userFileSystemItem.LastAccessTime; itemInfo.ChangeTime = userFileSystemItem.LastWriteTime; itemInfo.CustomData = PlaceholderItem.GetItem(userFileSystemItem.FullName).GetCustomData(); if (userFileSystemItem is FileInfo) { ((FileBasicInfo)itemInfo).Length = ((FileInfo)userFileSystemItem).Length; } ; return(itemInfo); }