feat: add default share duration setting for one-click shares

This commit is contained in:
Yeicor 2023-11-04 19:07:25 +01:00
parent a4cb813ddf
commit 846f72ebbc
14 changed files with 177 additions and 93 deletions

View File

@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
@ -160,6 +161,8 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
Password: pass, Password: pass,
Scope: a.Settings.Defaults.Scope, Scope: a.Settings.Defaults.Scope,
Locale: a.Settings.Defaults.Locale, Locale: a.Settings.Defaults.Locale,
DefaultShareDurationTime: a.Settings.Defaults.DefaultShareDurationTime,
DefaultShareDurationUnit: a.Settings.Defaults.DefaultShareDurationUnit,
ViewMode: a.Settings.Defaults.ViewMode, ViewMode: a.Settings.Defaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick, SingleClick: a.Settings.Defaults.SingleClick,
Sorting: a.Settings.Defaults.Sorting, Sorting: a.Settings.Defaults.Sorting,
@ -216,22 +219,26 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share), Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share),
Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download), Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download),
} }
defaultShareDurationTime, _ := strconv.Atoi(
a.Fields.GetString("user.defaultShareDurationTime", strconv.Itoa(d.DefaultShareDurationTime)))
user := users.User{ user := users.User{
ID: d.ID, ID: d.ID,
Username: d.Username, Username: d.Username,
Password: d.Password, Password: d.Password,
Scope: a.Fields.GetString("user.scope", d.Scope), Scope: a.Fields.GetString("user.scope", d.Scope),
Locale: a.Fields.GetString("user.locale", d.Locale), Locale: a.Fields.GetString("user.locale", d.Locale),
DefaultShareDurationTime: defaultShareDurationTime,
DefaultShareDurationUnit: a.Fields.GetString("user.defaultShareDurationUnit", d.DefaultShareDurationUnit),
LockPassword: true,
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),
Perm: perms,
Commands: a.Fields.GetArray("user.commands", d.Commands),
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),
}, },
Commands: a.Fields.GetArray("user.commands", d.Commands),
HideDotfiles: a.Fields.GetBoolean("user.hideDotfiles", d.HideDotfiles), HideDotfiles: a.Fields.GetBoolean("user.hideDotfiles", d.HideDotfiles),
Perm: perms,
LockPassword: true,
} }
return &user return &user

View File

@ -320,6 +320,8 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
Defaults: settings.UserDefaults{ Defaults: settings.UserDefaults{
Scope: ".", Scope: ".",
Locale: "en", Locale: "en",
DefaultShareDurationTime: -1,
DefaultShareDurationUnit: "hours",
SingleClick: false, SingleClick: false,
Perm: users.Permissions{ Perm: users.Permissions{
Admin: false, Admin: false,

View File

@ -27,14 +27,16 @@ var usersCmd = &cobra.Command{
func printUsers(usrs []*users.User) { func printUsers(usrs []*users.User) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
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\tDefShare\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%d%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.ID,
u.Username, u.Username,
u.Scope, u.Scope,
u.Locale, u.Locale,
u.DefaultShareDurationTime,
u.DefaultShareDurationUnit,
u.ViewMode, u.ViewMode,
u.SingleClick, u.SingleClick,
u.Perm.Admin, u.Perm.Admin,
@ -75,6 +77,8 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.StringSlice("commands", nil, "a list of the commands a user can execute") flags.StringSlice("commands", nil, "a list of the commands a user can execute")
flags.String("scope", ".", "scope for users") flags.String("scope", ".", "scope for users")
flags.String("locale", "en", "locale for users") flags.String("locale", "en", "locale for users")
flags.Int("defaultShareDurationTime", -1, "default share duration time for users (-1 disables it and 0 makes it permanent)")
flags.String("defaultShareDurationUnit", "hours", "default share duration unit 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")
} }
@ -95,6 +99,10 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
defaults.Scope = mustGetString(flags, flag.Name) defaults.Scope = mustGetString(flags, flag.Name)
case "locale": case "locale":
defaults.Locale = mustGetString(flags, flag.Name) defaults.Locale = mustGetString(flags, flag.Name)
case "defaultShareDurationTime":
defaults.DefaultShareDurationTime = mustGetInt(flags, flag.Name)
case "defaultShareDurationUnit":
defaults.DefaultShareDurationUnit = mustGetString(flags, flag.Name)
case "viewMode": case "viewMode":
defaults.ViewMode = getViewMode(flags) defaults.ViewMode = getViewMode(flags)
case "singleClick": case "singleClick":

View File

@ -43,6 +43,8 @@ options you want to change.`,
defaults := settings.UserDefaults{ defaults := settings.UserDefaults{
Scope: user.Scope, Scope: user.Scope,
Locale: user.Locale, Locale: user.Locale,
DefaultShareDurationTime: user.DefaultShareDurationTime,
DefaultShareDurationUnit: user.DefaultShareDurationUnit,
ViewMode: user.ViewMode, ViewMode: user.ViewMode,
SingleClick: user.SingleClick, SingleClick: user.SingleClick,
Perm: user.Perm, Perm: user.Perm,
@ -52,6 +54,8 @@ options you want to change.`,
getUserDefaults(flags, &defaults, false) getUserDefaults(flags, &defaults, false)
user.Scope = defaults.Scope user.Scope = defaults.Scope
user.Locale = defaults.Locale user.Locale = defaults.Locale
user.DefaultShareDurationTime = defaults.DefaultShareDurationTime
user.DefaultShareDurationUnit = defaults.DefaultShareDurationUnit
user.ViewMode = defaults.ViewMode user.ViewMode = defaults.ViewMode
user.SingleClick = defaults.SingleClick user.SingleClick = defaults.SingleClick
user.Perm = defaults.Perm user.Perm = defaults.Perm

View File

@ -43,6 +43,12 @@ func mustGetUint(flags *pflag.FlagSet, flag string) uint {
return b return b
} }
func mustGetInt(flags *pflag.FlagSet, flag string) int {
b, err := flags.GetInt(flag)
checkErr(err)
return b
}
func generateKey() []byte { func generateKey() []byte {
k, err := settings.GenerateKey() k, err := settings.GenerateKey()
checkErr(err) checkErr(err)

View File

@ -24,6 +24,7 @@
</td> </td>
<td class="small"> <td class="small">
<button <button
ref="copyShare"
class="action copy-clipboard" class="action copy-clipboard"
:data-clipboard-text="buildLink(link)" :data-clipboard-text="buildLink(link)"
:aria-label="$t('buttons.copyToClipboard')" :aria-label="$t('buttons.copyToClipboard')"
@ -34,6 +35,7 @@
</td> </td>
<td class="small" v-if="hasDownloadLink()"> <td class="small" v-if="hasDownloadLink()">
<button <button
ref="copyDownload"
class="action copy-clipboard" class="action copy-clipboard"
:data-clipboard-text="buildDownloadLink(link)" :data-clipboard-text="buildDownloadLink(link)"
:aria-label="$t('buttons.copyDownloadLinkToClipboard')" :aria-label="$t('buttons.copyDownloadLinkToClipboard')"
@ -144,7 +146,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["req", "selected", "selectedCount"]), ...mapState(["req", "selected", "selectedCount", "user"]),
...mapGetters(["isListing"]), ...mapGetters(["isListing"]),
url() { url() {
if (!this.isListing) { if (!this.isListing) {
@ -165,8 +167,23 @@ export default {
this.links = links; this.links = links;
this.sort(); this.sort();
if (this.links.length == 0) { if (this.links.length === 0) {
this.listing = false; this.listing = false;
// If enabled and this is the first share, automate the creation of the link and copy it to the clipboard.
if (this.user.defaultShareDurationTime !== -1) {
this.time = this.user.defaultShareDurationTime.toString();
this.unit = this.user.defaultShareDurationUnit;
await this.submit();
this.$nextTick(() => {
console.log(this.$refs)
if (this.$refs.hasOwnProperty("copyDownload") && this.$refs.copyDownload.length > 0) {
this.$refs.copyDownload[0].click();
} else if (this.$refs.hasOwnProperty("copyShare") && this.$refs.copyShare.length > 0) {
this.$refs.copyShare[0].click();
}
});
}
} }
} catch (e) { } catch (e) {
this.$showError(e); this.$showError(e);
@ -183,7 +200,7 @@ export default {
}, },
methods: { methods: {
submit: async function () { submit: async function () {
let isPermanent = !this.time || this.time == 0; let isPermanent = !this.time || this.time === "0";
try { try {
let res = null; let res = null;

View File

@ -298,15 +298,15 @@ body.rtl .card .card-title>*:first-child {
opacity: 1; opacity: 1;
} }
.card#share .input-group { .input-group {
display: flex; display: flex;
} }
.card#share .input-group * { .input-group * {
border: none; border: none;
} }
.card#share .input-group input { .input-group input {
flex: 1; flex: 1;
} }

View File

@ -199,6 +199,8 @@
"createUserHomeDirectory": "Create user home directory", "createUserHomeDirectory": "Create user home directory",
"customStylesheet": "Custom Stylesheet", "customStylesheet": "Custom Stylesheet",
"defaultUserDescription": "These are the default settings for new users.", "defaultUserDescription": "These are the default settings for new users.",
"defaultShareDuration": "Default share duration",
"defaultShareDurationExplanation": "The default share duration to automatically copy direct download links to the clipboard when creating a share. Set to 0 for permanent shares and -1 to disable the feature and choose a duration manually.",
"disableExternalLinks": "Disable external links (except documentation)", "disableExternalLinks": "Disable external links (except documentation)",
"disableUsedDiskPercentage": "Disable used disk percentage graph", "disableUsedDiskPercentage": "Disable used disk percentage graph",
"documentation": "documentation", "documentation": "documentation",

View File

@ -24,6 +24,23 @@
class="input input--block" class="input input--block"
:locale.sync="locale" :locale.sync="locale"
></languages> ></languages>
<h3>{{ $t("settings.defaultShareDuration") }}</h3>
<p>{{ $t("settings.defaultShareDurationExplanation") }}</p>
<div class="input-group input">
<input
v-focus
type="number"
max="2147483647"
min="-1"
v-model.trim="defaultShareDurationTime"
/>
<select class="right" v-model="defaultShareDurationUnit" :aria-label="$t('time.unit')">
<option value="seconds">{{ $t("time.seconds") }}</option>
<option value="minutes">{{ $t("time.minutes") }}</option>
<option value="hours">{{ $t("time.hours") }}</option>
<option value="days">{{ $t("time.days") }}</option>
</select>
</div>
</div> </div>
<div class="card-action"> <div class="card-action">
@ -90,6 +107,8 @@ export default {
singleClick: false, singleClick: false,
dateFormat: false, dateFormat: false,
locale: "", locale: "",
defaultShareDurationTime: -1,
defaultShareDurationUnit: "hours",
}; };
}, },
computed: { computed: {
@ -114,6 +133,8 @@ export default {
this.hideDotfiles = this.user.hideDotfiles; this.hideDotfiles = this.user.hideDotfiles;
this.singleClick = this.user.singleClick; this.singleClick = this.user.singleClick;
this.dateFormat = this.user.dateFormat; this.dateFormat = this.user.dateFormat;
this.defaultShareDurationTime = this.user.defaultShareDurationTime;
this.defaultShareDurationUnit = this.user.defaultShareDurationUnit;
}, },
methods: { methods: {
...mapMutations(["updateUser", "setLoading"]), ...mapMutations(["updateUser", "setLoading"]),
@ -143,6 +164,8 @@ export default {
hideDotfiles: this.hideDotfiles, hideDotfiles: this.hideDotfiles,
singleClick: this.singleClick, singleClick: this.singleClick,
dateFormat: this.dateFormat, dateFormat: this.dateFormat,
defaultShareDurationTime: parseInt(this.defaultShareDurationTime),
defaultShareDurationUnit: this.defaultShareDurationUnit,
}; };
const shouldReload = const shouldReload =
rtlLanguages.includes(data.locale) !== rtlLanguages.includes(data.locale) !==
@ -152,6 +175,8 @@ export default {
"hideDotfiles", "hideDotfiles",
"singleClick", "singleClick",
"dateFormat", "dateFormat",
"defaultShareDurationTime",
"defaultShareDurationUnit",
]); ]);
this.updateUser(data); this.updateUser(data);
if (shouldReload) { if (shouldReload) {

View File

@ -22,6 +22,8 @@ const (
type userInfo struct { type userInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Locale string `json:"locale"` Locale string `json:"locale"`
DefaultShareDurationTime int `json:"defaultShareDurationTime"`
DefaultShareDurationUnit string `json:"defaultShareDurationUnit"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
Perm users.Permissions `json:"perm"` Perm users.Permissions `json:"perm"`
@ -186,6 +188,8 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
User: userInfo{ User: userInfo{
ID: user.ID, ID: user.ID,
Locale: user.Locale, Locale: user.Locale,
DefaultShareDurationTime: user.DefaultShareDurationTime,
DefaultShareDurationUnit: user.DefaultShareDurationUnit,
ViewMode: user.ViewMode, ViewMode: user.ViewMode,
SingleClick: user.SingleClick, SingleClick: user.SingleClick,
Perm: user.Perm, Perm: user.Perm,

View File

@ -10,6 +10,8 @@ import (
type UserDefaults struct { type UserDefaults struct {
Scope string `json:"scope"` Scope string `json:"scope"`
Locale string `json:"locale"` Locale string `json:"locale"`
DefaultShareDurationTime int `json:"defaultShareDurationTime"`
DefaultShareDurationUnit string `json:"defaultShareDurationUnit"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
Sorting files.Sorting `json:"sorting"` Sorting files.Sorting `json:"sorting"`
@ -23,6 +25,8 @@ type UserDefaults struct {
func (d *UserDefaults) Apply(u *users.User) { func (d *UserDefaults) Apply(u *users.User) {
u.Scope = d.Scope u.Scope = d.Scope
u.Locale = d.Locale u.Locale = d.Locale
u.DefaultShareDurationTime = d.DefaultShareDurationTime
u.DefaultShareDurationUnit = d.DefaultShareDurationUnit
u.ViewMode = d.ViewMode u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick u.SingleClick = d.SingleClick
u.Perm = d.Perm u.Perm = d.Perm

View File

@ -125,6 +125,8 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
Commands: cfg.Defaults.Commands, Commands: cfg.Defaults.Commands,
ViewMode: users.ViewMode(cfg.Defaults.ViewMode), ViewMode: users.ViewMode(cfg.Defaults.ViewMode),
Locale: cfg.Defaults.Locale, Locale: cfg.Defaults.Locale,
DefaultShareDurationTime: -1,
DefaultShareDurationUnit: "hours",
Perm: users.Permissions{ Perm: users.Permissions{
Admin: false, Admin: false,
Execute: cfg.Defaults.AllowCommands, Execute: cfg.Defaults.AllowCommands,

View File

@ -3,7 +3,6 @@ package importer
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/asdine/storm/v3" "github.com/asdine/storm/v3"
bolt "go.etcd.io/bbolt" bolt "go.etcd.io/bbolt"
@ -60,6 +59,8 @@ func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
Password: oldUser.Password, Password: oldUser.Password,
Scope: oldUser.Scope, Scope: oldUser.Scope,
Locale: oldUser.Locale, Locale: oldUser.Locale,
DefaultShareDurationTime: -1,
DefaultShareDurationUnit: "hours",
LockPassword: oldUser.LockPassword, LockPassword: oldUser.LockPassword,
ViewMode: users.ViewMode(oldUser.ViewMode), ViewMode: users.ViewMode(oldUser.ViewMode),
Commands: oldUser.Commands, Commands: oldUser.Commands,

View File

@ -26,6 +26,8 @@ type User struct {
Password string `json:"password"` Password string `json:"password"`
Scope string `json:"scope"` Scope string `json:"scope"`
Locale string `json:"locale"` Locale string `json:"locale"`
DefaultShareDurationTime int `json:"defaultShareDurationTime"`
DefaultShareDurationUnit string `json:"defaultShareDurationUnit"`
LockPassword bool `json:"lockPassword"` LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"` ViewMode ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`