update settings and views (#41)
Co-authored-by: Graham Steffaniak <graham.steffaniak@autodesk.com>
This commit is contained in:
parent
5506135d32
commit
a33eab2a8c
|
@ -146,14 +146,11 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
|
|||
}
|
||||
|
||||
if u == nil {
|
||||
pass, err := users.HashPwd(a.Cred.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println("creds", a.Cred.Password)
|
||||
// create user with the provided credentials
|
||||
d := &users.User{
|
||||
Username: a.Cred.Username,
|
||||
Password: pass,
|
||||
Password: a.Cred.Password,
|
||||
Scope: a.Settings.UserDefaults.Scope,
|
||||
Locale: a.Settings.UserDefaults.Locale,
|
||||
ViewMode: a.Settings.UserDefaults.ViewMode,
|
||||
|
@ -178,16 +175,6 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
|
|||
}
|
||||
} else if p := !users.CheckPwd(a.Cred.Password, u.Password); len(a.Fields.Values) > 1 || p {
|
||||
u = a.GetUser(u)
|
||||
|
||||
// update the password when it doesn't match the current
|
||||
if p {
|
||||
pass, err := users.HashPwd(a.Cred.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Password = pass
|
||||
}
|
||||
|
||||
// update user with provided fields
|
||||
err := a.Users.Update(u)
|
||||
if err != nil {
|
||||
|
|
|
@ -30,7 +30,6 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
|
|||
if r.Body == nil {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&cred)
|
||||
if err != nil {
|
||||
return nil, os.ErrPermission
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
|
||||
== Running benchmark ==
|
||||
? github.com/gtsteffaniak/filebrowser [no test files]
|
||||
? github.com/gtsteffaniak/filebrowser/auth [no test files]
|
||||
? github.com/gtsteffaniak/filebrowser/cmd [no test files]
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/diskcache 1.318s
|
||||
? github.com/gtsteffaniak/filebrowser/errors [no test files]
|
||||
? github.com/gtsteffaniak/filebrowser/files [no test files]
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/fileutils 1.176s
|
||||
2023/09/29 17:01:53 h: 401 <nil>
|
||||
2023/09/29 17:01:53 h: 401 <nil>
|
||||
2023/09/29 17:01:53 h: 401 <nil>
|
||||
2023/09/29 17:01:53 h: 401 <nil>
|
||||
2023/09/29 17:01:53 h: 401 <nil>
|
||||
2023/09/29 17:01:53 h: 401 <nil>
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/http 1.055s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/img 0.771s
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/gtsteffaniak/filebrowser/index
|
||||
BenchmarkFillIndex-10 10 6355542 ns/op 12084 B/op 449 allocs/op
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/index 0.653s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/rules 0.549s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/runner 0.661s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/settings 0.665s
|
||||
? github.com/gtsteffaniak/filebrowser/share [no test files]
|
||||
? github.com/gtsteffaniak/filebrowser/storage [no test files]
|
||||
? github.com/gtsteffaniak/filebrowser/storage/bolt [no test files]
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/users 0.654s
|
||||
? github.com/gtsteffaniak/filebrowser/version [no test files]
|
|
@ -1,25 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(hashCmd)
|
||||
}
|
||||
|
||||
var hashCmd = &cobra.Command{
|
||||
Use: "hash <password>",
|
||||
Short: "Hashes a password",
|
||||
Long: `Hashes a password using bcrypt algorithm.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
pwd, err := users.HashPwd(args[0])
|
||||
checkErr(err)
|
||||
fmt.Println(pwd)
|
||||
},
|
||||
}
|
|
@ -137,12 +137,23 @@ func quickSetup(d pythonData) {
|
|||
log.Fatal("username and password cannot be empty during quick setup")
|
||||
}
|
||||
user := &users.User{
|
||||
Username: username,
|
||||
Password: password,
|
||||
LockPassword: false,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
settings.GlobalConfiguration.UserDefaults.Apply(user)
|
||||
user.Perm.Admin = true
|
||||
user.DarkMode = true
|
||||
user.ViewMode = "normal"
|
||||
user.LockPassword = false
|
||||
user.Perm = users.Permissions{
|
||||
Create: true,
|
||||
Rename: true,
|
||||
Modify: true,
|
||||
Delete: true,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Admin: true,
|
||||
}
|
||||
err = d.store.Users.Save(user)
|
||||
checkErr(err)
|
||||
}
|
||||
|
|
|
@ -16,12 +16,9 @@ var usersAddCmd = &cobra.Command{
|
|||
Long: `Create a new user and add it to the database.`,
|
||||
Args: cobra.ExactArgs(2), //nolint:gomnd
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
password, err := users.HashPwd(args[1])
|
||||
checkErr(err)
|
||||
|
||||
user := &users.User{
|
||||
Username: args[0],
|
||||
Password: password,
|
||||
Password: args[1],
|
||||
LockPassword: mustGetBool(cmd.Flags(), "lockPassword"),
|
||||
}
|
||||
servSettings, err := d.store.Settings.GetServer()
|
||||
|
|
|
@ -3,9 +3,11 @@ server:
|
|||
baseURL: "/"
|
||||
root: "/Users/steffag/git/go"
|
||||
auth:
|
||||
method: noauth
|
||||
method: password
|
||||
signup: true
|
||||
userDefaults:
|
||||
darkMode: true
|
||||
disableSettings: false
|
||||
scope: "."
|
||||
hideDotfiles: true
|
||||
singleClick: false
|
||||
|
@ -17,5 +19,3 @@ userDefaults:
|
|||
delete: true
|
||||
share: true
|
||||
download: true
|
||||
frontend:
|
||||
theme: dark
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/golang-jwt/jwt/v4/request"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
|
@ -19,20 +20,8 @@ const (
|
|||
TokenExpirationTime = time.Hour * 2
|
||||
)
|
||||
|
||||
type userInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Locale string `json:"locale"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Perm users.Permissions `json:"perm"`
|
||||
Commands []string `json:"commands"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
}
|
||||
|
||||
type authToken struct {
|
||||
User userInfo `json:"user"`
|
||||
User users.User `json:"user"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
|
@ -143,15 +132,9 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int,
|
|||
|
||||
user := &users.User{
|
||||
Username: info.Username,
|
||||
Password: info.Password,
|
||||
}
|
||||
|
||||
pwd, err := users.HashPwd(info.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
user.Password = pwd
|
||||
|
||||
settings.GlobalConfiguration.UserDefaults.Apply(user)
|
||||
userHome, err := d.settings.MakeUserDir(user.Username, user.Scope, d.server.Root)
|
||||
if err != nil {
|
||||
log.Printf("create user: failed to mkdir user home dir: [%s]", userHome)
|
||||
|
@ -176,17 +159,7 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data
|
|||
|
||||
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) {
|
||||
claims := &authToken{
|
||||
User: userInfo{
|
||||
ID: user.ID,
|
||||
Locale: user.Locale,
|
||||
ViewMode: user.ViewMode,
|
||||
SingleClick: user.SingleClick,
|
||||
Perm: user.Perm,
|
||||
LockPassword: user.LockPassword,
|
||||
Commands: user.Commands,
|
||||
HideDotfiles: user.HideDotfiles,
|
||||
DateFormat: user.DateFormat,
|
||||
},
|
||||
User: *user,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpirationTime)),
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
var searchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
response := []map[string]interface{}{}
|
||||
query := r.URL.Query().Get("query")
|
||||
|
||||
// Retrieve the User-Agent and X-Auth headers from the request
|
||||
sessionId := r.Header.Get("SessionId")
|
||||
index := *index.GetIndex()
|
||||
|
|
|
@ -129,7 +129,7 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
|
|||
|
||||
var token string
|
||||
if len(hash) > 0 {
|
||||
tokenBuffer := make([]byte, 96) //nolint:gomnd
|
||||
tokenBuffer := make([]byte, 24) //nolint:gomnd
|
||||
if _, err := rand.Read(tokenBuffer); err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
|||
"LoginPage": auther.LoginPage(),
|
||||
"CSS": false,
|
||||
"ReCaptcha": false,
|
||||
"Theme": d.settings.Frontend.Theme,
|
||||
"EnableThumbs": d.server.EnableThumbnails,
|
||||
"ResizePreview": d.server.ResizePreview,
|
||||
"EnableExec": d.server.EnableExec,
|
||||
|
|
|
@ -124,11 +124,6 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
|
|||
return http.StatusBadRequest, errors.ErrEmptyPassword
|
||||
}
|
||||
|
||||
req.Data.Password, err = users.HashPwd(req.Data.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
userHome, err := d.settings.MakeUserDir(req.Data.Username, req.Data.Scope, d.server.Root)
|
||||
if err != nil {
|
||||
log.Printf("create user: failed to mkdir user home dir: [%s]", userHome)
|
||||
|
@ -184,7 +179,6 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
|
|||
if !d.user.Perm.Admin && d.user.LockPassword {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
req.Data.Password, err = users.HashPwd(req.Data.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package index
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -120,3 +121,35 @@ func updateSize(given string) int {
|
|||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func IsMatchingType(extension string, matchType string) bool {
|
||||
mimetype := mime.TypeByExtension(extension)
|
||||
if strings.HasPrefix(mimetype, matchType) {
|
||||
return true
|
||||
}
|
||||
switch matchType {
|
||||
case "doc":
|
||||
return isDoc(extension)
|
||||
case "archive":
|
||||
return isArchive(extension)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isDoc(extension string) bool {
|
||||
for _, typefile := range documentTypes {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isArchive(extension string) bool {
|
||||
for _, typefile := range compressedFile {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package index
|
|||
|
||||
import (
|
||||
"math/rand"
|
||||
"mime"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
@ -61,10 +60,11 @@ func (si *Index) Search(search string, scope string, sourceSession string) ([]st
|
|||
continue
|
||||
}
|
||||
if isDir {
|
||||
pathName = pathName + "/"
|
||||
fileListTypes[pathName+"/"] = fileType
|
||||
} else {
|
||||
fileListTypes[pathName] = fileType
|
||||
}
|
||||
matching = append(matching, pathName)
|
||||
fileListTypes[pathName] = fileType
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
@ -88,39 +88,33 @@ func scopedPathNameFilter(pathName string, scope string) string {
|
|||
return pathName
|
||||
}
|
||||
|
||||
var fileTypes = map[string]bool{
|
||||
"audio": false,
|
||||
"image": false,
|
||||
"video": false,
|
||||
"doc": false,
|
||||
"archive": false,
|
||||
"dir": false,
|
||||
}
|
||||
|
||||
func containsSearchTerm(pathName string, searchTerm string, options SearchOptions, isDir bool) (bool, map[string]bool) {
|
||||
conditions := options.Conditions
|
||||
path := getLastPathComponent(pathName)
|
||||
// Convert to lowercase once
|
||||
lowerSearchTerm := searchTerm
|
||||
if !conditions["exact"] {
|
||||
path = strings.ToLower(path)
|
||||
lowerSearchTerm = strings.ToLower(searchTerm)
|
||||
searchTerm = strings.ToLower(searchTerm)
|
||||
}
|
||||
if strings.Contains(path, lowerSearchTerm) {
|
||||
// Reuse the fileTypes map and clear its values
|
||||
fileTypes := map[string]bool{
|
||||
"audio": false,
|
||||
"image": false,
|
||||
"video": false,
|
||||
"doc": false,
|
||||
"archive": false,
|
||||
"dir": false,
|
||||
}
|
||||
if strings.Contains(path, searchTerm) {
|
||||
// Calculate fileSize only if needed
|
||||
var fileSize int64
|
||||
if conditions["larger"] || conditions["smaller"] {
|
||||
fileSize = getFileSize(pathName)
|
||||
}
|
||||
matchesAllConditions := true
|
||||
extension := filepath.Ext(path)
|
||||
mimetype := mime.TypeByExtension(extension)
|
||||
fileTypes["audio"] = strings.HasPrefix(mimetype, "audio")
|
||||
fileTypes["image"] = strings.HasPrefix(mimetype, "image")
|
||||
fileTypes["video"] = strings.HasPrefix(mimetype, "video")
|
||||
fileTypes["doc"] = isDoc(extension)
|
||||
fileTypes["archive"] = isArchive(extension)
|
||||
for k := range fileTypes {
|
||||
fileTypes[k] = IsMatchingType(extension, k)
|
||||
}
|
||||
fileTypes["dir"] = isDir
|
||||
|
||||
for t, v := range conditions {
|
||||
if t == "exact" {
|
||||
continue
|
||||
|
@ -128,8 +122,14 @@ func containsSearchTerm(pathName string, searchTerm string, options SearchOption
|
|||
var matchesCondition bool
|
||||
switch t {
|
||||
case "larger":
|
||||
if fileSize == 0 {
|
||||
fileSize = getFileSize(pathName)
|
||||
}
|
||||
matchesCondition = fileSize > int64(options.LargerThan)*bytesInMegabyte
|
||||
case "smaller":
|
||||
if fileSize == 0 {
|
||||
fileSize = getFileSize(pathName)
|
||||
}
|
||||
matchesCondition = fileSize < int64(options.SmallerThan)*bytesInMegabyte
|
||||
default:
|
||||
matchesCondition = v == fileTypes[t]
|
||||
|
@ -144,15 +144,6 @@ func containsSearchTerm(pathName string, searchTerm string, options SearchOption
|
|||
return false, map[string]bool{}
|
||||
}
|
||||
|
||||
func isDoc(extension string) bool {
|
||||
for _, typefile := range documentTypes {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getFileSize(filepath string) int64 {
|
||||
fileInfo, err := os.Stat(rootPath + "/" + filepath)
|
||||
if err != nil {
|
||||
|
@ -161,15 +152,6 @@ func getFileSize(filepath string) int64 {
|
|||
return fileInfo.Size()
|
||||
}
|
||||
|
||||
func isArchive(extension string) bool {
|
||||
for _, typefile := range compressedFile {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getLastPathComponent(path string) string {
|
||||
// Use filepath.Base to extract the last component of the path
|
||||
return filepath.Base(path)
|
||||
|
|
|
@ -61,14 +61,18 @@ func setDefaults() Settings {
|
|||
},
|
||||
Auth: Auth{
|
||||
Method: "password",
|
||||
Signup: true,
|
||||
Recaptcha: Recaptcha{
|
||||
Host: "",
|
||||
},
|
||||
},
|
||||
UserDefaults: UserDefaults{
|
||||
Scope: ".",
|
||||
LockPassword: false,
|
||||
HideDotfiles: true,
|
||||
Scope: ".",
|
||||
LockPassword: false,
|
||||
HideDotfiles: true,
|
||||
DarkMode: false,
|
||||
DisableSettings: false,
|
||||
Locale: "en",
|
||||
Permissions: users.Permissions{
|
||||
Create: true,
|
||||
Rename: true,
|
||||
|
@ -76,6 +80,7 @@ func setDefaults() Settings {
|
|||
Delete: true,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Admin: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -83,6 +88,8 @@ func setDefaults() Settings {
|
|||
|
||||
// Apply applies the default options to a user.
|
||||
func (d *UserDefaults) Apply(u *users.User) {
|
||||
u.DisableSettings = d.DisableSettings
|
||||
u.DarkMode = d.DarkMode
|
||||
u.Scope = d.Scope
|
||||
u.Locale = d.Locale
|
||||
u.ViewMode = d.ViewMode
|
||||
|
|
|
@ -3,7 +3,6 @@ package settings
|
|||
import (
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
// StorageBackend is a settings storage backend.
|
||||
|
@ -59,7 +58,7 @@ func (s *Storage) Save(set *Settings) error {
|
|||
}
|
||||
|
||||
if set.UserDefaults.ViewMode == "" {
|
||||
set.UserDefaults.ViewMode = users.MosaicViewMode
|
||||
set.UserDefaults.ViewMode = "normal"
|
||||
}
|
||||
|
||||
if set.Rules == nil {
|
||||
|
|
|
@ -61,13 +61,13 @@ type Frontend struct {
|
|||
DisableExternal bool `json:"disableExternal"`
|
||||
DisableUsedPercentage bool `json:"disableUsedPercentage"`
|
||||
Files string `json:"files"`
|
||||
Theme string `json:"theme"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
// UserDefaults is a type that holds the default values
|
||||
// for some fields on User.
|
||||
type UserDefaults struct {
|
||||
DarkMode bool `json:"darkMode"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
DisableSettings bool `json:"disableSettings,omitempty"`
|
||||
Scope string `json:"scope"`
|
||||
|
|
|
@ -28,12 +28,13 @@ frontend:
|
|||
disableExternal: false
|
||||
disableUsedPercentage: true
|
||||
files: ""
|
||||
theme: ""
|
||||
color: ""
|
||||
userDefaults:
|
||||
scope: ""
|
||||
locale: ""
|
||||
viewMode: ""
|
||||
darkMode: true
|
||||
disableSettings: false
|
||||
singleClick: true
|
||||
sorting:
|
||||
by: ""
|
||||
|
|
|
@ -2,6 +2,7 @@ package bolt
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"github.com/asdine/storm/v3"
|
||||
|
@ -73,7 +74,13 @@ func (st usersBackend) Update(user *users.User, fields ...string) error {
|
|||
}
|
||||
|
||||
func (st usersBackend) Save(user *users.User) error {
|
||||
err := st.db.Save(user)
|
||||
log.Println("userinfo", user.Password)
|
||||
pass, err := users.HashPwd(user.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Password = pass
|
||||
err = st.db.Save(user)
|
||||
if err == storm.ErrAlreadyExists {
|
||||
return errors.ErrExist
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// HashPwd hashes a password.
|
||||
func HashPwd(password string) (string, error) {
|
||||
log.Println("hashing password", password)
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) {
|
|||
|
||||
// Update updates a user in the database.
|
||||
func (s *Storage) Update(user *User, fields ...string) error {
|
||||
err := user.Clean("", fields...)
|
||||
err := user.Clean("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,16 +6,10 @@ import (
|
|||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/files"
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
)
|
||||
|
||||
var (
|
||||
ListViewMode = "list"
|
||||
MosaicViewMode = "mosaic"
|
||||
)
|
||||
|
||||
type Permissions struct {
|
||||
Admin bool `json:"admin"`
|
||||
Execute bool `json:"execute"`
|
||||
|
@ -29,21 +23,23 @@ type Permissions struct {
|
|||
|
||||
// User describes a user.
|
||||
type User struct {
|
||||
ID uint `storm:"id,increment" json:"id"`
|
||||
Username string `storm:"unique" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Scope string `json:"scope"`
|
||||
Locale string `json:"locale"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Perm Permissions `json:"perm"`
|
||||
Commands []string `json:"commands"`
|
||||
Sorting files.Sorting `json:"sorting"`
|
||||
Fs afero.Fs `json:"-" yaml:"-"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
DarkMode bool `json:"darkMode"`
|
||||
DisableSettings bool `json:"disableSettings"`
|
||||
ID uint `storm:"id,increment" json:"id"`
|
||||
Username string `storm:"unique" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Scope string `json:"scope"`
|
||||
Locale string `json:"locale"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Perm Permissions `json:"perm"`
|
||||
Commands []string `json:"commands"`
|
||||
Sorting files.Sorting `json:"sorting"`
|
||||
Fs afero.Fs `json:"-" yaml:"-"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
}
|
||||
|
||||
// GetRules implements rules.Provider.
|
||||
|
@ -51,53 +47,11 @@ func (u *User) GetRules() []rules.Rule {
|
|||
return u.Rules
|
||||
}
|
||||
|
||||
var checkableFields = []string{
|
||||
"Username",
|
||||
"Password",
|
||||
"Scope",
|
||||
"ViewMode",
|
||||
"Commands",
|
||||
"Sorting",
|
||||
"Rules",
|
||||
}
|
||||
|
||||
// Clean cleans up a user and verifies if all its fields
|
||||
// are alright to be saved.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (u *User) Clean(baseScope string, fields ...string) error {
|
||||
if len(fields) == 0 {
|
||||
fields = checkableFields
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
switch field {
|
||||
case "Username":
|
||||
if u.Username == "" {
|
||||
return errors.ErrEmptyUsername
|
||||
}
|
||||
case "Password":
|
||||
if u.Password == "" {
|
||||
return errors.ErrEmptyPassword
|
||||
}
|
||||
case "ViewMode":
|
||||
if u.ViewMode == "" {
|
||||
u.ViewMode = ListViewMode
|
||||
}
|
||||
case "Commands":
|
||||
if u.Commands == nil {
|
||||
u.Commands = []string{}
|
||||
}
|
||||
case "Sorting":
|
||||
if u.Sorting.By == "" {
|
||||
u.Sorting.By = "name"
|
||||
}
|
||||
case "Rules":
|
||||
if u.Rules == nil {
|
||||
u.Rules = []rules.Rule{}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (u *User) Clean(baseScope string) error {
|
||||
|
||||
if u.Fs == nil {
|
||||
scope := u.Scope
|
||||
|
|
|
@ -2,7 +2,7 @@ package version
|
|||
|
||||
var (
|
||||
// Version is the current File Browser version.
|
||||
Version = "(0.2.0)"
|
||||
Version = "(0.2.1)"
|
||||
// CommitSHA is the commmit sha.
|
||||
CommitSHA = "(unknown)"
|
||||
)
|
||||
|
|
|
@ -40,6 +40,8 @@ frontend:
|
|||
theme: ""
|
||||
color: ""
|
||||
userDefaults:
|
||||
settingsAllowed: true
|
||||
darkMode: false
|
||||
scope: ""
|
||||
locale: ""
|
||||
viewMode: ""
|
||||
|
@ -176,6 +178,10 @@ UserDefaults:
|
|||
|
||||
### UserDefaults configuration settings
|
||||
|
||||
- `darkMode`: Determines whether dark mode is enabled for the user (true or false)
|
||||
|
||||
- `settingsAllowed`: Determines whether settings page is enabled for the user (true or false)
|
||||
|
||||
- `scope`: This is a scope of the permissions, "." or "./" means all directories, "./downloads" would mean only the downloads folder.
|
||||
|
||||
- `locale`: This is the locale configuration.
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"devDependencies": {
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
}
|
||||
|
@ -293,9 +293,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint-community/regexpp": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
|
||||
"integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz",
|
||||
"integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
|
||||
|
@ -347,9 +347,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.50.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
|
||||
"integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
|
||||
"version": "8.51.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
|
||||
"integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
@ -3025,15 +3025,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.50.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
|
||||
"integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
|
||||
"version": "8.51.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
|
||||
"integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.2",
|
||||
"@eslint/js": "8.50.0",
|
||||
"@eslint/js": "8.51.0",
|
||||
"@humanwhocodes/config-array": "^0.11.11",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
|
@ -3774,12 +3774,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/flat-cache": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
|
||||
"integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz",
|
||||
"integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"flatted": "^3.2.7",
|
||||
"flatted": "^3.2.9",
|
||||
"keyv": "^4.5.3",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
|
@ -3970,9 +3970,9 @@
|
|||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.22.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
|
||||
"integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
|
||||
"version": "13.23.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
|
||||
"integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.20.2"
|
||||
|
@ -4747,9 +4747,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
|
||||
"integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
|
@ -7974,9 +7974,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-eslint-parser": {
|
||||
"version": "9.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz",
|
||||
"integrity": "sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==",
|
||||
"version": "9.3.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
|
||||
"integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
"devDependencies": {
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
|
|
|
@ -128,8 +128,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
[{[ if .Theme -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css" />
|
||||
[{[ if .darkMode -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/dark.css" />
|
||||
[{[ end ]}]
|
||||
[{[ if .CSS -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
:root {
|
||||
--background: #141D24;
|
||||
--surfacePrimary: #20292F;
|
||||
--surfaceSecondary: #3A4147;
|
||||
--divider: rgba(255, 255, 255, 0.12);
|
||||
--textPrimary: rgba(255, 255, 255, 0.87);
|
||||
--textSecondary: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
#loading {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
#login {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
header {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
@supports (backdrop-filter: none) {
|
||||
header {
|
||||
background: transparent;
|
||||
backdrop-filter: blur(16px) invert(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
#search #input {
|
||||
background: var(--surfaceSecondary);
|
||||
border-color: var(--surfaceSecondary);
|
||||
}
|
||||
#search #input input::placeholder {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
#search.active #input {
|
||||
background: var(--surfacePrimary);
|
||||
border-color: white;
|
||||
}
|
||||
#search.active input {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
#search #result {
|
||||
background: var(--background);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
#search .boxes h3 {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.action {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
.action:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
.action .counter {
|
||||
border-color: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
nav > div {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
border-color: var(--divider);
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
.breadcrumbs span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
.breadcrumbs a:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
#listing .item {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
border-color: var(--divider) !important;
|
||||
}
|
||||
|
||||
#listing .item .modified {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
#listing h2,
|
||||
#listing.list .header span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
#listing.list .header span {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
#listing.list .item.header {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.message {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
.button--flat:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
|
||||
.dashboard #nav ul li {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
.dashboard #nav ul li:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
#result-list {
|
||||
background-color:#292929;
|
||||
}
|
||||
|
||||
.card h3,
|
||||
.dashboard #nav,
|
||||
.dashboard p label {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
.card#share input,
|
||||
.card#share select,
|
||||
.input {
|
||||
background: var(--surfaceSecondary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.input:hover,
|
||||
.input:focus {
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
.input--red {
|
||||
background: #73302D;
|
||||
}
|
||||
|
||||
.input--green {
|
||||
background: #147A41;
|
||||
}
|
||||
|
||||
.dashboard #nav .wrapper,
|
||||
.collapsible {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
.collapsible > label * {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
table th {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
.file-list li:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
.file-list li:before {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
.shell {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
.shell__result {
|
||||
border-top: 1px solid var(--divider);
|
||||
}
|
||||
|
||||
#editor-container {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
#editor-container .bar {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
nav {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
#file-selection {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
#file-selection span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
#dropdown {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
.share__box {
|
||||
background: var(--surfacePrimary) !important;
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.share__box__element {
|
||||
border-top-color: var(--divider);
|
||||
}
|
||||
|
||||
.helpButton {
|
||||
background: var(--background);
|
||||
}
|
||||
.sizeInputWrapper {
|
||||
background: var(--background);
|
||||
color: white
|
||||
}
|
||||
.button-group button {
|
||||
background: var(--background);
|
||||
color: white
|
||||
}
|
||||
#result-desktop #result-list {
|
||||
background: #2a3137;
|
||||
max-height: unset;
|
||||
}
|
|
@ -1,18 +1,20 @@
|
|||
<template>
|
||||
<router-view></router-view>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
computed: {
|
||||
},
|
||||
mounted() {
|
||||
const loading = document.getElementById("loading");
|
||||
loading.classList.add("done");
|
||||
|
||||
setTimeout(function () {
|
||||
setTimeout(() => {
|
||||
loading.parentNode.removeChild(loading);
|
||||
}, 200);
|
||||
},
|
||||
|
@ -20,5 +22,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<style>
|
||||
/* Always load styles.css */
|
||||
@import "./css/styles.css";
|
||||
@import "./css/dark.css";
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div id="search" @click="open" v-bind:class="{ active, ongoing }">
|
||||
<div id="search" @click="open" v-bind:class="{ active, ongoing, 'dark-mode': isDarkMode }">
|
||||
<div id="input">
|
||||
<button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
|
||||
<i class="material-icons">close</i>
|
||||
|
@ -149,6 +149,7 @@
|
|||
padding-bottom: 1em;
|
||||
-webkit-transition: width 0.3s ease 0s;
|
||||
transition: width 0.3s ease 0s;
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
#result-desktop {
|
||||
|
@ -172,6 +173,8 @@
|
|||
background-color: lightgray;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#search.active #result-desktop ul li a {
|
||||
|
@ -201,6 +204,7 @@
|
|||
|
||||
/* Search */
|
||||
#search {
|
||||
background-color:unset;
|
||||
z-index:3;
|
||||
position: fixed;
|
||||
top: .5em;
|
||||
|
@ -314,7 +318,7 @@ body.rtl #search #result ul>* {
|
|||
}
|
||||
|
||||
#search.active #input {
|
||||
background-color: lightgray;
|
||||
background-color: var(--background);
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-bottom-style: none;
|
||||
|
@ -485,7 +489,7 @@ export default {
|
|||
{ label: "Photos", value: "type:image" },
|
||||
{ label: "Audio", value: "type:audio" },
|
||||
{ label: "Videos", value: "type:video" },
|
||||
{ label: "Documents", value: "type:docs" },
|
||||
{ label: "Documents", value: "type:doc" },
|
||||
{ label: "Archives", value: "type:archive" },
|
||||
],
|
||||
value: "",
|
||||
|
@ -538,6 +542,9 @@ export default {
|
|||
computed: {
|
||||
...mapState(["user", "show"]),
|
||||
...mapGetters(["isListing"]),
|
||||
isDarkMode() {
|
||||
return this.user.darkMode === true
|
||||
},
|
||||
showBoxes() {
|
||||
return this.searchTypes == "";
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<nav :class="{ active }">
|
||||
<nav :class="{ active, 'dark-mode': isDarkMode }">
|
||||
<template v-if="isLogged">
|
||||
<button class="action" @click="toRoot" :aria-label="$t('sidebar.myFiles')" :title="$t('sidebar.myFiles')">
|
||||
<i class="material-icons">folder</i>
|
||||
|
@ -87,6 +87,9 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
isDarkMode() {
|
||||
return this.user.darkMode === true
|
||||
},
|
||||
...mapGetters(["isLogged"]),
|
||||
active() {
|
||||
return this.$store.state.show === "sidebar";
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<template>
|
||||
<select v-on:change="change" :value="viewMode">
|
||||
<option v-for="mode in viewModes" :key="mode" :value="mode">
|
||||
{{ mode }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ViewMode",
|
||||
props: ["viewMode"],
|
||||
data() {
|
||||
return {
|
||||
viewModes: ['list', 'compact', 'normal', 'gallery'],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
change(event) {
|
||||
this.$emit("update:viewMode", event.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -203,6 +203,7 @@ body.rtl .breadcrumbs a {
|
|||
width: 95%;
|
||||
max-width: 30em;
|
||||
z-index: 1;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
button {
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
/* Define a class .dark-mode for dark mode styles */
|
||||
.dark-mode {
|
||||
--background: #141D24;
|
||||
--surfacePrimary: #20292F;
|
||||
--surfaceSecondary: #3A4147;
|
||||
--divider: rgba(255, 255, 255, 0.12);
|
||||
--textPrimary: rgba(255, 255, 255, 0.87);
|
||||
--textSecondary: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.dark-mode #loading {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.dark-mode #login {
|
||||
background: var(--background);
|
||||
}
|
||||
/* Loading */
|
||||
.dark-mode #loading {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
/* Login */
|
||||
.dark-mode #login {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.dark-mode header {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
/* Header with backdrop-filter support */
|
||||
@supports (backdrop-filter: none) {
|
||||
.dark-mode header {
|
||||
background: transparent;
|
||||
backdrop-filter: blur(16px) invert(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
#search.dark-mode input {
|
||||
color:white
|
||||
}
|
||||
|
||||
#search.active.dark-mode #input {
|
||||
border-color: white;
|
||||
}
|
||||
/* Search input */
|
||||
.dark-mode #search #input {
|
||||
background: var(--surfaceSecondary);
|
||||
border-color: var(--surfaceSecondary);
|
||||
}
|
||||
|
||||
.dark-mode #search #input input::placeholder {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
/* Active Search input */
|
||||
.dark-mode #search.active #input {
|
||||
background: var(--surfacePrimary);
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.dark-mode #search.active input {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Search result */
|
||||
.dark-mode #search #result {
|
||||
background: var(--background);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Search boxes */
|
||||
.dark-mode #search .boxes h3 {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Action */
|
||||
.dark-mode .action {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
|
||||
.dark-mode .action:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
/* Action counter */
|
||||
.dark-mode .action .counter {
|
||||
border-color: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.dark-mode nav > div {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
|
||||
/* Breadcrumbs */
|
||||
.dark-mode .breadcrumbs {
|
||||
border-color: var(--divider);
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
|
||||
.dark-mode .breadcrumbs span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
|
||||
.dark-mode .breadcrumbs a:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
/* Listing items */
|
||||
.dark-mode #listing .item {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
border-color: var(--divider) !important;
|
||||
}
|
||||
|
||||
/* Listing item modified text */
|
||||
.dark-mode #listing .item .modified {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
/* Listing header and span */
|
||||
.dark-mode #listing h2,
|
||||
.dark-mode #listing.list .header span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
|
||||
/* Message */
|
||||
.dark-mode .message {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Card */
|
||||
.dark-mode .card {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Flat button hover */
|
||||
.dark-mode .button--flat:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
|
||||
/* Dashboard navigation */
|
||||
.dark-mode .dashboard #nav ul li {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
.dark-mode .dashboard #nav ul li:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
#search.active.dark-mode #result {
|
||||
background-color: black;
|
||||
}
|
||||
/* Result list */
|
||||
.dark-mode #result-list {
|
||||
background-color: var(--surfacePrimary);
|
||||
color:white;
|
||||
}
|
||||
|
||||
/* Card, Dashboard navigation, and label */
|
||||
.dark-mode .card h3,
|
||||
.dark-mode .dashboard #nav,
|
||||
.dark-mode .dashboard p label {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.dark-mode .card#share input,
|
||||
.dark-mode .card#share select,
|
||||
.dark-mode .input {
|
||||
background: var(--surfaceSecondary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Input hover and focus */
|
||||
.dark-mode .input:hover,
|
||||
.dark-mode .input:focus {
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Red input */
|
||||
.dark-mode .input--red {
|
||||
background: #73302D;
|
||||
}
|
||||
|
||||
/* Green input */
|
||||
.dark-mode .input--green {
|
||||
background: #147A41;
|
||||
}
|
||||
|
||||
/* Collapsible and label */
|
||||
.dark-mode .dashboard #nav .wrapper,
|
||||
.dark-mode .collapsible {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
|
||||
.dark-mode .collapsible > label * {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Table header */
|
||||
.dark-mode table th {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
/* File list item */
|
||||
.dark-mode .file-list li:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
|
||||
.dark-mode .file-list li:before {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
/* Shell */
|
||||
.dark-mode .shell {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Shell result */
|
||||
.dark-mode .shell__result {
|
||||
border-top: 1px solid var(--divider);
|
||||
}
|
||||
|
||||
/* Editor container */
|
||||
.dark-mode #editor-container {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.dark-mode #editor-container .bar {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.dark-mode nav {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
/* File selection */
|
||||
.dark-mode #file-selection {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
.dark-mode #file-selection span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
|
||||
/* Dropdown */
|
||||
.dark-mode #dropdown {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
/* Share box */
|
||||
.dark-mode .share__box {
|
||||
background: var(--surfacePrimary) !important;
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
/* Share box element */
|
||||
.dark-mode .share__box__element {
|
||||
border-top-color: var(--divider);
|
||||
}
|
||||
|
||||
/* Help button */
|
||||
.dark-mode .helpButton {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
/* Size input wrapper */
|
||||
.dark-mode .sizeInputWrapper {
|
||||
background: var(--background);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Button group button */
|
||||
.dark-mode .button-group button {
|
||||
background: var(--background);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Result desktop */
|
||||
.dark-mode #result-desktop #result-list {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
/* Result desktop background */
|
||||
.dark-mode #result-desktop {
|
||||
background-color: var(--background);
|
||||
}
|
|
@ -85,41 +85,53 @@ body.rtl #listing {
|
|||
display: block;
|
||||
}
|
||||
|
||||
#listing.mosaic {
|
||||
#listing {
|
||||
padding-top: 1em;
|
||||
margin: 0 -0.5em;
|
||||
}
|
||||
|
||||
#listing.mosaic .item {
|
||||
#listing.gallery .item,
|
||||
#listing.compact .item,
|
||||
#listing.normal .item,
|
||||
#listing.list .item {
|
||||
width: calc(33% - 1em);
|
||||
max-width: 300px;
|
||||
margin: .5em;
|
||||
padding: 0.5em;
|
||||
border-radius: 1em;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
|
||||
}
|
||||
#listing.gallery .item {
|
||||
max-width: 300px;
|
||||
}
|
||||
#listing.list .item,
|
||||
#listing.compact .item {
|
||||
max-width: 100%;
|
||||
border-radius: 0em;
|
||||
}
|
||||
|
||||
#listing.mosaic .item:hover {
|
||||
#listing .item:hover {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !important;
|
||||
}
|
||||
|
||||
#listing.mosaic .header {
|
||||
#listing .header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing.mosaic .item div:first-of-type {
|
||||
#listing .item div:first-of-type {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
#listing.mosaic .item div:last-of-type {
|
||||
#listing .item div:last-of-type {
|
||||
width: calc(100% - 5vw);
|
||||
}
|
||||
|
||||
#listing.mosaic.gallery .item div:first-of-type {
|
||||
#listing.gallery .item div:first-of-type {
|
||||
width: 100%;
|
||||
height: 12em;
|
||||
}
|
||||
|
||||
#listing.mosaic.gallery .item div:last-of-type {
|
||||
#listing.gallery .item div:last-of-type {
|
||||
position: absolute;
|
||||
bottom: 0.5em;
|
||||
padding: 1em;
|
||||
|
@ -127,19 +139,19 @@ body.rtl #listing {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
#listing.mosaic.gallery .item[data-type=image] div:last-of-type {
|
||||
#listing.gallery .item[data-type=image] div:last-of-type {
|
||||
color: white;
|
||||
background: linear-gradient(#0000, #0009);
|
||||
}
|
||||
|
||||
#listing.mosaic.gallery .item i {
|
||||
#listing.gallery .item i {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
font-size: 8em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#listing.mosaic.gallery .item img {
|
||||
#listing.gallery .item img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -149,6 +161,109 @@ body.rtl #listing {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#listing.compact {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#listing.compact .item {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
#listing.compact h2 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing.compact .item div:first-of-type {
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
#listing.compact .item div:first-of-type i {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#listing.compact .item div:first-of-type img {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
#listing.compact .item div:last-of-type {
|
||||
width: calc(100% - 3em);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#listing.compact .item .name {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#listing.compact .item .size {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
#listing.compact .header i {
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
margin-left: .2em;
|
||||
}
|
||||
|
||||
#listing.compact .header {
|
||||
display: flex !important;
|
||||
background: var(--surfacePrimary);
|
||||
z-index: 999;
|
||||
padding: .85em;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#listing.compact .header>div:first-child {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#listing.compact .header .name {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
#listing.compact .header a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#listing.compact .header>div:first-child {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#listing.compact .name {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#listing.compact .header .name {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
#listing.compact .header span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#listing.compact .header i {
|
||||
opacity: 0;
|
||||
transition: .1s ease all;
|
||||
}
|
||||
|
||||
#listing.compact .header p:hover i,
|
||||
#listing.compact .header .active i {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#listing.compact .header .active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#listing.list {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
@ -160,14 +275,10 @@ body.rtl #listing {
|
|||
width: 100%;
|
||||
margin: 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 0;
|
||||
padding: .5em;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
#listing.list h2 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing .item[aria-selected=true] {
|
||||
background: var(--blue) !important;
|
||||
color: var(--item-selected) !important;
|
||||
|
@ -200,7 +311,7 @@ body.rtl #listing {
|
|||
width: 25%;
|
||||
}
|
||||
|
||||
#listing .item.header {
|
||||
#listing .header {
|
||||
display: none !important;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
@ -211,20 +322,34 @@ body.rtl #listing {
|
|||
margin-left: .2em;
|
||||
}
|
||||
|
||||
#listing.list .item.header {
|
||||
#listing.compact .header,
|
||||
#listing.list .header {
|
||||
display: flex !important;
|
||||
background: #fafafa;
|
||||
background: var(--surfacePrimary);
|
||||
border-top-left-radius: 1em;
|
||||
border-top-right-radius: 1em;
|
||||
z-index: 999;
|
||||
padding: .85em;
|
||||
width:100%;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
#listing.list .item:first-child {
|
||||
margin-top: .5em;
|
||||
border-top-left-radius: 1em;
|
||||
border-top-right-radius: 1em;
|
||||
}
|
||||
|
||||
#listing.list .item.header>div:first-child {
|
||||
#listing.list .item:last-child {
|
||||
margin-bottom: .5em;
|
||||
border-bottom-left-radius: 1em;
|
||||
border-bottom-right-radius: 1em;
|
||||
}
|
||||
#listing.list .header>div:first-child {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#listing.list .item.header .name {
|
||||
#listing.list .header .name {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
|
@ -232,7 +357,7 @@ body.rtl #listing {
|
|||
color: inherit;
|
||||
}
|
||||
|
||||
#listing.list .item.header>div:first-child {
|
||||
#listing.list .header>div:first-child {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
|
@ -240,7 +365,7 @@ body.rtl #listing {
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
#listing.list .item.header .name {
|
||||
#listing.list .header .name {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
|
@ -258,7 +383,7 @@ body.rtl #listing {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
#listing.list .item.header .active {
|
||||
#listing.list .header .active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
#listing.list .item div:last-of-type{
|
||||
display:block;
|
||||
width:100%;
|
||||
}
|
||||
body {
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
#listing.list .item .size {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing.list .item .name {
|
||||
width: 60%;
|
||||
}
|
||||
|
@ -36,7 +36,9 @@
|
|||
#listing {
|
||||
margin-bottom: 5em;
|
||||
}
|
||||
|
||||
#listing .item {
|
||||
min-width: 100%
|
||||
}
|
||||
body.rtl #listing {
|
||||
margin-right: unset;
|
||||
}
|
||||
|
@ -117,9 +119,6 @@
|
|||
|
||||
|
||||
@media (max-width: 450px) {
|
||||
#listing.list .item .modified {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing.list .item .name {
|
||||
width: 100%;
|
||||
|
|
|
@ -11,9 +11,7 @@ export function parseToken(token) {
|
|||
}
|
||||
|
||||
const data = JSON.parse(Base64.decode(parts[1]));
|
||||
|
||||
document.cookie = `auth=${token}; path=/`;
|
||||
|
||||
localStorage.setItem("jwt", token);
|
||||
store.commit("setJWT", token);
|
||||
store.commit("setSession", generateRandomCode(8));
|
||||
|
@ -40,7 +38,6 @@ export async function login(username, password, recaptcha) {
|
|||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
const body = await res.text();
|
||||
|
||||
if (res.status === 200) {
|
||||
|
|
|
@ -11,7 +11,6 @@ const logoURL = `${staticURL}/img/logo.svg`;
|
|||
const noAuth = window.FileBrowser.NoAuth;
|
||||
const authMethod = window.FileBrowser.AuthMethod;
|
||||
const loginPage = window.FileBrowser.LoginPage;
|
||||
const theme = window.FileBrowser.Theme;
|
||||
const enableThumbs = window.FileBrowser.EnableThumbs;
|
||||
const resizePreview = window.FileBrowser.ResizePreview;
|
||||
const enableExec = window.FileBrowser.EnableExec;
|
||||
|
@ -30,7 +29,6 @@ export {
|
|||
noAuth,
|
||||
authMethod,
|
||||
loginPage,
|
||||
theme,
|
||||
enableThumbs,
|
||||
resizePreview,
|
||||
enableExec,
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
<editorBar v-else-if="currentView === 'editor'"></editorBar>
|
||||
<defaultBar v-else></defaultBar>
|
||||
<sidebar></sidebar>
|
||||
<main>
|
||||
<main :class="{ 'dark-mode': isDarkMode }">
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
<prompts></prompts>
|
||||
<prompts :class="{ 'dark-mode': isDarkMode }"></prompts>
|
||||
<upload-files></upload-files>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -45,7 +45,9 @@ export default {
|
|||
computed: {
|
||||
...mapGetters(["isLogged", "progress", "isListing"]),
|
||||
...mapState(["req", "user", "state"]),
|
||||
|
||||
isDarkMode() {
|
||||
return this.user.darkMode === true
|
||||
},
|
||||
isExecEnabled: () => enableExec,
|
||||
currentView() {
|
||||
if (this.req.type == undefined) {
|
||||
|
@ -82,3 +84,13 @@ export default {
|
|||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
/* Use the class .dark-mode to apply styles conditionally */
|
||||
.dark-mode {
|
||||
background: var(--background);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,10 +1,7 @@
|
|||
<template>
|
||||
<div class="dashboard">
|
||||
<div id="nav">
|
||||
<div v-if="disabledSettings">
|
||||
nothing to see here
|
||||
</div>
|
||||
<div v-else class="wrapper">
|
||||
<div v-if="settingsEnabled" class="wrapper">
|
||||
<ul>
|
||||
<router-link to="/settings/profile"
|
||||
><li :class="{ active: $route.path === '/settings/profile' }">
|
||||
|
@ -60,11 +57,10 @@ export default {
|
|||
this.$store.commit("updateRequest", { name: "Settings" });
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user", "loading","req"]),
|
||||
disableSettings() {
|
||||
console.log(this.User)
|
||||
return this.User.disableSettings == "true"
|
||||
}
|
||||
...mapState(["user"]),
|
||||
settingsEnabled() {
|
||||
return this.user.disableSettings == false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -38,6 +38,7 @@ export default {
|
|||
dragCounter: 0,
|
||||
width: window.innerWidth,
|
||||
itemWeight: 0,
|
||||
viewModes: ['list', 'compact', 'normal', 'gallery'],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -106,8 +107,9 @@ export default {
|
|||
viewIcon() {
|
||||
const icons = {
|
||||
list: "view_module",
|
||||
mosaic: "grid_view",
|
||||
"mosaic gallery": "view_list",
|
||||
compact: "view_module",
|
||||
normal: "grid_view",
|
||||
gallery: "view_list",
|
||||
};
|
||||
return icons[this.user.viewMode];
|
||||
},
|
||||
|
@ -259,21 +261,14 @@ export default {
|
|||
},
|
||||
switchView: async function () {
|
||||
this.$store.commit("closeHovers");
|
||||
const modes = {
|
||||
list: "mosaic",
|
||||
mosaic: "mosaic gallery",
|
||||
"mosaic gallery": "list",
|
||||
};
|
||||
|
||||
const currentIndex = this.viewModes.indexOf(this.user.viewMode);
|
||||
const nextIndex = (currentIndex + 1) % this.viewModes.length;
|
||||
const data = {
|
||||
id: this.user.id,
|
||||
viewMode: modes[this.user.viewMode] || "list",
|
||||
viewMode: this.viewModes[nextIndex],
|
||||
};
|
||||
//users.update(data, ["viewMode"]).catch(this.$showError);
|
||||
users.update(data, ["viewMode"]).catch(this.$showError);
|
||||
this.$store.commit("updateUser", data);
|
||||
|
||||
//this.setItemWeight();
|
||||
//this.fillWindow();
|
||||
},
|
||||
preventDefault(event) {
|
||||
// Wrapper around prevent default.
|
||||
|
@ -375,7 +370,7 @@ export default {
|
|||
let columns = Math.floor(
|
||||
document.querySelector("main").offsetWidth / this.columnWidth
|
||||
);
|
||||
let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
|
||||
let items = css(["#listing .item", "#listing .item"]);
|
||||
if (columns === 0) columns = 1;
|
||||
items.style.width = `calc(${100 / columns}% - 1em)`;
|
||||
},
|
||||
|
@ -583,7 +578,6 @@ export default {
|
|||
}
|
||||
this.$store.commit("updateRequest", {});
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
console.log(url)
|
||||
this.$router.push({ path: uri });
|
||||
},
|
||||
upload: function () {
|
||||
|
|
|
@ -50,6 +50,7 @@ export default {
|
|||
dragCounter: 0,
|
||||
width: window.innerWidth,
|
||||
itemWeight: 0,
|
||||
viewModes: ['list', 'compact', 'normal', 'gallery'],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -115,8 +116,9 @@ export default {
|
|||
viewIcon() {
|
||||
const icons = {
|
||||
list: "view_module",
|
||||
mosaic: "grid_view",
|
||||
"mosaic gallery": "view_list",
|
||||
compact: "view_module",
|
||||
normal: "grid_view",
|
||||
gallery: "view_list",
|
||||
};
|
||||
return icons[this.user.viewMode];
|
||||
},
|
||||
|
@ -268,21 +270,14 @@ export default {
|
|||
},
|
||||
switchView: async function () {
|
||||
this.$store.commit("closeHovers");
|
||||
const modes = {
|
||||
list: "mosaic",
|
||||
mosaic: "mosaic gallery",
|
||||
"mosaic gallery": "list",
|
||||
};
|
||||
|
||||
const currentIndex = this.viewModes.indexOf(this.user.viewMode);
|
||||
const nextIndex = (currentIndex + 1) % this.viewModes.length;
|
||||
const data = {
|
||||
id: this.user.id,
|
||||
viewMode: modes[this.user.viewMode] || "list",
|
||||
viewMode: this.viewModes[nextIndex],
|
||||
};
|
||||
//users.update(data, ["viewMode"]).catch(this.$showError);
|
||||
users.update(data, ["viewMode"]).catch(this.$showError);
|
||||
this.$store.commit("updateUser", data);
|
||||
|
||||
//this.setItemWeight();
|
||||
//this.fillWindow();
|
||||
},
|
||||
preventDefault(event) {
|
||||
// Wrapper around prevent default.
|
||||
|
@ -384,7 +379,7 @@ export default {
|
|||
let columns = Math.floor(
|
||||
document.querySelector("main").offsetWidth / this.columnWidth
|
||||
);
|
||||
let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
|
||||
let items = css(["#listing .item", "#listing .item"]);
|
||||
if (columns === 0) columns = 1;
|
||||
items.style.width = `calc(${100 / columns}% - 1em)`;
|
||||
},
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
multiple
|
||||
/>
|
||||
</div>
|
||||
<div v-else id="listing" ref="listing" :class="user.viewMode + ' file-icons'">
|
||||
<div v-else id="listing" ref="listing" :class="listingViewMode + ' file-icons'">
|
||||
<div>
|
||||
<div class="item header">
|
||||
<div></div>
|
||||
|
@ -132,8 +132,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 v-if="req.numDirs > 0">{{ $t("files.folders") }}</h2>
|
||||
<div v-if="req.numDirs > 0">
|
||||
<div class="header-items">
|
||||
<h2>{{ $t("files.folders") }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="req.numDirs > 0">
|
||||
<item
|
||||
v-for="item in dirs"
|
||||
|
@ -150,7 +153,11 @@
|
|||
</item>
|
||||
</div>
|
||||
|
||||
<h2 v-if="req.numFiles > 0">{{ $t("files.files") }}</h2>
|
||||
<div v-if="req.numFiles > 0">
|
||||
<div class="header-items">
|
||||
<h2>{{ $t("files.files") }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="req.numFiles > 0">
|
||||
<item
|
||||
v-for="item in files"
|
||||
|
@ -201,6 +208,15 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
.header-items {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||
|
@ -290,11 +306,15 @@ export default {
|
|||
viewIcon() {
|
||||
const icons = {
|
||||
list: "view_module",
|
||||
mosaic: "grid_view",
|
||||
"mosaic gallery": "view_list",
|
||||
compact: "view_module",
|
||||
normal: "grid_view",
|
||||
gallery: "view_list",
|
||||
};
|
||||
return icons[this.user.viewMode];
|
||||
},
|
||||
listingViewMode() {
|
||||
return this.user.viewMode
|
||||
},
|
||||
headerButtons() {
|
||||
return {
|
||||
select: this.selectedCount > 0,
|
||||
|
@ -527,7 +547,7 @@ export default {
|
|||
let columns = Math.floor(
|
||||
document.querySelector("main").offsetWidth / this.columnWidth
|
||||
);
|
||||
let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
|
||||
let items = css(["#listing .item", "#listing .item"]);
|
||||
if (columns === 0) columns = 1;
|
||||
items.style.width = `calc(${100 / columns}% - 1em)`;
|
||||
},
|
||||
|
|
|
@ -71,15 +71,6 @@
|
|||
{{ $t("settings.disableUsedDiskPercentage") }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="theme">{{ $t("settings.themes.title") }}</label>
|
||||
<themes
|
||||
class="input input--block"
|
||||
:theme.sync="settings.frontend.theme"
|
||||
id="theme"
|
||||
></themes>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="branding-name">{{ $t("settings.instanceName") }}</label>
|
||||
<input
|
||||
|
@ -194,13 +185,11 @@ import { settings as api } from "@/api";
|
|||
import { enableExec } from "@/utils/constants";
|
||||
import UserForm from "@/components/settings/UserForm";
|
||||
import Rules from "@/components/settings/Rules";
|
||||
import Themes from "@/components/settings/Themes";
|
||||
import Errors from "@/views/Errors";
|
||||
|
||||
export default {
|
||||
name: "settings",
|
||||
components: {
|
||||
Themes,
|
||||
UserForm,
|
||||
Rules,
|
||||
Errors,
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>
|
||||
<input type="checkbox" v-model="darkMode" />
|
||||
Dark Mode
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" v-model="hideDotfiles" />
|
||||
{{ $t("settings.hideDotfiles") }}
|
||||
|
@ -19,6 +23,11 @@
|
|||
<input type="checkbox" v-model="dateFormat" />
|
||||
{{ $t("settings.setDateFormat") }}
|
||||
</p>
|
||||
<h3>Listing View Style</h3>
|
||||
<ViewMode
|
||||
class="input input--block"
|
||||
:viewMode.sync="viewMode"
|
||||
></ViewMode>
|
||||
<h3>{{ $t("settings.language") }}</h3>
|
||||
<languages
|
||||
class="input input--block"
|
||||
|
@ -75,11 +84,13 @@
|
|||
import { mapState, mapMutations } from "vuex";
|
||||
import { users as api } from "@/api";
|
||||
import Languages from "@/components/settings/Languages";
|
||||
import ViewMode from "@/components/settings/ViewMode";
|
||||
import i18n, { rtlLanguages } from "@/i18n";
|
||||
|
||||
export default {
|
||||
name: "settings",
|
||||
components: {
|
||||
ViewMode,
|
||||
Languages,
|
||||
},
|
||||
data: function () {
|
||||
|
@ -89,6 +100,8 @@ export default {
|
|||
hideDotfiles: false,
|
||||
singleClick: false,
|
||||
dateFormat: false,
|
||||
darkMode: false,
|
||||
viewMode: "list",
|
||||
locale: "",
|
||||
};
|
||||
},
|
||||
|
@ -109,8 +122,14 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
if (typeof this.user.darkMode === 'undefined') {
|
||||
this.darkMode = false;
|
||||
} else {
|
||||
this.darkMode = this.user.darkMode
|
||||
}
|
||||
this.setLoading(false);
|
||||
this.locale = this.user.locale;
|
||||
this.viewMode = this.user.viewMode;
|
||||
this.hideDotfiles = this.user.hideDotfiles;
|
||||
this.singleClick = this.user.singleClick;
|
||||
this.dateFormat = this.user.dateFormat;
|
||||
|
@ -135,11 +154,12 @@ export default {
|
|||
},
|
||||
async updateSettings(event) {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const data = {
|
||||
id: this.user.id,
|
||||
locale: this.locale,
|
||||
darkMode: this.darkMode,
|
||||
viewMode: this.viewMode,
|
||||
hideDotfiles: this.hideDotfiles,
|
||||
singleClick: this.singleClick,
|
||||
dateFormat: this.dateFormat,
|
||||
|
@ -149,6 +169,8 @@ export default {
|
|||
rtlLanguages.includes(i18n.locale);
|
||||
await api.update(data, [
|
||||
"locale",
|
||||
"darkMode",
|
||||
"viewMode",
|
||||
"hideDotfiles",
|
||||
"singleClick",
|
||||
"dateFormat",
|
||||
|
|
Loading…
Reference in New Issue