Skip to content

Suggested changes #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 51 additions & 29 deletions src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,39 +351,62 @@ await DialogDisplayHelper.ShowDialogAsync(
}
else
{
FilesystemResult<BaseStorageFolder> destinationResult = await _associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination));
var sourceResult = await source.ToStorageItemResult();
FilesystemResult fsResult = sourceResult.ErrorCode | destinationResult.ErrorCode;
var fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.MoveFileFromApp(source.Path, destination));

if (fsResult)
if (!fsResult)
{
if (sourceResult.Result is IPasswordProtectedItem ppis)
ppis.PasswordRequestedCallback = UIFilesystemHelpers.RequestPassword;
Debug.WriteLine(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

var folder = (BaseStorageFolder)sourceResult;
FilesystemResult fsResultMove;
fsResultMove = await FilesystemTasks.Wrap(() => folder.MoveFolderAsync(destinationResult.Result, collision).AsTask());
var fsSourceFolder = await source.ToStorageItemResult();
var fsDestinationFolder = await _associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(PathNormalization.GetParentDir(destination));
fsResult = fsSourceFolder.ErrorCode | fsDestinationFolder.ErrorCode;

if (sourceResult.Result is IPasswordProtectedItem ppiu)
ppiu.PasswordRequestedCallback = null;

if (fsResultMove == FileSystemStatusCode.AlreadyExists)
if (fsResult)
{
fsProgress.ReportStatus(FileSystemStatusCode.AlreadyExists);
if (fsSourceFolder.Result is IPasswordProtectedItem ppis)
ppis.PasswordRequestedCallback = UIFilesystemHelpers.RequestPassword;

return null;
}
var srcFolder = (BaseStorageFolder)fsSourceFolder;
var fsResultMove = await FilesystemTasks.Wrap(() => srcFolder.MoveAsync(fsDestinationFolder.Result, collision).AsTask());

if (fsResultMove)
movedItem = folder;
if (!fsResultMove) // Use generic move folder operation (move folder items one by one)
{
// Moving folders using Storage API can result in data loss, copy instead
//var fsResultMove = await FilesystemTasks.Wrap(() => MoveDirectoryAsync((BaseStorageFolder)fsSourceFolder, (BaseStorageFolder)fsDestinationFolder, fsSourceFolder.Result.Name, collision.Convert(), true));

fsResult = fsResultMove;
}
if (fsResult == FileSystemStatusCode.Unauthorized || fsResult == FileSystemStatusCode.ReadOnly)
{
// Cannot do anything, already tried with admin FTP
if (await DialogDisplayHelper.ShowDialogAsync("ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), "ErrorDialogUnsupportedMoveOperation".GetLocalizedResource(), "OK", "Cancel".GetLocalizedResource()))
fsResultMove = await FilesystemTasks.Wrap(() => CloneDirectoryAsync((BaseStorageFolder)fsSourceFolder, (BaseStorageFolder)fsDestinationFolder, fsSourceFolder.Result.Name, collision.Convert()));
}

if (fsSourceFolder.Result is IPasswordProtectedItem ppiu)
ppiu.PasswordRequestedCallback = null;

if (fsResultMove == FileSystemStatusCode.AlreadyExists)
{
fsProgress.ReportStatus(FileSystemStatusCode.AlreadyExists);

return null;
}

if (fsResultMove)
{
if (NativeFileOperationsHelper.HasFileAttribute(source.Path, SystemIO.FileAttributes.Hidden))
{
// The source folder was hidden, apply hidden attribute to destination
NativeFileOperationsHelper.SetFileAttribute(fsResultMove.Result.Path, SystemIO.FileAttributes.Hidden);
}

movedItem = (BaseStorageFolder)fsResultMove;
}
fsResult = fsResultMove;
}
if (fsResult == FileSystemStatusCode.Unauthorized || fsResult == FileSystemStatusCode.ReadOnly)
{
// Cannot do anything, already tried with admin FTP
}
}
fsProgress.ReportStatus(sourceResult.ErrorCode);

fsProgress.ReportStatus(fsResult.ErrorCode);
}
}
else if (source.ItemType == FilesystemItemType.File)
Expand All @@ -404,8 +427,7 @@ await DialogDisplayHelper.ShowDialogAsync(
ppis.PasswordRequestedCallback = UIFilesystemHelpers.RequestPassword;

var file = (BaseStorageFile)sourceResult;
FilesystemResult fsResultMove;
fsResultMove = await FilesystemTasks.Wrap(() => file.MoveAsync(destinationResult.Result, Path.GetFileName(file.Name), collision).AsTask());
var fsResultMove = await FilesystemTasks.Wrap(() => file.MoveAsync(destinationResult.Result, Path.GetFileName(file.Name), collision).AsTask());

if (sourceResult.Result is IPasswordProtectedItem ppiu)
ppiu.PasswordRequestedCallback = null;
Expand Down Expand Up @@ -436,9 +458,10 @@ await DialogDisplayHelper.ShowDialogAsync(
return null;
}

bool sourceInCurrentFolder = PathNormalization.TrimPath(_associatedInstance.FilesystemViewModel.CurrentFolder.ItemPath) ==
bool sourceInCurrentFolder = PathNormalization.TrimPath(_associatedInstance.FilesystemViewModel.CurrentFolder.ItemPath) ==
PathNormalization.GetParentDir(source.Path);
if (fsProgress.Status == FileSystemStatusCode.Success && sourceInCurrentFolder) {
if (fsProgress.Status == FileSystemStatusCode.Success && sourceInCurrentFolder)
{
await _associatedInstance.FilesystemViewModel.RemoveFileOrFolderAsync(source.Path);
await _associatedInstance.FilesystemViewModel.ApplyFilesAndFoldersChangesAsync();
}
Expand Down Expand Up @@ -703,7 +726,6 @@ public async Task<IStorageHistory> RestoreFromTrashAsync(IStorageItemWithPath so
if (fsResult)
{
// Moving folders using Storage API can result in data loss, copy instead

//fsResult = await FilesystemTasks.Wrap(() => MoveDirectoryAsync(sourceFolder.Result, destinationFolder.Result, Path.GetFileName(destination), CreationCollisionOption.FailIfExists, true));

if (await DialogDisplayHelper.ShowDialogAsync("ErrorDialogThisActionCannotBeDone".GetLocalizedResource(), "ErrorDialogUnsupportedMoveOperation".GetLocalizedResource(), "OK", "Cancel".GetLocalizedResource()))
Expand Down
18 changes: 2 additions & 16 deletions src/Files.App/Utils/Storage/StorageBaseItems/BaseStorageFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,24 +184,10 @@ IAsyncOperation<StorageFolder> IStorageFolder.CreateFolderAsync(string desiredNa
=> await (await CreateFolderAsync(desiredName, options)).ToStorageFolderAsync());
}

public abstract IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder);
public abstract IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder);

IAsyncAction IBaseStorageFolder.MoveFolderAsync(IStorageFolder destinationFolder)
{
return
AsyncInfo.Run(async (cancellationToken)
=> await MoveFolderAsync(destinationFolder));
}

public abstract IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option);
public abstract IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option);

IAsyncAction IBaseStorageFolder.MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option)
{
return
AsyncInfo.Run(async (cancellationToken)
=> await MoveFolderAsync(destinationFolder, option));
}

public abstract IAsyncAction RenameAsync(string desiredName);

public abstract IAsyncAction RenameAsync(string desiredName, NameCollisionOption option);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ public interface IBaseStorageFolder : IStorageItem2, IStorageFolder, IStorageFol
new IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desiredName);

new IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desiredName, CreationCollisionOption options);
IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder);
IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option);

IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder);
IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option);

new BaseStorageItemQueryResult CreateItemQueryWithOptions(QueryOptions queryOptions);

Expand Down
15 changes: 9 additions & 6 deletions src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,20 @@ public override IAsyncAction MoveAsync(IStorageFolder destinationFolder, string
using var ftpClient = GetFtpClient();
if (!await ftpClient.EnsureConnectedAsync())
throw new IOException($"Failed to connect to FTP server.");

BaseStorageFolder destFolder = destinationFolder.AsBaseStorageFolder();
if (destFolder is FtpStorageFolder ftpFolder) {
string destName = $"{(ftpFolder).FtpPath}/{Name}";

if (destFolder is FtpStorageFolder ftpFolder)
{
string destName = $"{ftpFolder.FtpPath}/{Name}";
FtpRemoteExists ftpRemoteExists = option is NameCollisionOption.ReplaceExisting ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;

bool isSuccessful = await ftpClient.MoveFile(FtpPath, destName, ftpRemoteExists, cancellationToken);
if (!isSuccessful)
throw new IOException($"Failed to move folder from {Path} to {destFolder}.");
throw new IOException($"Failed to move file from {Path} to {destFolder}.");
}

else
throw new NotSupportedException();
}, ((IPasswordProtectedItem)this).RetryWithCredentials));
}

Expand Down Expand Up @@ -258,7 +261,7 @@ private AsyncFtpClient GetFtpClient()
{
string host = FtpHelpers.GetFtpHost(Path);
ushort port = FtpHelpers.GetFtpPort(Path);
var credentials = Credentials is not null ?
var credentials = Credentials is not null ?
new NetworkCredential(Credentials.UserName, Credentials.SecurePassword) :
FtpManager.Credentials.Get(host, FtpManager.Anonymous);

Expand Down
33 changes: 22 additions & 11 deletions src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,22 +264,33 @@ public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desi
}, ((IPasswordProtectedItem)this).RetryWithCredentials));
}

public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder)
=> MoveFolderAsync(destinationFolder, NameCollisionOption.FailIfExists);
public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option)
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder)
=> MoveAsync(destinationFolder, NameCollisionOption.FailIfExists);
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option)
{
return AsyncInfo.Run((cancellationToken) => SafetyExtensions.Wrap(async () =>
return AsyncInfo.Run((cancellationToken) => SafetyExtensions.Wrap<BaseStorageFolder>(async () =>
{
BaseStorageFolder destFolder = destinationFolder.AsBaseStorageFolder();
using var ftpClient = GetFtpClient();
if (!await ftpClient.EnsureConnectedAsync())
throw new IOException($"Failed to connect to FTP server.");

string destName = $"{(destFolder as FtpStorageFolder).FtpPath}/{Name}";
FtpRemoteExists ftpRemoteExists = option is NameCollisionOption.ReplaceExisting ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;
bool isSuccessful = await ftpClient.MoveDirectory(FtpPath, destName, ftpRemoteExists, token: cancellationToken);
if (!isSuccessful)
throw new IOException($"Failed to move folder from {Path} to {destFolder}.");

BaseStorageFolder destFolder = destinationFolder.AsBaseStorageFolder();

if (destFolder is FtpStorageFolder ftpFolder)
{
string destName = $"{ftpFolder.FtpPath}/{Name}";
FtpRemoteExists ftpRemoteExists = option is NameCollisionOption.ReplaceExisting ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;

bool isSuccessful = await ftpClient.MoveDirectory(FtpPath, destName, ftpRemoteExists, token: cancellationToken);
if (!isSuccessful)
throw new IOException($"Failed to move folder from {Path} to {destFolder}.");

var folder = new FtpStorageFolder(new StorageFileWithPath(null, destName));
((IPasswordProtectedItem)folder).CopyFrom(this);
return folder;
}
else
throw new NotSupportedException();
}, ((IPasswordProtectedItem)this).RetryWithCredentials));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ public override IAsyncOperation<BaseStorageFile> CreateFileAsync(string desiredN
public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desiredName, CreationCollisionOption options)
=> throw new NotSupportedException();

public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();

public override IAsyncAction RenameAsync(string desiredName) => throw new NotSupportedException();
public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption option) => throw new NotSupportedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desi
public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desiredName, CreationCollisionOption options)
=> AsyncInfo.Run<BaseStorageFolder>(async (cancellationToken) => new SystemStorageFolder(await Folder.CreateFolderAsync(desiredName, options)));

public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();

public override IAsyncAction RenameAsync(string desiredName) => Folder.RenameAsync(desiredName);
public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption option) => Folder.RenameAsync(desiredName, option);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desi
=> CreateFolderAsync(desiredName, CreationCollisionOption.FailIfExists);
public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desiredName, CreationCollisionOption options)
=> throw new NotSupportedException();
public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();

public override IAsyncAction RenameAsync(string desiredName)
=> RenameAsync(desiredName, NameCollisionOption.FailIfExists);
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desi
}, ((IPasswordProtectedItem)this).RetryWithCredentials));
}

public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncAction MoveFolderAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder) => throw new NotSupportedException();
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option) => throw new NotSupportedException();

public override IAsyncAction RenameAsync(string desiredName) => RenameAsync(desiredName, NameCollisionOption.FailIfExists);
public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption option)
Expand Down