feat: user trash dir (#8)

pull/3756/head
Laurynas Gadliauskas 2021-06-04 11:44:04 +03:00 committed by GitHub
parent 8fe3f85d18
commit 1b79b0c166
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 87 additions and 9 deletions

View File

@ -27,13 +27,14 @@ var usersCmd = &cobra.Command{
func printUsers(usrs []*users.User) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
fmt.Fprintln(w, "ID\tUsername\tScope\tTrash Dir\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
for _, u := range usrs {
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
u.ID,
u.Username,
u.Scope,
u.TrashDir,
u.Locale,
u.ViewMode,
u.SingleClick,
@ -74,6 +75,7 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.Bool("lockPassword", false, "lock password")
flags.StringSlice("commands", nil, "a list of the commands a user can execute")
flags.String("scope", ".", "scope for users")
flags.String("trashDir", "", "trash directory path for users")
flags.String("locale", "en", "locale for users")
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
flags.Bool("singleClick", false, "use single clicks only")
@ -93,6 +95,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
switch flag.Name {
case "scope":
defaults.Scope = mustGetString(flags, flag.Name)
case "trashDir":
defaults.TrashDir = mustGetString(flags, flag.Name)
case "locale":
defaults.Locale = mustGetString(flags, flag.Name)
case "viewMode":

View File

@ -42,6 +42,7 @@ options you want to change.`,
defaults := settings.UserDefaults{
Scope: user.Scope,
TrashDir: user.TrashDir,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
@ -51,6 +52,7 @@ options you want to change.`,
}
getUserDefaults(flags, &defaults, false)
user.Scope = defaults.Scope
user.TrashDir = defaults.TrashDir
user.Locale = defaults.Locale
user.ViewMode = defaults.ViewMode
user.SingleClick = defaults.SingleClick

View File

@ -49,8 +49,8 @@ async function resourceAction(url, method, content) {
}
}
export async function remove(url) {
return resourceAction(url, "DELETE");
export async function remove(url, skipTrash = true) {
return resourceAction(`${url}?skip_trash=${skipTrash}`, "DELETE");
}
export async function put(url, content = "") {

View File

@ -33,6 +33,18 @@
</button>
</div>
<div v-if="trashDir != ''">
<router-link
class="action"
:to="'/files/' + trashDir"
:aria-label="$t('sidebar.trashBin')"
:title="$t('sidebar.trashBin')"
>
<i class="material-icons">delete</i>
<span>{{ $t("sidebar.trashBin") }}</span>
</router-link>
</div>
<div>
<router-link
class="action"
@ -109,6 +121,7 @@ import {
version,
signup,
disableExternal,
trashDir,
noAuth,
authMethod,
authLogoutURL,
@ -125,6 +138,7 @@ export default {
signup: () => signup,
version: () => version,
disableExternal: () => disableExternal,
trashDir: () => trashDir,
noAuth: () => noAuth,
authMethod: () => authMethod,
authLogoutURL: () => authLogoutURL,

View File

@ -7,6 +7,10 @@
<p v-else>
{{ $t("prompts.deleteMessageMultiple", { count: selectedCount }) }}
</p>
<p v-if="trashBinCheckbox">
<input type="checkbox" v-model="skipTrash" />
{{ $t("prompts.skipTrashMessage") }}
</p>
</div>
<div class="card-action">
<button
@ -33,12 +37,36 @@
import { mapGetters, mapMutations, mapState } from "vuex";
import { files as api } from "@/api";
import buttons from "@/utils/buttons";
import { trashDir } from "@/utils/constants";
export default {
name: "delete",
data: function () {
return {
skipTrash: true,
};
},
computed: {
...mapGetters(["isListing", "selectedCount"]),
...mapState(["req", "selected", "showConfirm"]),
trashBinCheckbox() {
if (trashDir === "") {
return false;
}
if (this.req.path.startsWith(`/${trashDir}/`)) {
return false;
}
if (
this.selectedCount == 1 &&
this.req.items[this.selected].name == trashDir
) {
return false;
}
return true;
},
},
methods: {
...mapMutations(["closeHovers"]),
@ -47,7 +75,7 @@ export default {
try {
if (!this.isListing) {
await api.remove(this.$route.path);
await api.remove(this.$route.path, this.skipTrash);
buttons.success("delete");
this.showConfirm();
@ -63,7 +91,7 @@ export default {
let promises = [];
for (let index of this.selected) {
promises.push(api.remove(this.req.items[index].url));
promises.push(api.remove(this.req.items[index].url, this.skipTrash));
}
await Promise.all(promises);

View File

@ -138,6 +138,7 @@
"deleteMessageMultiple": "Are you sure you want to delete {count} file(s)?",
"deleteMessageSingle": "Are you sure you want to delete this file/folder?",
"deleteMessageShare": "Are you sure you want to delete this share({path})?",
"skipTrashMessage": "Skip trash bin and delete immediately",
"deleteTitle": "Delete files",
"displayName": "Display Name:",
"download": "Download files",
@ -262,6 +263,7 @@
"myFiles": "My files",
"newFile": "New file",
"newFolder": "New folder",
"trashBin": "Trash bin",
"preview": "Preview",
"settings": "Settings",
"signup": "Signup",

View File

@ -7,6 +7,7 @@ const recaptchaKey = window.FileBrowser.ReCaptchaKey;
const signup = window.FileBrowser.Signup;
const version = window.FileBrowser.Version;
const logoURL = `${staticURL}/img/logo.svg`;
const trashDir = window.FileBrowser.TrashDir;
const noAuth = window.FileBrowser.NoAuth;
const authMethod = window.FileBrowser.AuthMethod;
const authLogoutURL = window.FileBrowser.AuthLogoutURL;
@ -25,6 +26,7 @@ export {
recaptchaKey,
signup,
version,
trashDir,
noAuth,
authMethod,
authLogoutURL,

View File

@ -95,9 +95,31 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc {
return errToStatus(err), err
}
err = d.RunHook(func() error {
return d.user.Fs.RemoveAll(r.URL.Path)
}, "delete", r.URL.Path, "", d.user)
skipTrash := r.URL.Query().Get("skip_trash") == "true"
if d.user.TrashDir == "" || skipTrash {
err = d.RunHook(func() error {
return d.user.Fs.RemoveAll(r.URL.Path)
}, "delete", r.URL.Path, "", d.user)
} else {
src := r.URL.Path
dst := d.user.TrashDir
if !d.Check(src) || !d.Check(dst) {
return http.StatusForbidden, nil
}
src = path.Clean("/" + src)
dst = path.Clean("/" + dst)
err = d.user.Fs.MkdirAll(dst, 0755)
if err != nil {
return errToStatus(err), err
}
dst = path.Join(dst, file.Name)
err = fileutils.MoveFile(d.user.Fs, src, dst)
}
if err != nil {
return errToStatus(err), err

View File

@ -33,6 +33,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
"Version": version.Version,
"StaticURL": path.Join(d.server.BaseURL, "/static"),
"Signup": d.settings.Signup,
"TrashDir": d.settings.Defaults.TrashDir,
"NoAuth": d.settings.AuthMethod == auth.MethodNoAuth,
"AuthMethod": d.settings.AuthMethod,
"AuthLogoutURL": d.settings.AuthLogoutURL,

View File

@ -9,6 +9,7 @@ import (
// for some fields on User.
type UserDefaults struct {
Scope string `json:"scope"`
TrashDir string `json:"trashDir"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
@ -21,6 +22,7 @@ type UserDefaults struct {
// Apply applies the default options to a user.
func (d *UserDefaults) Apply(u *users.User) {
u.Scope = d.Scope
u.TrashDir = d.TrashDir
u.Locale = d.Locale
u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick

View File

@ -25,6 +25,7 @@ type User struct {
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
TrashDir string `json:"trashDir"`
Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"`