feat: restore files from trash
This commit is contained in:
@@ -987,6 +987,49 @@ func (a *App) ListVaultTrash(pluginID string) ([]corefiles.TrashEntry, string) {
|
||||
return entries, ""
|
||||
}
|
||||
|
||||
// RestoreVaultTrash restores a file or folder from internal trash for a plugin with files.delete and files.write.
|
||||
func (a *App) RestoreVaultTrash(pluginID, trashID string, options corefiles.RestoreOptions) (string, string) {
|
||||
if _, err := a.requirePluginAccess(pluginID, "files.delete"); err != nil {
|
||||
return "", err.Error()
|
||||
}
|
||||
if _, err := a.requirePluginAccess(pluginID, "files.write"); err != nil {
|
||||
return "", err.Error()
|
||||
}
|
||||
if a.files == nil {
|
||||
return "", "files service not initialized"
|
||||
}
|
||||
entries, err := a.files.ListTrashEntries()
|
||||
if err != nil {
|
||||
return "", err.Error()
|
||||
}
|
||||
var entry corefiles.TrashEntry
|
||||
for _, candidate := range entries {
|
||||
if candidate.TrashID == trashID {
|
||||
entry = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
if entry.TrashID == "" {
|
||||
return "", "not-found: trash entry " + trashID
|
||||
}
|
||||
restoredPath, err := a.files.RestoreTrashEntry(trashID, options)
|
||||
if err != nil {
|
||||
return "", err.Error()
|
||||
}
|
||||
if err := a.recordFileSyncOp(syncEntityTypeForFileType(entry.OriginalType), restoredPath, syncsvc.OpCreate, map[string]string{
|
||||
"path": restoredPath,
|
||||
}); err != nil {
|
||||
return "", err.Error()
|
||||
}
|
||||
a.publishFileActivity("file.changed", pluginID, restoredPath, map[string]interface{}{
|
||||
"operation": syncsvc.OpCreate,
|
||||
"type": string(entry.OriginalType),
|
||||
"restored": true,
|
||||
"trashId": trashID,
|
||||
})
|
||||
return restoredPath, ""
|
||||
}
|
||||
|
||||
// OpenVaultPathExternal opens a vault-relative file or folder in the OS default app.
|
||||
func (a *App) OpenVaultPathExternal(pluginID, relativePath string) string {
|
||||
if _, err := a.requirePluginAccess(pluginID, "files.openExternal"); err != nil {
|
||||
|
||||
@@ -395,6 +395,17 @@ func TestFilesBridgeReadWriteListMoveTrash(t *testing.T) {
|
||||
if len(trashEntries) != 1 || trashEntries[0].OriginalPath != "Docs/two.txt" || trashEntries[0].TrashID != trash.TrashID {
|
||||
t.Fatalf("trash entries = %+v, want Docs/two.txt", trashEntries)
|
||||
}
|
||||
|
||||
restored, errStr := app.RestoreVaultTrash("files.plugin", trash.TrashID, corefiles.RestoreOptions{})
|
||||
if errStr != "" {
|
||||
t.Fatalf("RestoreVaultTrash: %s", errStr)
|
||||
}
|
||||
if restored != "Docs/two.txt" {
|
||||
t.Fatalf("restored path = %q, want Docs/two.txt", restored)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "Docs", "two.txt")); err != nil {
|
||||
t.Fatalf("restored file missing: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilesBridgeWritePublishesFileChangedActivityEvent(t *testing.T) {
|
||||
@@ -610,6 +621,24 @@ func TestFilesBridgePermissions(t *testing.T) {
|
||||
call: func(app *App) string { _, errStr := app.TrashVaultPath("files.plugin", "one.txt"); return errStr },
|
||||
wantPhrase: "files.delete",
|
||||
},
|
||||
{
|
||||
name: "restore requires delete",
|
||||
perms: []string{"files.read", "files.write"},
|
||||
call: func(app *App) string {
|
||||
_, errStr := app.RestoreVaultTrash("files.plugin", "missing", corefiles.RestoreOptions{})
|
||||
return errStr
|
||||
},
|
||||
wantPhrase: "files.delete",
|
||||
},
|
||||
{
|
||||
name: "restore requires write",
|
||||
perms: []string{"files.read", "files.delete"},
|
||||
call: func(app *App) string {
|
||||
_, errStr := app.RestoreVaultTrash("files.plugin", "missing", corefiles.RestoreOptions{})
|
||||
return errStr
|
||||
},
|
||||
wantPhrase: "files.write",
|
||||
},
|
||||
{
|
||||
name: "open external requires openExternal",
|
||||
perms: []string{"files.read", "files.write", "files.delete"},
|
||||
|
||||
Reference in New Issue
Block a user