private ISet <string> GetPathsUnderRecycleBin(IEnumerable <IStorageItemWithPath> source) { return(source.Select(item => item.Path).Where(path => recycleBinHelpers.IsPathUnderRecycleBin(path)).ToHashSet()); }
public async Task <IStorageHistory> DeleteAsync(IStorageItemWithPath source, IProgress <float> progress, IProgress <FilesystemErrorCode> errorCode, bool permanently, CancellationToken cancellationToken) { bool deleteFromRecycleBin = recycleBinHelpers.IsPathUnderRecycleBin(source.Path); FilesystemResult fsResult = FilesystemErrorCode.ERROR_INPROGRESS; errorCode?.Report(fsResult); progress?.Report(0.0f); if (source.ItemType == FilesystemItemType.File) { fsResult = await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(source.Path) .OnSuccess((t) => t.DeleteAsync(permanently ? StorageDeleteOption.PermanentDelete : StorageDeleteOption.Default).AsTask()); } else if (source.ItemType == FilesystemItemType.Directory) { fsResult = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(source.Path) .OnSuccess((t) => t.DeleteAsync(permanently ? StorageDeleteOption.PermanentDelete : StorageDeleteOption.Default).AsTask()); } errorCode?.Report(fsResult); if (fsResult == FilesystemErrorCode.ERROR_UNAUTHORIZED) { // Try again with fulltrust process if (associatedInstance.FilesystemViewModel.Connection != null) { AppServiceResponse response = await associatedInstance.FilesystemViewModel.Connection.SendMessageAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "DeleteItem" }, { "filepath", source.Path }, { "permanently", permanently } }); fsResult = (FilesystemResult)(response.Status == AppServiceResponseStatus.Success && response.Message.Get("Success", false)); } } else if (fsResult == FilesystemErrorCode.ERROR_INUSE) { // TODO: retry or show dialog await DialogDisplayHelper.ShowDialogAsync("FileInUseDeleteDialog/Title".GetLocalized(), "FileInUseDeleteDialog/Text".GetLocalized()); } if (deleteFromRecycleBin) { // Recycle bin also stores a file starting with $I for each item string iFilePath = Path.Combine(Path.GetDirectoryName(source.Path), Path.GetFileName(source.Path).Replace("$R", "$I")); await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(iFilePath) .OnSuccess(t => t.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask()); } errorCode?.Report(fsResult); progress?.Report(100.0f); if (fsResult) { await associatedInstance.FilesystemViewModel.RemoveFileOrFolderAsync(source.Path); if (!permanently) { // Enumerate Recycle Bin List <ShellFileItem> items = await recycleBinHelpers.EnumerateRecycleBin(); List <ShellFileItem> nameMatchItems = new List <ShellFileItem>(); // Get name matching files if (items != null) { if (Path.GetExtension(source.Path) == ".lnk" || Path.GetExtension(source.Path) == ".url") // We need to check if it is a shortcut file { nameMatchItems = items.Where((item) => item.FilePath == Path.Combine(Path.GetDirectoryName(source.Path), Path.GetFileNameWithoutExtension(source.Path))).ToList(); } else { nameMatchItems = items.Where((item) => item.FilePath == source.Path).ToList(); } } // Get newest file ShellFileItem item = nameMatchItems.Where((item) => item.RecycleDate != null).OrderBy((item) => item.RecycleDate).FirstOrDefault(); return(new StorageHistory(FileOperationType.Recycle, source, StorageItemHelpers.FromPathAndType(item?.RecyclePath, source.ItemType))); } return(new StorageHistory(FileOperationType.Delete, source, null)); } else { // Stop at first error return(null); } }
public async Task <ReturnResult> DeleteItemsAsync(IEnumerable <IStorageItemWithPath> source, bool showDialog, bool permanently, bool registerHistory) { source = await source.ToListAsync(); var returnStatus = ReturnResult.InProgress; var deleteFromRecycleBin = source.Select(item => item.Path).Any(path => recycleBinHelpers.IsPathUnderRecycleBin(path)); var canBeSentToBin = !deleteFromRecycleBin && await recycleBinHelpers.HasRecycleBin(source.FirstOrDefault()?.Path); if (((!permanently && !canBeSentToBin) || UserSettingsService.PreferencesSettingsService.ShowConfirmDeleteDialog) && showDialog) // Check if the setting to show a confirmation dialog is on { var incomingItems = new List <BaseFileSystemDialogItemViewModel>(); List <ShellFileItem> binItems = null; foreach (var src in source) { if (recycleBinHelpers.IsPathUnderRecycleBin(src.Path)) { binItems ??= await recycleBinHelpers.EnumerateRecycleBin(); if (!binItems.IsEmpty()) // Might still be null because we're deserializing the list from Json { var matchingItem = binItems.FirstOrDefault(x => x.RecyclePath == src.Path); // Get original file name incomingItems.Add(new FileSystemDialogDefaultItemViewModel() { SourcePath = src.Path, DisplayName = matchingItem?.FileName ?? src.Name }); } } else { incomingItems.Add(new FileSystemDialogDefaultItemViewModel() { SourcePath = src.Path }); } } var dialogViewModel = FileSystemDialogViewModel.GetDialogViewModel( new() { IsInDeleteMode = true }, (canBeSentToBin ? permanently : true, canBeSentToBin), FilesystemOperationType.Delete, incomingItems, new()); var dialogService = Ioc.Default.GetRequiredService <IDialogService>(); if (await dialogService.ShowDialogAsync(dialogViewModel) != DialogResult.Primary) { return(ReturnResult.Cancelled); // Return if the result isn't delete } // Delete selected items if the result is Yes permanently = dialogViewModel.DeletePermanently; } // post the status banner var banner = PostBannerHelpers.PostBanner_Delete(source, returnStatus, permanently, false, 0); banner.ErrorCode.ProgressChanged += (s, e) => returnStatus = returnStatus < ReturnResult.Failed ? e.ToStatus() : returnStatus; var token = banner.CancellationToken; var sw = new Stopwatch(); sw.Start(); IStorageHistory history = await filesystemOperations.DeleteItemsAsync((IList <IStorageItemWithPath>) source, banner.Progress, banner.ErrorCode, permanently, token); ((IProgress <float>)banner.Progress).Report(100.0f); await Task.Yield(); if (!permanently && registerHistory) { App.HistoryWrapper.AddHistory(history); } var itemsDeleted = history?.Source.Count() ?? 0; source.ForEach(x => App.JumpList.RemoveFolder(x.Path)); // Remove items from jump list banner.Remove(); sw.Stop(); PostBannerHelpers.PostBanner_Delete(source, returnStatus, permanently, token.IsCancellationRequested, itemsDeleted); return(returnStatus); }
public async Task <ReturnResult> DeleteItemAsync(IStorageItemWithPath source, bool showDialog, bool permanently, bool registerHistory) { PostedStatusBanner banner; bool deleteFromRecycleBin = recycleBinHelpers.IsPathUnderRecycleBin(source.Path); if (deleteFromRecycleBin) { permanently = true; } if (permanently) { banner = associatedInstance.StatusCenterActions.PostBanner(string.Empty, associatedInstance.FilesystemViewModel.WorkingDirectory, 0, ReturnResult.InProgress, FileOperationType.Delete); } else { banner = associatedInstance.StatusCenterActions.PostBanner(string.Empty, associatedInstance.FilesystemViewModel.WorkingDirectory, 0, ReturnResult.InProgress, FileOperationType.Recycle); } var returnStatus = ReturnResult.InProgress; banner.ErrorCode.ProgressChanged += (s, e) => returnStatus = e.ToStatus(); if (App.AppSettings.ShowConfirmDeleteDialog && showDialog) // Check if the setting to show a confirmation dialog is on { List <FilesystemItemsOperationItemModel> incomingItems = new List <FilesystemItemsOperationItemModel> { new FilesystemItemsOperationItemModel(FilesystemOperationType.Delete, source.Path ?? source.Item.Path, null) }; FilesystemOperationDialog dialog = FilesystemOperationDialogViewModel.GetDialog(new FilesystemItemsOperationDataModel( FilesystemOperationType.Delete, false, !deleteFromRecycleBin ? permanently : deleteFromRecycleBin, !deleteFromRecycleBin, incomingItems, new List <FilesystemItemsOperationItemModel>())); ContentDialogResult result = await dialog.ShowAsync(); if (result != ContentDialogResult.Primary) { banner.Remove(); return(ReturnResult.Cancelled); // Return if the result isn't delete } // Delete selected item if the result is Yes permanently = dialog.ViewModel.PermanentlyDelete; } var sw = new Stopwatch(); sw.Start(); IStorageHistory history = await filesystemOperations.DeleteAsync(source, banner.Progress, banner.ErrorCode, permanently, cancellationToken); ((IProgress <float>)banner.Progress).Report(100.0f); if (!permanently && registerHistory) { App.HistoryWrapper.AddHistory(history); } banner.Remove(); sw.Stop(); PostBannerHelpers.PostBanner_Delete(returnStatus, permanently ? FileOperationType.Delete : FileOperationType.Recycle, sw, associatedInstance); return(returnStatus); }
public async Task <IStorageHistory> DeleteItemsAsync(IEnumerable <IStorageItemWithPath> source, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, bool permanently, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\"))) { // Fallback to builtin file operations return(await filesystemOperations.DeleteItemsAsync(source, progress, errorCode, permanently, cancellationToken)); } var deleleFilePaths = source.Select(s => s.Path); var deleteFromRecycleBin = source.Any() ? recycleBinHelpers.IsPathUnderRecycleBin(source.ElementAt(0).Path) : false; permanently |= deleteFromRecycleBin; if (deleteFromRecycleBin) { // Recycle bin also stores a file starting with $I for each item deleleFilePaths = deleleFilePaths.Concat(source.Select(x => Path.Combine(Path.GetDirectoryName(x.Path), Path.GetFileName(x.Path).Replace("$R", "$I")))); } var operationID = Guid.NewGuid().ToString(); using var _ = cancellationToken.Register(CancelOperation, operationID, false); EventHandler <Dictionary <string, object> > handler = (s, e) => OnProgressUpdated(s, e, progress); connection.RequestReceived += handler; var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "DeleteItem" }, { "operationID", operationID }, { "filepath", string.Join('|', deleleFilePaths) }, { "permanently", permanently } }); var result = (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); if (connection != null) { connection.RequestReceived -= handler; } if (result) { progress?.Report(100.0f); var deletedItems = JsonConvert.DeserializeObject <IEnumerable <string> >(response["DeletedItems"] as string); var recycledItems = JsonConvert.DeserializeObject <IEnumerable <string> >(response["RecycledItems"] as string); errorCode?.Report(FileSystemStatusCode.Success); if (deletedItems != null) { foreach (var item in deletedItems) { await associatedInstance.FilesystemViewModel.RemoveFileOrFolderAsync(item); } } if (!permanently && recycledItems != null && recycledItems.Count() == source.Count()) { return(new StorageHistory(FileOperationType.Recycle, source, recycledItems.Select((item, index) => StorageItemHelpers.FromPathAndType(item, source.ElementAt(index).ItemType)))); } return(new StorageHistory(FileOperationType.Delete, source, null)); } else { // Retry failed operations var deletedSources = JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("DeletedItems", "")) ?? Enumerable.Empty <string>(); var deletedZip = source.Where(x => !deletedSources.Contains(x.Path)); return(await filesystemOperations.DeleteItemsAsync(deletedZip, progress, errorCode, permanently, cancellationToken)); } }
public async Task <IStorageHistory> DeleteItemsAsync(IEnumerable <IStorageItemWithPath> source, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, bool permanently, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\", StringComparison.Ordinal) || FtpHelpers.IsFtpPath(x.Path))) { // Fallback to builtin file operations return(await filesystemOperations.DeleteItemsAsync(source, progress, errorCode, permanently, cancellationToken)); } source = source.DistinctBy(x => x.Path); // #5771 var deleleFilePaths = source.Select(s => s.Path); var deleteFromRecycleBin = source.Any() ? recycleBinHelpers.IsPathUnderRecycleBin(source.ElementAt(0).Path) : false; permanently |= deleteFromRecycleBin; if (deleteFromRecycleBin) { // Recycle bin also stores a file starting with $I for each item deleleFilePaths = deleleFilePaths.Concat(source.Select(x => Path.Combine(Path.GetDirectoryName(x.Path), Path.GetFileName(x.Path).Replace("$R", "$I", StringComparison.Ordinal)))).Distinct(); } var operationID = Guid.NewGuid().ToString(); using var r = cancellationToken.Register(CancelOperation, operationID, false); EventHandler <Dictionary <string, object> > handler = (s, e) => OnProgressUpdated(s, e, operationID, progress); connection.RequestReceived += handler; var deleteResult = new ShellOperationResult(); var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "DeleteItem" }, { "operationID", operationID }, { "filepath", string.Join('|', deleleFilePaths) }, { "permanently", permanently }, { "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() } }); var result = (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); var shellOpResult = JsonConvert.DeserializeObject <ShellOperationResult>(response.Get("Result", "")); deleteResult.Items.AddRange(shellOpResult?.Items ?? Enumerable.Empty <ShellOperationItemResult>()); if (connection != null) { connection.RequestReceived -= handler; } result &= (FilesystemResult)deleteResult.Items.All(x => x.Succeeded); if (result) { progress?.Report(100.0f); errorCode?.Report(FileSystemStatusCode.Success); foreach (var item in deleteResult.Items) { await associatedInstance.FilesystemViewModel.RemoveFileOrFolderAsync(item.Source); } var recycledSources = deleteResult.Items.Where(x => source.Select(s => s.Path).Contains(x.Source)).Where(x => x.Succeeded && x.Destination != null && x.Source != x.Destination); if (recycledSources.Any()) { return(new StorageHistory(FileOperationType.Recycle, recycledSources.Select(x => source.Single(s => s.Path == x.Source)), recycledSources.Select(item => StorageHelpers.FromPathAndType(item.Destination, source.Single(s => s.Path == item.Source).ItemType)))); } return(new StorageHistory(FileOperationType.Delete, source, null)); } else { // Retry failed operations var failedSources = deleteResult.Items.Where(x => source.Select(s => s.Path).Contains(x.Source)) .Where(x => !x.Succeeded && x.HResult != HResult.COPYENGINE_E_USER_CANCELLED && x.HResult != HResult.COPYENGINE_E_RECYCLE_BIN_NOT_FOUND); return(await filesystemOperations.DeleteItemsAsync( failedSources.Select(x => source.Single(s => s.Path == x.Source)), progress, errorCode, permanently, cancellationToken)); } }
public async Task <ReturnResult> DeleteItemsAsync(IEnumerable <IStorageItemWithPath> source, bool showDialog, bool permanently, bool registerHistory) { source = await source.ToListAsync(); var returnStatus = ReturnResult.InProgress; var deleteFromRecycleBin = source.Select(item => item.Path).Any(path => recycleBinHelpers.IsPathUnderRecycleBin(path)); var canBeSentToBin = !deleteFromRecycleBin && await recycleBinHelpers.HasRecycleBin(source.FirstOrDefault()?.Path); if (((!permanently && !canBeSentToBin) || UserSettingsService.PreferencesSettingsService.ShowConfirmDeleteDialog) && showDialog) // Check if the setting to show a confirmation dialog is on { List <FilesystemItemsOperationItemModel> incomingItems = new List <FilesystemItemsOperationItemModel>(); foreach (var src in source) { if (recycleBinHelpers.IsPathUnderRecycleBin(src.Path)) { var binItems = associatedInstance.FilesystemViewModel.FilesAndFolders; var matchingItem = binItems.FirstOrDefault(x => x.ItemPath == src.Path); // Get original file name incomingItems.Add(new FilesystemItemsOperationItemModel(FilesystemOperationType.Delete, src.Path, null, matchingItem?.ItemName)); } else { incomingItems.Add(new FilesystemItemsOperationItemModel(FilesystemOperationType.Delete, src.Path, null)); } } var dialog = FilesystemOperationDialogViewModel.GetDialog(new FilesystemItemsOperationDataModel( FilesystemOperationType.Delete, false, canBeSentToBin ? permanently : true, canBeSentToBin, incomingItems, new List <FilesystemItemsOperationItemModel>())); if (await dialog.TryShowAsync() != DialogResult.Primary) { return(ReturnResult.Cancelled); // Return if the result isn't delete } // Delete selected items if the result is Yes permanently = dialog.ViewModel.PermanentlyDelete; } // post the status banner var banner = PostBannerHelpers.PostBanner_Delete(source, returnStatus, permanently, false, 0); banner.ErrorCode.ProgressChanged += (s, e) => returnStatus = e.ToStatus(); var token = banner.CancellationToken; var sw = new Stopwatch(); sw.Start(); IStorageHistory history = await filesystemOperations.DeleteItemsAsync((IList <IStorageItemWithPath>) source, banner.Progress, banner.ErrorCode, permanently, token); ((IProgress <float>)banner.Progress).Report(100.0f); await Task.Yield(); if (!permanently && registerHistory) { App.HistoryWrapper.AddHistory(history); } var itemsDeleted = history?.Source.Count() ?? 0; source.ForEach(x => App.JumpList.RemoveFolder(x.Path)); // Remove items from jump list banner.Remove(); sw.Stop(); PostBannerHelpers.PostBanner_Delete(source, returnStatus, permanently, token.IsCancellationRequested, itemsDeleted); return(returnStatus); }