mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
add copy permission
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
e5fc1bd574
commit
51ae2d7301
12 changed files with 95 additions and 21 deletions
|
@ -37,7 +37,7 @@ SFTPGo supports the following built-in SSH commands:
|
|||
- `scp`, SFTPGo implements the SCP protocol so we can support it for cloud filesystems too and we can avoid the other system commands limitations. SCP between two remote hosts is supported using the `-3` scp option. Wildcard expansion is not supported.
|
||||
- `md5sum`, `sha1sum`, `sha256sum`, `sha384sum`, `sha512sum`. Useful to check message digests for uploaded files.
|
||||
- `cd`, `pwd`. Some SFTP clients do not support the SFTP SSH_FXP_REALPATH packet type, so they use `cd` and `pwd` SSH commands to get the initial directory. Currently `cd` does nothing and `pwd` always returns the `/` path. These commands will work with any storage backend but keep in mind that to calculate the hash we need to read the whole file, for remote backends this means downloading the file, for the encrypted backend this means decrypting the file.
|
||||
- `sftpgo-copy`. This is a built-in copy implementation. It allows server side copy for files and directories. The first argument is the source file/directory and the second one is the destination file/directory, for example `sftpgo-copy <src> <dst>`. :warning: Copying directories that span virtual folders is supported but, for Cloud Storage filesystems, the remote copy API is not currently used.
|
||||
- `sftpgo-copy`. This is a built-in copy implementation. It allows server side copy for files and directories. The first argument is the source file/directory and the second one is the destination file/directory, for example `sftpgo-copy <src> <dst>`.
|
||||
- `sftpgo-remove`. This is a built-in remove implementation. It allows to remove single files and to recursively remove directories. The first argument is the file/directory to remove, for example `sftpgo-remove <dst>`. Removing directories spanning virtual folders is not supported.
|
||||
|
||||
The following SSH commands are enabled by default:
|
||||
|
|
8
go.mod
8
go.mod
|
@ -13,9 +13,9 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/config v1.27.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.1
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.1
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.0
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.1
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
|
@ -39,7 +39,7 @@ require (
|
|||
github.com/jackc/pgx/v5 v5.5.3
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/klauspost/compress v1.17.6
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.19
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.20
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/mhale/smtpd v0.8.2
|
||||
|
@ -144,7 +144,7 @@ require (
|
|||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.47.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
|
|
16
go.sum
16
go.sum
|
@ -43,8 +43,8 @@ github.com/aws/aws-sdk-go-v2/credentials v1.17.0 h1:lMW2x6sKBsiAJrpi1doOXqWFyEPo
|
|||
github.com/aws/aws-sdk-go-v2/credentials v1.17.0/go.mod h1:uT41FIH8cCIxOdUYIL0PYyHlL1NoneDuDSCwg5VE/5o=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0 h1:xWCwjjvVz2ojYTP4kBKUuUh9ZrXfcAXpflhOUUeXg1k=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0/go.mod h1:j3fACuqXg4oMTQOR2yY7m0NmJY0yBK4L4sLsRXq1Ins=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.1 h1:FqtJUSBgT2yfZ8kZhTi9AO131qMLOzb4MiH4riAM8XM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.1/go.mod h1:G3V4qNUPMHKrXW/l149QXmHjf1vlMWBO4UuGPCK4a/c=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2 h1:VEekE/fJWqAWYozxFQ07B+h8NdvTPAYhV13xIBenuO0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2/go.mod h1:8vozqAHmDNmoD4YbuDKIfpnLbByzngczL4My1RELLVo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 h1:NPs/EqVO+ajwOoq56EfcGKa3L3ruWuazkIw1BqxwOPw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0/go.mod h1:D+duLy2ylgatV+yTlQ8JTuLfDD0BnFvnQRc+o6tbZ4M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 h1:ks7KGMVUMoDzcxNWUlEdI+/lokMFD136EL6DWmUOV80=
|
||||
|
@ -63,8 +63,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 h1:l5puwOHr7IxECu
|
|||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0/go.mod h1:Oov79flWa/n7Ni+lQC3z+VM7PoRM47omRqbJU9B5Y7E=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.1 h1:eHNChn4Sp+g1hdz4rkx96n1l/LpJEQLDuFB0V+fA/yg=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.1/go.mod h1:9ev55pJx9xNX3UAOKzZmbmaTbwwuLTCemOJPsd7rUz8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.0 h1:jZAdMD1ioZdqirzzVVRhpHHWJmcGGCn8JqDYBs5nmYA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.0/go.mod h1:1o/W6JFUuREj2ExoQ21vHJgO7wakvjhol91M9eknFgs=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1 h1:bjpWJEXch7moIt3PX2r5XpGROsletl7enqG1Q3Te1Dc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1/go.mod h1:1o/W6JFUuREj2ExoQ21vHJgO7wakvjhol91M9eknFgs=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.1 h1:ss/HbHbONu0uscM549++4YanT6MnjNN0BGhE5pZRfG4=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.1/go.mod h1:JsJDZFHwLGZu6dxhV9EV1gJrMnCeE4GEXubSZA59xdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 h1:u6OkVDxtBPnxPkZ9/63ynEe+8kHbtS5IfaC4PzVxzWM=
|
||||
|
@ -270,8 +270,8 @@ github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJG
|
|||
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.20 h1:sAgXuWS/t8ykxS9Bi2Qtn5Qhpakw1wrcjxChudjolCc=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.20/go.mod h1:UlCSmKqw+agm5BsOBfEAbTvKsEApaGNqHAEUTv5PJC4=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
|
@ -322,8 +322,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a h1:XCUtNgBnZfUBhdfCX2QK+fslr9vevSsUg3W3peZwlak=
|
||||
github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
|
|
|
@ -612,6 +612,9 @@ func (c *BaseConnection) checkCopy(srcInfo, dstInfo os.FileInfo, virtualSource,
|
|||
}
|
||||
|
||||
func (c *BaseConnection) copyFile(virtualSourcePath, virtualTargetPath string, srcSize int64) error {
|
||||
if !c.User.HasPerm(dataprovider.PermCopy, virtualSourcePath) || !c.User.HasPerm(dataprovider.PermCopy, virtualTargetPath) {
|
||||
return c.GetPermissionDeniedError()
|
||||
}
|
||||
if ok, _ := c.User.IsFileAllowed(virtualTargetPath); !ok {
|
||||
return fmt.Errorf("file %q is not allowed: %w", virtualTargetPath, c.GetPermissionDeniedError())
|
||||
}
|
||||
|
|
|
@ -8601,6 +8601,13 @@ func TestCopyAndRemovePermissions(t *testing.T) {
|
|||
user.Permissions[testDir] = []string{dataprovider.PermListItems}
|
||||
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
// no copy permission
|
||||
out, err = runSSHCommand(fmt.Sprintf(`sftpgo-copy %s %s`, testFileName, restrictedPath), user)
|
||||
assert.Error(t, err, string(out))
|
||||
user.Permissions[restrictedPath] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermCopy}
|
||||
user.Permissions[testDir] = []string{dataprovider.PermListItems, dataprovider.PermCopy}
|
||||
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
out, err = runSSHCommand(fmt.Sprintf(`sftpgo-copy %s %s`, testFileName, restrictedPath), user)
|
||||
assert.NoError(t, err, string(out))
|
||||
// overwrite will fail, no permission
|
||||
|
|
|
@ -160,8 +160,8 @@ var (
|
|||
BoltDataProviderName, MemoryDataProviderName, CockroachDataProviderName}
|
||||
// ValidPerms defines all the valid permissions for a user
|
||||
ValidPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermOverwrite, PermCreateDirs, PermRename,
|
||||
PermRenameFiles, PermRenameDirs, PermDelete, PermDeleteFiles, PermDeleteDirs, PermCreateSymlinks, PermChmod,
|
||||
PermChown, PermChtimes}
|
||||
PermRenameFiles, PermRenameDirs, PermDelete, PermDeleteFiles, PermDeleteDirs, PermCopy, PermCreateSymlinks,
|
||||
PermChmod, PermChown, PermChtimes}
|
||||
// ValidLoginMethods defines all the valid login methods
|
||||
ValidLoginMethods = []string{SSHLoginMethodPublicKey, LoginMethodPassword, SSHLoginMethodPassword,
|
||||
SSHLoginMethodKeyboardInteractive, SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt,
|
||||
|
|
|
@ -72,6 +72,8 @@ const (
|
|||
PermChown = "chown"
|
||||
// changing file or directory access and modification time is allowed
|
||||
PermChtimes = "chtimes"
|
||||
// copying files or directories is allowed
|
||||
PermCopy = "copy"
|
||||
)
|
||||
|
||||
// Available login methods
|
||||
|
@ -1113,6 +1115,21 @@ func (u *User) CanDeleteFromWeb(target string) bool {
|
|||
return u.HasAnyPerm(permsDeleteAny, target)
|
||||
}
|
||||
|
||||
// CanCopyFromWeb returns true if the client can copy objects from the web UI.
|
||||
// The specified src and dest are the source and target directories for the copy.
|
||||
func (u *User) CanCopyFromWeb(src, dest string) bool {
|
||||
if util.Contains(u.Filters.WebClient, sdk.WebClientWriteDisabled) {
|
||||
return false
|
||||
}
|
||||
if !u.HasPerm(PermListItems, src) {
|
||||
return false
|
||||
}
|
||||
if !u.HasPerm(PermDownload, src) {
|
||||
return false
|
||||
}
|
||||
return u.HasPerm(PermCopy, src) && u.HasPerm(PermCopy, dest)
|
||||
}
|
||||
|
||||
// PasswordExpiresIn returns the number of days before the password expires.
|
||||
// The returned value is negative if the password is expired.
|
||||
// The caller must ensure that a PasswordExpiration is set
|
||||
|
|
|
@ -16223,6 +16223,21 @@ func TestRenameDifferentResource(t *testing.T) {
|
|||
setBearerForReq(req, webAPIToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
assert.Contains(t, rr.Body.String(), "Cannot perform copy step")
|
||||
|
||||
u.Permissions = map[string][]string{
|
||||
"/": {dataprovider.PermUpload, dataprovider.PermListItems, dataprovider.PermCreateDirs,
|
||||
dataprovider.PermDownload, dataprovider.PermOverwrite, dataprovider.PermCopy},
|
||||
}
|
||||
_, resp, err = httpdtest.UpdateUser(u, http.StatusOK, "")
|
||||
assert.NoError(t, err, string(resp))
|
||||
webAPIToken, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodPost, userFileActionsPath+"/move?path="+testFileName+"&target="+url.QueryEscape(path.Join("/", "folderPath", testFileName)), nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, webAPIToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
assert.Contains(t, rr.Body.String(), "Cannot perform remove step")
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
|
|
|
@ -135,6 +135,7 @@ type filesPage struct {
|
|||
CanDelete bool
|
||||
CanDownload bool
|
||||
CanShare bool
|
||||
CanCopy bool
|
||||
ShareUploadBaseURL string
|
||||
Error *util.I18nError
|
||||
Paths []dirMapping
|
||||
|
@ -755,6 +756,7 @@ func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Reque
|
|||
CanDelete: false,
|
||||
CanDownload: share.Scope != dataprovider.ShareScopeWrite,
|
||||
CanShare: false,
|
||||
CanCopy: false,
|
||||
Paths: getDirMapping(dirName, currentURL),
|
||||
QuotaUsage: newUserQuotaUsage(&dataprovider.User{}),
|
||||
}
|
||||
|
@ -797,6 +799,7 @@ func (s *httpdServer) renderFilesPage(w http.ResponseWriter, r *http.Request, di
|
|||
CanDelete: user.CanDeleteFromWeb(dirName),
|
||||
CanDownload: user.HasPerm(dataprovider.PermDownload, dirName),
|
||||
CanShare: user.CanManageShares(),
|
||||
CanCopy: user.CanCopyFromWeb(dirName, dirName),
|
||||
ShareUploadBaseURL: "",
|
||||
Paths: getDirMapping(dirName, webClientFilesPath),
|
||||
QuotaUsage: newUserQuotaUsage(user),
|
||||
|
|
|
@ -9280,7 +9280,7 @@ func TestSSHCopyPermissions(t *testing.T) {
|
|||
u := getTestUser(usePubKey)
|
||||
u.Permissions["/dir1"] = []string{dataprovider.PermUpload, dataprovider.PermDownload, dataprovider.PermListItems}
|
||||
u.Permissions["/dir2"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDownload,
|
||||
dataprovider.PermListItems}
|
||||
dataprovider.PermListItems, dataprovider.PermCopy}
|
||||
u.Permissions["/dir3"] = []string{dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks, dataprovider.PermDownload,
|
||||
dataprovider.PermListItems}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
|
|
|
@ -4904,6 +4904,7 @@ components:
|
|||
- chmod
|
||||
- chown
|
||||
- chtimes
|
||||
- copy
|
||||
description: |
|
||||
Permissions:
|
||||
* `*` - all permissions are granted
|
||||
|
@ -4922,6 +4923,7 @@ components:
|
|||
* `chmod` changing file or directory permissions is allowed
|
||||
* `chown` changing file or directory owner and group is allowed
|
||||
* `chtimes` changing file or directory access and modification time is allowed
|
||||
* `copy`, copying files or directories is allowed
|
||||
AdminPermissions:
|
||||
type: string
|
||||
enum:
|
||||
|
|
|
@ -17,6 +17,20 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
|
||||
{{- define "page_body"}}
|
||||
{{- template "errmsg" .Error}}
|
||||
|
||||
{{- $move_copy_msg := ""}}
|
||||
{{- if and .CanRename .CanCopy}}
|
||||
{{- $move_copy_msg = "fs.move_copy"}}
|
||||
{{- else}}
|
||||
{{- if .CanRename}}
|
||||
{{- $move_copy_msg = "fs.move.msg"}}
|
||||
{{- else}}
|
||||
{{- if .CanCopy}}
|
||||
{{- $move_copy_msg = "fs.copy.msg"}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
<div class="card card-flush shadow-sm">
|
||||
<div class="card-header pt-8">
|
||||
<div class="card-title">
|
||||
|
@ -91,7 +105,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
{{- if not .ShareUploadBaseURL}}
|
||||
{{- if or .CanRename .CanAddFiles}}
|
||||
<div class="menu-item px-3">
|
||||
<a data-i18n="fs.move_copy" href="#" class="menu-link px-3 fs-6" data-kt-filemanager-table-select="move_or_copy_selected">
|
||||
<a data-i18n="{{$move_copy_msg}}" href="#" class="menu-link px-3 fs-6" data-kt-filemanager-table-select="move_or_copy_selected">
|
||||
Move or copy
|
||||
</a>
|
||||
</div>
|
||||
|
@ -208,6 +222,19 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/glightbox/glightbox.min.js"></script>
|
||||
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/pdfobject/pdfobject.min.js"></script>
|
||||
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
|
||||
//{{- $move_copy_msg := ""}}
|
||||
//{{- if and .CanRename .CanCopy}}
|
||||
//{{- $move_copy_msg = "fs.move_copy"}}
|
||||
//{{- else}}
|
||||
//{{- if .CanRename}}
|
||||
//{{- $move_copy_msg = "fs.move.msg"}}
|
||||
//{{- else}}
|
||||
//{{- if .CanCopy}}
|
||||
//{{- $move_copy_msg = "fs.copy.msg"}}
|
||||
//{{- end}}
|
||||
//{{- end}}
|
||||
//{{- end}}
|
||||
|
||||
//{{- if not .ShareUploadBaseURL}}
|
||||
const supportedEditExtensions = ["csv", "bat", "dyalog", "apl", "asc", "pgp", "sig", "asn", "asn1", "b", "bf",
|
||||
"c", "h", "ino", "cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx", "cob", "cpy", "cbl", "cs", "clj",
|
||||
|
@ -673,9 +700,9 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
<a data-i18n="general.rename" href="#" class="menu-link px-3" data-kt-filemanager-table-action="rename">Rename</a>
|
||||
</div>
|
||||
{{- end}}
|
||||
{{- if or .CanRename .CanAddFiles}}
|
||||
{{- if or .CanRename .CanCopy}}
|
||||
<div class="menu-item px-3">
|
||||
<a data-i18n="fs.move_copy" href="#" class="menu-link px-3" data-kt-filemanager-table-action="move_or_copy">Move or copy</a>
|
||||
<a data-i18n="{{$move_copy_msg}}" href="#" class="menu-link px-3" data-kt-filemanager-table-action="move_or_copy">Move or copy</a>
|
||||
</div>
|
||||
{{- end}}
|
||||
{{- if .CanShare}}
|
||||
|
@ -2398,8 +2425,8 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
|
||||
</div>
|
||||
<div class="modal-footer border-0">
|
||||
{{- if .CanAddFiles }}
|
||||
<button id="id_copy_button" type="button" class="btn btn-light-primary me-5" data-bs-dismiss="modal">
|
||||
{{- if .CanCopy }}
|
||||
<button id="id_copy_button" type="button" class="btn {{if .CanRename}}btn-light-primary me-5{{else}}btn-primary{{end}}" data-bs-dismiss="modal">
|
||||
<span data-i18n="fs.copy.msg">Copy</span>
|
||||
</button>
|
||||
{{- end}}
|
||||
|
|
Loading…
Reference in a new issue