feat: add "redirect after copy/move" user setting (#5662)

This commit is contained in:
Beckam White
2026-01-10 20:27:54 +11:00
committed by GitHub
parent 208535a8cc
commit fda8a99292
18 changed files with 119 additions and 80 deletions

View File

@@ -164,6 +164,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
Locale: a.Settings.Defaults.Locale, Locale: a.Settings.Defaults.Locale,
ViewMode: a.Settings.Defaults.ViewMode, ViewMode: a.Settings.Defaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick, SingleClick: a.Settings.Defaults.SingleClick,
RedirectAfterCopyMove: a.Settings.Defaults.RedirectAfterCopyMove,
Sorting: a.Settings.Defaults.Sorting, Sorting: a.Settings.Defaults.Sorting,
Perm: a.Settings.Defaults.Perm, Perm: a.Settings.Defaults.Perm,
Commands: a.Settings.Defaults.Commands, Commands: a.Settings.Defaults.Commands,
@@ -226,6 +227,7 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
Locale: a.Fields.GetString("user.locale", d.Locale), Locale: a.Fields.GetString("user.locale", d.Locale),
ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))), ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))),
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick), SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
RedirectAfterCopyMove: a.Fields.GetBoolean("user.redirectAfterCopyMove", d.RedirectAfterCopyMove),
Sorting: files.Sorting{ Sorting: files.Sorting{
Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc), Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc),
By: a.Fields.GetString("user.sorting.by", d.Sorting.By), By: a.Fields.GetString("user.sorting.by", d.Sorting.By),
@@ -251,6 +253,7 @@ var validHookFields = []string{
"user.locale", "user.locale",
"user.viewMode", "user.viewMode",
"user.singleClick", "user.singleClick",
"user.redirectAfterCopyMove",
"user.sorting.by", "user.sorting.by",
"user.sorting.asc", "user.sorting.asc",
"user.commands", "user.commands",

View File

@@ -240,6 +240,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale) fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode) fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick) fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick)
fmt.Fprintf(w, "\tRedirect after Copy/Move:\t%t\n", set.Defaults.RedirectAfterCopyMove)
fmt.Fprintf(w, "\tFile Creation Mode:\t%O\n", set.FileMode) fmt.Fprintf(w, "\tFile Creation Mode:\t%O\n", set.FileMode)
fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode) fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " ")) fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))

View File

@@ -396,6 +396,7 @@ func quickSetup(v *viper.Viper, s *storage.Storage) error {
Scope: ".", Scope: ".",
Locale: "en", Locale: "en",
SingleClick: false, SingleClick: false,
RedirectAfterCopyMove: true,
AceEditorTheme: v.GetString("defaults.aceEditorTheme"), AceEditorTheme: v.GetString("defaults.aceEditorTheme"),
Perm: users.Permissions{ Perm: users.Permissions{
Admin: false, Admin: false,

View File

@@ -30,13 +30,14 @@ func printUsers(usrs []*users.User) {
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\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
for _, u := range usrs { 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%t\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.ID,
u.Username, u.Username,
u.Scope, u.Scope,
u.Locale, u.Locale,
u.ViewMode, u.ViewMode,
u.SingleClick, u.SingleClick,
u.RedirectAfterCopyMove,
u.Perm.Admin, u.Perm.Admin,
u.Perm.Execute, u.Perm.Execute,
u.Perm.Create, u.Perm.Create,
@@ -77,6 +78,7 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.String("locale", "en", "locale for users") flags.String("locale", "en", "locale for users")
flags.String("viewMode", string(users.ListViewMode), "view mode for users") flags.String("viewMode", string(users.ListViewMode), "view mode for users")
flags.Bool("singleClick", false, "use single clicks only") flags.Bool("singleClick", false, "use single clicks only")
flags.Bool("redirectAfterCopyMove", false, "redirect to destination after copy/move")
flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)") flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)")
flags.Bool("hideDotfiles", false, "hide dotfiles") flags.Bool("hideDotfiles", false, "hide dotfiles")
flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users") flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users")
@@ -110,6 +112,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
defaults.ViewMode, err = getAndParseViewMode(flags) defaults.ViewMode, err = getAndParseViewMode(flags)
case "singleClick": case "singleClick":
defaults.SingleClick, err = flags.GetBool(flag.Name) defaults.SingleClick, err = flags.GetBool(flag.Name)
case "redirectAfterCopyMove":
defaults.RedirectAfterCopyMove, err = flags.GetBool(flag.Name)
case "aceEditorTheme": case "aceEditorTheme":
defaults.AceEditorTheme, err = flags.GetString(flag.Name) defaults.AceEditorTheme, err = flags.GetString(flag.Name)
case "perm.admin": case "perm.admin":

View File

@@ -56,6 +56,7 @@ options you want to change.`,
Locale: user.Locale, Locale: user.Locale,
ViewMode: user.ViewMode, ViewMode: user.ViewMode,
SingleClick: user.SingleClick, SingleClick: user.SingleClick,
RedirectAfterCopyMove: user.RedirectAfterCopyMove,
Perm: user.Perm, Perm: user.Perm,
Sorting: user.Sorting, Sorting: user.Sorting,
Commands: user.Commands, Commands: user.Commands,
@@ -70,6 +71,7 @@ options you want to change.`,
user.Locale = defaults.Locale user.Locale = defaults.Locale
user.ViewMode = defaults.ViewMode user.ViewMode = defaults.ViewMode
user.SingleClick = defaults.SingleClick user.SingleClick = defaults.SingleClick
user.RedirectAfterCopyMove = defaults.RedirectAfterCopyMove
user.Perm = defaults.Perm user.Perm = defaults.Perm
user.Commands = defaults.Commands user.Commands = defaults.Commands
user.Sorting = defaults.Sorting user.Sorting = defaults.Sorting

View File

@@ -109,6 +109,7 @@ export default {
return; return;
} }
if (this.user.redirectAfterCopyMove)
this.$router.push({ path: this.dest }); this.$router.push({ path: this.dest });
}) })
.catch((e) => { .catch((e) => {

View File

@@ -79,7 +79,7 @@ export default {
computed: { computed: {
...mapState(useFileStore, ["req", "selected"]), ...mapState(useFileStore, ["req", "selected"]),
...mapState(useAuthStore, ["user"]), ...mapState(useAuthStore, ["user"]),
...mapWritableState(useFileStore, ["preselect"]), ...mapWritableState(useFileStore, ["reload", "preselect"]),
excludedFolders() { excludedFolders() {
return this.selected return this.selected
.filter((idx) => this.req.items[idx].isDir) .filter((idx) => this.req.items[idx].isDir)
@@ -108,7 +108,9 @@ export default {
.then(() => { .then(() => {
buttons.success("move"); buttons.success("move");
this.preselect = removePrefix(items[0].to); this.preselect = removePrefix(items[0].to);
if (this.user.redirectAfterCopyMove)
this.$router.push({ path: this.dest }); this.$router.push({ path: this.dest });
else this.reload = true;
}) })
.catch((e) => { .catch((e) => {
buttons.done("move"); buttons.done("move");

View File

@@ -232,6 +232,7 @@
"permissions": "Permissions", "permissions": "Permissions",
"permissionsHelp": "You can set the user to be an administrator or choose the permissions individually. If you select \"Administrator\", all of the other options will be automatically checked. The management of users remains a privilege of an administrator.\n", "permissionsHelp": "You can set the user to be an administrator or choose the permissions individually. If you select \"Administrator\", all of the other options will be automatically checked. The management of users remains a privilege of an administrator.\n",
"profileSettings": "Profile Settings", "profileSettings": "Profile Settings",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "prevents the access to any dotfile (such as .git, .gitignore) in every folder.\n", "ruleExample1": "prevents the access to any dotfile (such as .git, .gitignore) in every folder.\n",
"ruleExample2": "blocks the access to the file named Caddyfile on the root of the scope.", "ruleExample2": "blocks the access to the file named Caddyfile on the root of the scope.",
"rules": "Rules", "rules": "Rules",

View File

@@ -18,6 +18,7 @@ interface SettingsDefaults {
locale: string; locale: string;
viewMode: ViewModeType; viewMode: ViewModeType;
singleClick: boolean; singleClick: boolean;
redirectAfterCopyMove: boolean;
sorting: Sorting; sorting: Sorting;
perm: Permissions; perm: Permissions;
commands: any[]; commands: any[];

View File

@@ -10,6 +10,7 @@ interface IUser {
lockPassword: boolean; lockPassword: boolean;
hideDotfiles: boolean; hideDotfiles: boolean;
singleClick: boolean; singleClick: boolean;
redirectAfterCopyMove: boolean;
dateFormat: boolean; dateFormat: boolean;
viewMode: ViewModeType; viewMode: ViewModeType;
sorting?: Sorting; sorting?: Sorting;
@@ -30,6 +31,7 @@ interface IUserForm {
lockPassword?: boolean; lockPassword?: boolean;
hideDotfiles?: boolean; hideDotfiles?: boolean;
singleClick?: boolean; singleClick?: boolean;
redirectAfterCopyMove?: boolean;
dateFormat?: boolean; dateFormat?: boolean;
} }

View File

@@ -15,6 +15,14 @@
<input type="checkbox" name="singleClick" v-model="singleClick" /> <input type="checkbox" name="singleClick" v-model="singleClick" />
{{ t("settings.singleClick") }} {{ t("settings.singleClick") }}
</p> </p>
<p>
<input
type="checkbox"
name="redirectAfterCopyMove"
v-model="redirectAfterCopyMove"
/>
{{ t("settings.redirectAfterCopyMove") }}
</p>
<p> <p>
<input type="checkbox" name="dateFormat" v-model="dateFormat" /> <input type="checkbox" name="dateFormat" v-model="dateFormat" />
{{ t("settings.setDateFormat") }} {{ t("settings.setDateFormat") }}
@@ -116,6 +124,7 @@ const currentPassword = ref<string>("");
const isCurrentPasswordRequired = ref<boolean>(false); const isCurrentPasswordRequired = ref<boolean>(false);
const hideDotfiles = ref<boolean>(false); const hideDotfiles = ref<boolean>(false);
const singleClick = ref<boolean>(false); const singleClick = ref<boolean>(false);
const redirectAfterCopyMove = ref<boolean>(false);
const dateFormat = ref<boolean>(false); const dateFormat = ref<boolean>(false);
const locale = ref<string>(""); const locale = ref<string>("");
const aceEditorTheme = ref<string>(""); const aceEditorTheme = ref<string>("");
@@ -140,6 +149,7 @@ onMounted(async () => {
locale.value = authStore.user.locale; locale.value = authStore.user.locale;
hideDotfiles.value = authStore.user.hideDotfiles; hideDotfiles.value = authStore.user.hideDotfiles;
singleClick.value = authStore.user.singleClick; singleClick.value = authStore.user.singleClick;
redirectAfterCopyMove.value = authStore.user.redirectAfterCopyMove;
dateFormat.value = authStore.user.dateFormat; dateFormat.value = authStore.user.dateFormat;
aceEditorTheme.value = authStore.user.aceEditorTheme; aceEditorTheme.value = authStore.user.aceEditorTheme;
layoutStore.loading = false; layoutStore.loading = false;
@@ -187,6 +197,7 @@ const updateSettings = async (event: Event) => {
locale: locale.value, locale: locale.value,
hideDotfiles: hideDotfiles.value, hideDotfiles: hideDotfiles.value,
singleClick: singleClick.value, singleClick: singleClick.value,
redirectAfterCopyMove: redirectAfterCopyMove.value,
dateFormat: dateFormat.value, dateFormat: dateFormat.value,
aceEditorTheme: aceEditorTheme.value, aceEditorTheme: aceEditorTheme.value,
}; };
@@ -195,6 +206,7 @@ const updateSettings = async (event: Event) => {
"locale", "locale",
"hideDotfiles", "hideDotfiles",
"singleClick", "singleClick",
"redirectAfterCopyMove",
"dateFormat", "dateFormat",
"aceEditorTheme", "aceEditorTheme",
]); ]);

View File

@@ -27,6 +27,7 @@ type userInfo struct {
Locale string `json:"locale"` Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"`
Perm users.Permissions `json:"perm"` Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`
LockPassword bool `json:"lockPassword"` LockPassword bool `json:"lockPassword"`
@@ -208,6 +209,7 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
Locale: user.Locale, Locale: user.Locale,
ViewMode: user.ViewMode, ViewMode: user.ViewMode,
SingleClick: user.SingleClick, SingleClick: user.SingleClick,
RedirectAfterCopyMove: user.RedirectAfterCopyMove,
Perm: user.Perm, Perm: user.Perm,
LockPassword: user.LockPassword, LockPassword: user.LockPassword,
Commands: user.Commands, Commands: user.Commands,

View File

@@ -12,6 +12,7 @@ type UserDefaults struct {
Locale string `json:"locale"` Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"`
Sorting files.Sorting `json:"sorting"` Sorting files.Sorting `json:"sorting"`
Perm users.Permissions `json:"perm"` Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`
@@ -26,6 +27,7 @@ func (d *UserDefaults) Apply(u *users.User) {
u.Locale = d.Locale u.Locale = d.Locale
u.ViewMode = d.ViewMode u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick u.SingleClick = d.SingleClick
u.RedirectAfterCopyMove = d.RedirectAfterCopyMove
u.Perm = d.Perm u.Perm = d.Perm
u.Sorting = d.Sorting u.Sorting = d.Sorting
u.Commands = d.Commands u.Commands = d.Commands

View File

@@ -28,6 +28,7 @@ type User struct {
LockPassword bool `json:"lockPassword"` LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"` ViewMode ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"`
Perm Permissions `json:"perm"` Perm Permissions `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`
Sorting files.Sorting `json:"sorting"` Sorting files.Sorting `json:"sorting"`

View File

@@ -61,6 +61,7 @@ filebrowser config init [flags]
--recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com") --recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com")
--recaptcha.key string ReCaptcha site key --recaptcha.key string ReCaptcha site key
--recaptcha.secret string ReCaptcha secret --recaptcha.secret string ReCaptcha secret
--redirectAfterCopyMove redirect to destination after copy/move
-r, --root string root to prepend to relative paths (default ".") -r, --root string root to prepend to relative paths (default ".")
--scope string scope for users (default ".") --scope string scope for users (default ".")
--shell string shell command to which other commands should be appended --shell string shell command to which other commands should be appended

View File

@@ -58,6 +58,7 @@ filebrowser config set [flags]
--recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com") --recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com")
--recaptcha.key string ReCaptcha site key --recaptcha.key string ReCaptcha site key
--recaptcha.secret string ReCaptcha secret --recaptcha.secret string ReCaptcha secret
--redirectAfterCopyMove redirect to destination after copy/move
-r, --root string root to prepend to relative paths (default ".") -r, --root string root to prepend to relative paths (default ".")
--scope string scope for users (default ".") --scope string scope for users (default ".")
--shell string shell command to which other commands should be appended --shell string shell command to which other commands should be appended

View File

@@ -28,6 +28,7 @@ filebrowser users add <username> <password> [flags]
--perm.modify modify perm for users (default true) --perm.modify modify perm for users (default true)
--perm.rename rename perm for users (default true) --perm.rename rename perm for users (default true)
--perm.share share perm for users (default true) --perm.share share perm for users (default true)
--redirectAfterCopyMove redirect to destination after copy/move
--scope string scope for users (default ".") --scope string scope for users (default ".")
--singleClick use single clicks only --singleClick use single clicks only
--sorting.asc sorting by ascending order --sorting.asc sorting by ascending order

View File

@@ -30,6 +30,7 @@ filebrowser users update <id|username> [flags]
--perm.modify modify perm for users (default true) --perm.modify modify perm for users (default true)
--perm.rename rename perm for users (default true) --perm.rename rename perm for users (default true)
--perm.share share perm for users (default true) --perm.share share perm for users (default true)
--redirectAfterCopyMove redirect to destination after copy/move
--scope string scope for users (default ".") --scope string scope for users (default ".")
--singleClick use single clicks only --singleClick use single clicks only
--sorting.asc sorting by ascending order --sorting.asc sorting by ascending order