mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 00:50:31 +00:00
WebAdmin: allow to specify quota and upload size in human format
For example 1 GB Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
5eca73a399
commit
e58709c822
8 changed files with 158 additions and 50 deletions
4
go.mod
4
go.mod
|
@ -70,7 +70,7 @@ require (
|
|||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
|
||||
golang.org/x/sys v0.0.0-20220913175220-63ea55921009
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||
google.golang.org/api v0.95.0
|
||||
google.golang.org/api v0.96.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
|
||||
|
@ -156,7 +156,7 @@ require (
|
|||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
9
go.sum
9
go.sum
|
@ -976,7 +976,6 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1123,8 +1122,8 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
|
|||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/api v0.95.0 h1:d1c24AAS01DYqXreBeuVV7ewY/U8Mnhh47pwtsgVtYg=
|
||||
google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
|
||||
google.golang.org/api v0.96.0 h1:F60cuQPJq7K7FzsxMYHAUJSiXh2oKctHxBMbDygxhfM=
|
||||
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -1229,8 +1228,8 @@ google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP
|
|||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5 h1:ou3VRVAif8UJqz3l1r4Isoz7rrUWHWDHBonShMNYoQs=
|
||||
google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f h1:wwbo0UziciPT4Dsca+bmplW53QNAl7tiUOw7FfAcsf8=
|
||||
google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
|
|
@ -16954,7 +16954,7 @@ func TestWebUserAddMock(t *testing.T) {
|
|||
req.Header.Set("Content-Type", contentType)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
form.Set("max_upload_file_size", "1000")
|
||||
form.Set("max_upload_file_size", "1 KB")
|
||||
// test invalid default shares expiration
|
||||
form.Set("default_shares_expiration", "a")
|
||||
b, contentType, _ = getMultipartFormData(form, "", "")
|
||||
|
@ -17277,7 +17277,7 @@ func TestWebUserUpdateMock(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
user.MaxSessions = 1
|
||||
user.QuotaFiles = 2
|
||||
user.QuotaSize = 3
|
||||
user.QuotaSize = 1000 * 1000 * 1000
|
||||
user.GID = 1000
|
||||
user.Filters.AllowAPIKeyAuth = true
|
||||
user.AdditionalInfo = "new additional info"
|
||||
|
@ -17291,7 +17291,7 @@ func TestWebUserUpdateMock(t *testing.T) {
|
|||
form.Set("uid", "0")
|
||||
form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
|
||||
form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
|
||||
form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
|
||||
form.Set("quota_size", "1 GB")
|
||||
form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
|
||||
form.Set("upload_bandwidth", "0")
|
||||
form.Set("download_bandwidth", "0")
|
||||
|
|
|
@ -482,6 +482,7 @@ func loadAdminTemplates(templatesPath string) {
|
|||
sdk.SFTPFilesystemProvider, sdk.HTTPFilesystemProvider,
|
||||
}
|
||||
},
|
||||
"HumanizeBytes": util.ByteCountSI,
|
||||
})
|
||||
usersTmpl := util.LoadTemplate(nil, usersPaths...)
|
||||
userTmpl := util.LoadTemplate(fsBaseTpl, userPaths...)
|
||||
|
@ -1079,7 +1080,7 @@ func getVirtualFoldersFromPostFields(r *http.Request) []vfs.VirtualFolder {
|
|||
QuotaSize: -1,
|
||||
}
|
||||
if len(folderQuotaSizes) > idx {
|
||||
quotaSize, err := strconv.ParseInt(strings.TrimSpace(folderQuotaSizes[idx]), 10, 64)
|
||||
quotaSize, err := util.ParseBytes(folderQuotaSizes[idx])
|
||||
if err == nil {
|
||||
vfolder.QuotaSize = quotaSize
|
||||
}
|
||||
|
@ -1305,7 +1306,7 @@ func getFiltersFromUserPostFields(r *http.Request) (sdk.BaseUserFilters, error)
|
|||
if err != nil {
|
||||
return filters, err
|
||||
}
|
||||
maxFileSize, err := strconv.ParseInt(r.Form.Get("max_upload_file_size"), 10, 64)
|
||||
maxFileSize, err := util.ParseBytes(r.Form.Get("max_upload_file_size"))
|
||||
if err != nil {
|
||||
return filters, fmt.Errorf("invalid max upload file size: %w", err)
|
||||
}
|
||||
|
@ -1722,7 +1723,7 @@ func getTransferLimits(r *http.Request) (int64, int64, int64, error) {
|
|||
}
|
||||
|
||||
func getQuotaLimits(r *http.Request) (int64, int, error) {
|
||||
quotaSize, err := strconv.ParseInt(r.Form.Get("quota_size"), 10, 64)
|
||||
quotaSize, err := util.ParseBytes(r.Form.Get("quota_size"))
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("invalid quota size: %w", err)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -37,8 +38,10 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lithammer/shortuuid/v3"
|
||||
|
@ -59,6 +62,59 @@ var (
|
|||
additionalSharedDataSearchPath = ""
|
||||
)
|
||||
|
||||
// IEC Sizes.
|
||||
// kibis of bits
|
||||
const (
|
||||
oneByte = 1 << (iota * 10)
|
||||
kiByte
|
||||
miByte
|
||||
giByte
|
||||
tiByte
|
||||
piByte
|
||||
eiByte
|
||||
)
|
||||
|
||||
// SI Sizes.
|
||||
const (
|
||||
iByte = 1
|
||||
kbByte = iByte * 1000
|
||||
mByte = kbByte * 1000
|
||||
gByte = mByte * 1000
|
||||
tByte = gByte * 1000
|
||||
pByte = tByte * 1000
|
||||
eByte = pByte * 1000
|
||||
)
|
||||
|
||||
var bytesSizeTable = map[string]uint64{
|
||||
"b": oneByte,
|
||||
"kib": kiByte,
|
||||
"kb": kbByte,
|
||||
"mib": miByte,
|
||||
"mb": mByte,
|
||||
"gib": giByte,
|
||||
"gb": gByte,
|
||||
"tib": tiByte,
|
||||
"tb": tByte,
|
||||
"pib": piByte,
|
||||
"pb": pByte,
|
||||
"eib": eiByte,
|
||||
"eb": eByte,
|
||||
// Without suffix
|
||||
"": oneByte,
|
||||
"ki": kiByte,
|
||||
"k": kbByte,
|
||||
"mi": miByte,
|
||||
"m": mByte,
|
||||
"gi": giByte,
|
||||
"g": gByte,
|
||||
"ti": tiByte,
|
||||
"t": tByte,
|
||||
"pi": piByte,
|
||||
"p": pByte,
|
||||
"ei": eiByte,
|
||||
"e": eByte,
|
||||
}
|
||||
|
||||
// Contains reports whether v is present in elems.
|
||||
func Contains[T comparable](elems []T, v T) bool {
|
||||
for _, s := range elems {
|
||||
|
@ -135,6 +191,9 @@ func ByteCountIEC(b int64) string {
|
|||
}
|
||||
|
||||
func byteCount(b int64, unit int64) string {
|
||||
if b <= 0 {
|
||||
return strconv.FormatInt(b, 10)
|
||||
}
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
|
@ -143,12 +202,61 @@ func byteCount(b int64, unit int64) string {
|
|||
div *= unit
|
||||
exp++
|
||||
}
|
||||
val := strconv.FormatFloat(float64(b)/float64(div), 'f', -1, 64)
|
||||
if unit == 1000 {
|
||||
return fmt.Sprintf("%.1f %cB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
return fmt.Sprintf("%s %cB", val, "KMGTPE"[exp])
|
||||
}
|
||||
return fmt.Sprintf("%.1f %ciB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
return fmt.Sprintf("%s %ciB", val, "KMGTPE"[exp])
|
||||
}
|
||||
|
||||
// ParseBytes parses a string representation of bytes into the number
|
||||
// of bytes it represents.
|
||||
//
|
||||
// ParseBytes("42 MB") -> 42000000, nil
|
||||
// ParseBytes("42 mib") -> 44040192, nil
|
||||
//
|
||||
// copied from here:
|
||||
//
|
||||
// https://github.com/dustin/go-humanize/blob/master/bytes.go
|
||||
//
|
||||
// with minor modifications
|
||||
func ParseBytes(s string) (int64, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(num, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
||||
if m, ok := bytesSizeTable[extra]; ok {
|
||||
f *= float64(m)
|
||||
if f >= math.MaxInt64 {
|
||||
return 0, fmt.Errorf("value too large: %v", s)
|
||||
}
|
||||
if f < 0 {
|
||||
return 0, fmt.Errorf("negative value not allowed: %v", s)
|
||||
}
|
||||
return int64(f), nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unhandled size name: %v", extra)
|
||||
}
|
||||
|
||||
// GetIPFromRemoteAddress returns the IP from the remote address.
|
||||
|
|
|
@ -64,7 +64,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<b>Virtual folders</b>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-4">Quota -1 means included within user quota, 0 unlimited. Don't set -1 for shared folders</h6>
|
||||
<h6 class="card-title mb-4">Quota size -1 means included within user quota, 0 unlimited. Don't set -1 for shared folders. You can use MB/GB/TB suffix. With no suffix we assume bytes</h6>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 form_field_vfolders_outer">
|
||||
{{range $idx, $val := .Group.VirtualFolders}}
|
||||
|
@ -81,10 +81,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="number" class="form-control" id="idVfolderQuotaSize{{$idx}}" name="vfolder_quota_size"
|
||||
value="{{$val.QuotaSize}}" min="-1" aria-describedby="vqsHelpBlock{{$idx}}">
|
||||
<input type="text" class="form-control" id="idVfolderQuotaSize{{$idx}}" name="vfolder_quota_size"
|
||||
value="{{HumanizeBytes $val.QuotaSize}}" aria-describedby="vqsHelpBlock{{$idx}}">
|
||||
<small id="vqsHelpBlock{{$idx}}" class="form-text text-muted">
|
||||
Quota size (bytes)
|
||||
Quota size
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
|
@ -114,10 +114,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="number" class="form-control" id="idVfolderQuotaSize0" name="vfolder_quota_size"
|
||||
value="" min="-1" aria-describedby="vqsHelpBlock0">
|
||||
<input type="text" class="form-control" id="idVfolderQuotaSize0" name="vfolder_quota_size"
|
||||
value="" aria-describedby="vqsHelpBlock0">
|
||||
<small id="vqsHelpBlock0" class="form-text text-muted">
|
||||
Quota size (bytes)
|
||||
Quota size
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
|
@ -386,12 +386,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="idQuotaSize" class="col-sm-2 col-form-label">Quota size (bytes)</label>
|
||||
<label for="idQuotaSize" class="col-sm-2 col-form-label">Quota size</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" class="form-control" id="idQuotaSize" name="quota_size" placeholder=""
|
||||
value="{{.Group.UserSettings.QuotaSize}}" min="0" aria-describedby="qsHelpBlock">
|
||||
<input type="text" class="form-control" id="idQuotaSize" name="quota_size" placeholder=""
|
||||
value="{{HumanizeBytes .Group.UserSettings.QuotaSize}}" aria-describedby="qsHelpBlock">
|
||||
<small id="qsHelpBlock" class="form-text text-muted">
|
||||
0 means no limit
|
||||
0 means no limit. You can use MB/GB/TB suffix
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-sm-2"></div>
|
||||
|
@ -406,13 +406,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="idMaxUploadSize" class="col-sm-2 col-form-label">Max file upload size (bytes)</label>
|
||||
<label for="idMaxUploadSize" class="col-sm-2 col-form-label">Max file upload size</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" id="idMaxUploadSize" name="max_upload_file_size"
|
||||
placeholder="" value="{{.Group.UserSettings.Filters.MaxUploadFileSize}}" min="0"
|
||||
<input type="text" class="form-control" id="idMaxUploadSize" name="max_upload_file_size"
|
||||
placeholder="" value="{{HumanizeBytes .Group.UserSettings.Filters.MaxUploadFileSize}}"
|
||||
aria-describedby="fqsHelpBlock">
|
||||
<small id="fqsHelpBlock" class="form-text text-muted">
|
||||
Maximum upload size for a single file. 0 means no limit
|
||||
Maximum upload size for a single file. 0 means no limit. You can use MB/GB/TB suffix
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -63,10 +63,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="number" class="form-control" id="idVfolderQuotaSize${index}" name="vfolder_quota_size"
|
||||
value="" min="-1" aria-describedby="vqsHelpBlock${index}">
|
||||
<input type="text" class="form-control" id="idVfolderQuotaSize${index}" name="vfolder_quota_size"
|
||||
value="" aria-describedby="vqsHelpBlock${index}">
|
||||
<small id="vqsHelpBlock${index}" class="form-text text-muted">
|
||||
Quota size (bytes)
|
||||
Quota size
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
|
|
|
@ -203,7 +203,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<b>Virtual folders</b>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-4">Quota -1 means included within user quota, 0 unlimited. Don't set -1 for shared folders</h6>
|
||||
<h6 class="card-title mb-4">Quota size -1 means included within user quota, 0 unlimited. Don't set -1 for shared folders. You can use MB/GB/TB suffix. With no suffix we assume bytes</h6>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 form_field_vfolders_outer">
|
||||
{{range $idx, $val := .User.VirtualFolders}}
|
||||
|
@ -220,10 +220,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="number" class="form-control" id="idVfolderQuotaSize{{$idx}}" name="vfolder_quota_size"
|
||||
value="{{$val.QuotaSize}}" min="-1" aria-describedby="vqsHelpBlock{{$idx}}">
|
||||
<input type="text" class="form-control" id="idVfolderQuotaSize{{$idx}}" name="vfolder_quota_size"
|
||||
value="{{HumanizeBytes $val.QuotaSize}}" aria-describedby="vqsHelpBlock{{$idx}}">
|
||||
<small id="vqsHelpBlock{{$idx}}" class="form-text text-muted">
|
||||
Quota size (bytes)
|
||||
Quota size
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
|
@ -253,10 +253,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="number" class="form-control" id="idVfolderQuotaSize0" name="vfolder_quota_size"
|
||||
value="" min="-1" aria-describedby="vqsHelpBlock0">
|
||||
<input type="text" class="form-control" id="idVfolderQuotaSize0" name="vfolder_quota_size"
|
||||
value="" aria-describedby="vqsHelpBlock0">
|
||||
<small id="vqsHelpBlock0" class="form-text text-muted">
|
||||
Quota size (bytes)
|
||||
Quota size
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
|
@ -603,12 +603,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="idQuotaSize" class="col-sm-2 col-form-label">Quota size (bytes)</label>
|
||||
<label for="idQuotaSize" class="col-sm-2 col-form-label">Quota size</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="number" class="form-control" id="idQuotaSize" name="quota_size" placeholder=""
|
||||
value="{{.User.QuotaSize}}" min="0" aria-describedby="qsHelpBlock">
|
||||
<input type="text" class="form-control" id="idQuotaSize" name="quota_size" placeholder=""
|
||||
value="{{HumanizeBytes .User.QuotaSize}}" aria-describedby="qsHelpBlock">
|
||||
<small id="qsHelpBlock" class="form-text text-muted">
|
||||
0 means no limit
|
||||
0 means no limit. You can use MB/GB/TB suffix
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-sm-2"></div>
|
||||
|
@ -623,13 +623,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="idMaxUploadSize" class="col-sm-2 col-form-label">Max file upload size (bytes)</label>
|
||||
<label for="idMaxUploadSize" class="col-sm-2 col-form-label">Max file upload size</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" id="idMaxUploadSize" name="max_upload_file_size"
|
||||
placeholder="" value="{{.User.Filters.MaxUploadFileSize}}" min="0"
|
||||
<input type="text" class="form-control" id="idMaxUploadSize" name="max_upload_file_size"
|
||||
placeholder="" value="{{HumanizeBytes .User.Filters.MaxUploadFileSize}}"
|
||||
aria-describedby="fqsHelpBlock">
|
||||
<small id="fqsHelpBlock" class="form-text text-muted">
|
||||
Maximum upload size for a single file. 0 means no limit
|
||||
Maximum upload size for a single file. 0 means no limit. You can use MB/GB/TB suffix
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue