Details
-
User Story
-
Resolution: Done
-
P2: Important
-
5.5.1
-
None
-
74a2467edd1bf0800cbaf1878984c6bccd42570d
Description
A cross platform API that allows applications to move one file or directory to the trash.
Must:
- secure, ie files that are moved to the trash are accessible with the same permissions as the original file
- returns whether the operation was successful
- does not delete the file permanently if it cannot be moved to the trash
- follow platform specific conventions: file should show up in the respective users' trash can, and the user needs to be able to restore it from there (at least manually)
- synchronous API call; API returns when the transaction is complete
- atomic operation (as in, the file isn't half moved to the trash in case of moving being done via copy/delete)
- must work for console applications without event loop or other GUI context
Should:
- return the location of the file/directory in the trash
Could:
- Integration with the platform's file explorer to allow moving the file back
Won't:
- Do not play any sound
- Do not copy files from external storage to system or home partition
- Does not require any user interaction or display and other kind of UI (such as progress dialogs)
API proposal
QString QFile::moveToTrash() const;
static QString QFile::moveToTrash(const QString&);
Moves the provided file to the trash, returns the location of the file in the trash, or a null-string if the file has not been moved. The function can return an empty string if the file was moved to the trash, but the new location of the file is not known.
Depending on the behaviour of the platform, this function may delete the file without moving it to the trash. However, Qt tries to avoid this.
Alternatives would be
bool QFile::moveToTrash(QString *trashedLocation);
Returns whether the file was moved to the trash; trashedLocation might contain the new path to the file in the trash
or simply
bool QFile::moveToTrash();
If successful, QFile::fileName() returns the new location, or the empty string if the file was moved, but the new location is not known.
Implementation details
After prototyping, the following implementations are verified:
- on Mac, we can use NSFileManager::trashItemAtURL; this moves the file to the volume-specific trash, without any need for an event loop, and returns the path to the file in the trash. The only missing functionality is the integration with Finder's "Put Back" option, as this API does not populate the .DS_Store file in the trash with the respective meta-data.
An alternative implementation sending Apple Event to the Finder app enables the "Put Back" option, but getting the new filename is tricky, it's comparatively heavy, it plays the trashing sound, and it prompts the user to allow "qtapp to access the Finder app". The latter is not acceptable. Given all that, NSFileManager provides the better tradeoff. - on Windows, SHFileOperation (FO_DELETE with FOF_ALLOWUNDO) is available, but deprecated, and it doesn't return the location of the file in the trash, which makes it a bad choice. In that case, we also have no way to prevent the deletion of the file if it is not moved to the trash.
A better alternative uses IFileManager, which via a IFileOperationProgressSink allows us to learn about the location of the file after the trashing, and also allows us to cancel the operation if the file would be deleted (ie because it resides on a network share or otherwise volume that doesn't support recycling). The only drawback is that this requires an up-to-date SDK; building with MinGW has been problematic. - On Linux, implementing https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html is rather straight forward, but might result in a file being copied from one file system to another if the only possible trash location is ~/.Trash. The requirement of asserting that a trash in the top-dir has the sticky bit set "if the file system supports the sticky bit" requires, in theory, that we detect whether the file system supports the sticky bit at all before checking for it. We might naively skip this, and abort if ~/.Trash is the only option, and if the file is on a different file system.
Testing
Must:
- Create a file in the home directory and move it to the trash. Verify that we get a file name, that the file name exists, and that the file contents are identical
- Create a directory in the home directory, move it to the trash. Verify that we get the location in the trash, and that the directory in the trash has the same contents
- Create a file in the temp directory; move it to the trash, verify as before
- Attempt to move a file to the trash that doesn't exist, verify return value is the null string
- Attempt to move a file that the user doesn't have write access to, verify return value is the null string
- Attempt to move a system file to the trash, verify return value is the null string
Should:
These test cases might not be possible to automate reliably (either because the infrastructure setup is complex, or because the underlying system behavior is too opaque).
- Create a file on a mounted file system, move it to the trash
- Create a file on an external drive (f.ex USB stick), move it to the trash, verify that the file in the trash still lives on the same file system
Attachments
Issue Links
- relates to
-
QTBUG-5874 Add a progress callback to QFile::copy and QFile::rename
- Open
-
QTBUG-181 Add function to put file(s), dir(s) into the recycle bin
- Closed
Gerrit Reviews
For Gerrit Dashboard: QTBUG-47703 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
279855,16 | Implement moving of a single file system entry to the trash | 5.15 | qt/qtbase | Status: MERGED | +2 | 0 |
287241,1 | Introduce QFileSystemOperations for simple file system operations | 5.15 | qt/qtbase | Status: ABANDONED | -1 | 0 |
287373,17 | Add QFile::moveToTrash, which moves a file to the trash | 5.15 | qt/qtbase | Status: MERGED | +2 | 0 |