WebClient share: add a download page
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
61e6cc6985
commit
1a765c7ff7
11 changed files with 167 additions and 21 deletions
8
go.mod
8
go.mod
|
@ -10,10 +10,10 @@ require (
|
|||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
|
||||
github.com/aws/aws-sdk-go-v2 v1.23.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.2
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.1
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.4
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.8
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.9
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.18.2
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.2
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.23.2
|
||||
|
@ -74,7 +74,7 @@ require (
|
|||
golang.org/x/sys v0.14.0
|
||||
golang.org/x/term v0.14.0
|
||||
golang.org/x/time v0.4.0
|
||||
google.golang.org/api v0.150.0
|
||||
google.golang.org/api v0.151.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
|
@ -88,7 +88,7 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.3 // indirect
|
||||
|
|
16
go.sum
16
go.sum
|
@ -75,20 +75,20 @@ github.com/aws/aws-sdk-go-v2 v1.23.0 h1:PiHAzmiQQr6JULBUdvR8fKlA+UPKLT/8KbiqpFBW
|
|||
github.com/aws/aws-sdk-go-v2 v1.23.0/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1 h1:ZY3108YtBNq96jNZTICHxN1gSBSbnvIdYwwqnvCV4Mc=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1/go.mod h1:t8PYl/6LzdAqsU4/9tz28V/kU+asFePvpOMkdul0gEQ=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.1 h1:YsjngBOl2mx4l3egkVWndr6/6TqtkdsWJFZIsQ924Ek=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.1/go.mod h1:yV6h7TRVzhdIFmUk9WWDRpWwYGg1woEzKr0k1IYz2Tk=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.2 h1:+Gy7Xe372Tw/PiUw3We94Le9IwU1tmJqCD6cvI4oBJM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.2/go.mod h1:6hFlwWQiVOUG0Ej2ql0tG4zPlpDH++HD0WT1MA6l5Q4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.1 h1:WessyrdgyFN5TB+eLQdrFSlN/3oMnqukIFhDxK6z8h0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.1/go.mod h1:RQJyPxKcr+m4ArlIG1LUhMOrjposVfzbX6H8oR6oCgE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.4 h1:9wKDWEjwSnXZre0/O3+ZwbBl1SmlgWYBbrTV10X/H1s=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.4/go.mod h1:t4i+yGHMCcUNIX1x7YVYa6bH/Do7civ5I6cG/6PMfyA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.8 h1:wuOjvalpd2CnXffks74Vq6n3yv9vunKCoy4R1sjStGk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.8/go.mod h1:vywwjy6VnrR48Izg136JoSUXC4mH9QeUi3g0EH9DSrA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.9 h1:yG01Big4R5CDxftieMlgZPcHKZbwkRygur4DMGTqSzg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.9/go.mod h1:RV5gmgYb4psddWMPaf4giuGdsK1l0KwlXNFAbzWAIIo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.3 h1:DUwbD79T8gyQ23qVXFUthjzVMTviSHi3y4z58KvghhM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.3/go.mod h1:7sGSz1JCKHWWBHq98m6sMtWQikmYPpxjqOydDemiVoM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.3 h1:AplLJCtIaUZDCbr6+gLYdsYNxne4iuaboJhVt9d+WXI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.3/go.mod h1:ify42Rb7nKeDDPkFjKn7q1bPscVPu/+gmHH8d2c+anU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.0 h1:usgqiJtamuGIBj+OvYmMq89+Z1hIKkMJToz1WpoeNUY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.0/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.3 h1:lMwCXiWJlrtZot0NJTjbC8G9zl+V3i68gBTBBvDeEXA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.3/go.mod h1:5yzAuE9i2RkVAttBl8yxZgQr5OCq4D5yDnG7j9x2L0U=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 h1:rpkF4n0CyFcrJUG/rNNohoTmhtWlFTRI4BsZOh9PvLs=
|
||||
|
@ -773,8 +773,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
|||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE=
|
||||
google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
|
||||
google.golang.org/api v0.151.0 h1:FhfXLO/NFdJIzQtCqjpysWwqKk8AzGWBUhMIx67cVDU=
|
||||
google.golang.org/api v0.151.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
|
||||
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=
|
||||
|
|
|
@ -13745,6 +13745,20 @@ func TestWebClientShareCredentials(t *testing.T) {
|
|||
setJWTCookieForReq(req, cookie)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
// get the download page
|
||||
req, err = http.NewRequest(http.MethodGet, path.Join(webClientPubSharesPath, shareReadID, "download?a=b"), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RequestURI = uri
|
||||
setJWTCookieForReq(req, cookie)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
// get the download page for a missing share
|
||||
req, err = http.NewRequest(http.MethodGet, path.Join(webClientPubSharesPath, "invalidshareid", "download"), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RequestURI = uri
|
||||
setJWTCookieForReq(req, cookie)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusNotFound, rr)
|
||||
// the same cookie will not work for the other share
|
||||
req, err = http.NewRequest(http.MethodGet, path.Join(webClientPubSharesPath, shareWriteID, "browse"), nil)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -3476,6 +3476,41 @@ func TestUserQuotaUsage(t *testing.T) {
|
|||
assert.True(t, usage.IsTransferQuotaLow())
|
||||
}
|
||||
|
||||
func TestShareRedirectURL(t *testing.T) {
|
||||
shareID := util.GenerateUniqueID()
|
||||
base := path.Join(webClientPubSharesPath, shareID)
|
||||
next := path.Join(webClientPubSharesPath, shareID, "browse")
|
||||
ok, res := checkShareRedirectURL(next, base)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, next, res)
|
||||
next = path.Join(webClientPubSharesPath, shareID, "browse") + "?a=b"
|
||||
ok, res = checkShareRedirectURL(next, base)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, next, res)
|
||||
next = path.Join(webClientPubSharesPath, shareID)
|
||||
ok, res = checkShareRedirectURL(next, base)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, path.Join(base, "download"), res)
|
||||
next = path.Join(webClientEditFilePath, shareID)
|
||||
ok, res = checkShareRedirectURL(next, base)
|
||||
assert.False(t, ok)
|
||||
assert.Empty(t, res)
|
||||
next = path.Join(webClientPubSharesPath, shareID) + "?compress=false&a=b"
|
||||
ok, res = checkShareRedirectURL(next, base)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, path.Join(base, "download?compress=false&a=b"), res)
|
||||
next = path.Join(webClientPubSharesPath, shareID) + "?compress=true&b=c"
|
||||
ok, res = checkShareRedirectURL(next, base)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, path.Join(base, "download?compress=true&b=c"), res)
|
||||
ok, res = checkShareRedirectURL("http://foo\x7f.com/ab", "http://foo\x7f.com/")
|
||||
assert.False(t, ok)
|
||||
assert.Empty(t, res)
|
||||
ok, res = checkShareRedirectURL("http://foo.com/?foo\nbar", "http://foo.com")
|
||||
assert.False(t, ok)
|
||||
assert.Empty(t, res)
|
||||
}
|
||||
|
||||
func isSharedProviderSupported() bool {
|
||||
// SQLite shares the implementation with other SQL-based provider but it makes no sense
|
||||
// to use it outside test cases
|
||||
|
|
|
@ -1524,6 +1524,7 @@ func (s *httpdServer) setupWebClientRoutes() {
|
|||
s.router.Get(webClientPubSharesPath+"/{id}", s.downloadFromShare)
|
||||
s.router.Post(webClientPubSharesPath+"/{id}/partial", s.handleClientSharePartialDownload)
|
||||
s.router.Get(webClientPubSharesPath+"/{id}/browse", s.handleShareGetFiles)
|
||||
s.router.Get(webClientPubSharesPath+"/{id}/download", s.handleClientSharedFile)
|
||||
s.router.Get(webClientPubSharesPath+"/{id}/upload", s.handleClientUploadToShare)
|
||||
s.router.With(compressor.Handler).Get(webClientPubSharesPath+"/{id}/dirs", s.handleShareGetDirContents)
|
||||
s.router.Post(webClientPubSharesPath+"/{id}", s.uploadFilesToShare)
|
||||
|
|
|
@ -63,6 +63,7 @@ const (
|
|||
templateClientShares = "shares.html"
|
||||
templateClientViewPDF = "viewpdf.html"
|
||||
templateShareLogin = "sharelogin.html"
|
||||
templateShareDownload = "sharedownload.html"
|
||||
templateUploadToShare = "shareupload.html"
|
||||
pageClientFilesTitle = "Files"
|
||||
pageClientSharesTitle = "Shares"
|
||||
|
@ -74,6 +75,7 @@ const (
|
|||
pageClientResetPwdTitle = "SFTPGo WebClient - Reset password"
|
||||
pageExtShareTitle = "Shared files"
|
||||
pageUploadToShareTitle = "Upload to share"
|
||||
pageDownloadFromShareTitle = "Download shared file"
|
||||
)
|
||||
|
||||
// condResult is the result of an HTTP request precondition check.
|
||||
|
@ -174,6 +176,11 @@ type shareLoginPage struct {
|
|||
Branding UIBranding
|
||||
}
|
||||
|
||||
type shareDownloadPage struct {
|
||||
baseClientPage
|
||||
DownloadLink string
|
||||
}
|
||||
|
||||
type shareUploadPage struct {
|
||||
baseClientPage
|
||||
Share *dataprovider.Share
|
||||
|
@ -495,6 +502,11 @@ func loadClientTemplates(templatesPath string) {
|
|||
filepath.Join(templatesPath, templateClientDir, templateClientBase),
|
||||
filepath.Join(templatesPath, templateClientDir, templateUploadToShare),
|
||||
}
|
||||
shareDownloadPath := []string{
|
||||
filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
|
||||
filepath.Join(templatesPath, templateClientDir, templateClientBase),
|
||||
filepath.Join(templatesPath, templateClientDir, templateShareDownload),
|
||||
}
|
||||
|
||||
filesTmpl := util.LoadTemplate(nil, filesPaths...)
|
||||
profileTmpl := util.LoadTemplate(nil, profilePaths...)
|
||||
|
@ -512,6 +524,7 @@ func loadClientTemplates(templatesPath string) {
|
|||
resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
|
||||
viewPDFTmpl := util.LoadTemplate(nil, viewPDFPaths...)
|
||||
shareUploadTmpl := util.LoadTemplate(nil, shareUploadPath...)
|
||||
shareDownloadTmpl := util.LoadTemplate(nil, shareDownloadPath...)
|
||||
|
||||
clientTemplates[templateClientFiles] = filesTmpl
|
||||
clientTemplates[templateClientProfile] = profileTmpl
|
||||
|
@ -529,6 +542,7 @@ func loadClientTemplates(templatesPath string) {
|
|||
clientTemplates[templateClientViewPDF] = viewPDFTmpl
|
||||
clientTemplates[templateShareLogin] = shareLoginTmpl
|
||||
clientTemplates[templateUploadToShare] = shareUploadTmpl
|
||||
clientTemplates[templateShareDownload] = shareDownloadTmpl
|
||||
}
|
||||
|
||||
func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Request) baseClientPage {
|
||||
|
@ -780,6 +794,14 @@ func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Reque
|
|||
renderClientTemplate(w, templateClientFiles, data)
|
||||
}
|
||||
|
||||
func (s *httpdServer) renderShareDownloadPage(w http.ResponseWriter, r *http.Request, downloadLink string) {
|
||||
data := shareDownloadPage{
|
||||
baseClientPage: s.getBaseClientPageData(pageDownloadFromShareTitle, "", r),
|
||||
DownloadLink: downloadLink,
|
||||
}
|
||||
renderClientTemplate(w, templateShareDownload, data)
|
||||
}
|
||||
|
||||
func (s *httpdServer) renderUploadToSharePage(w http.ResponseWriter, r *http.Request, share dataprovider.Share) {
|
||||
currentURL := path.Join(webClientPubSharesPath, share.ShareID, "upload")
|
||||
data := shareUploadPage{
|
||||
|
@ -1799,15 +1821,53 @@ func (s *httpdServer) handleClientShareLoginPost(w http.ResponseWriter, r *http.
|
|||
return
|
||||
}
|
||||
next := path.Clean(r.URL.Query().Get("next"))
|
||||
if strings.HasPrefix(next, path.Join(webClientPubSharesPath, share.ShareID)) {
|
||||
http.Redirect(w, r, next, http.StatusFound)
|
||||
baseShareURL := path.Join(webClientPubSharesPath, share.ShareID)
|
||||
isRedirect, redirectTo := checkShareRedirectURL(next, baseShareURL)
|
||||
if isRedirect {
|
||||
http.Redirect(w, r, redirectTo, http.StatusFound)
|
||||
return
|
||||
}
|
||||
s.renderClientMessagePage(w, r, "Share Login OK", "Share login successful, you can now use your link",
|
||||
http.StatusOK, nil, "")
|
||||
}
|
||||
|
||||
func (s *httpdServer) handleClientSharedFile(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead}
|
||||
share, _, err := s.checkPublicShare(w, r, validScopes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
query := ""
|
||||
if r.URL.RawQuery != "" {
|
||||
query = "?" + r.URL.RawQuery
|
||||
}
|
||||
s.renderShareDownloadPage(w, r, path.Join(webClientPubSharesPath, share.ShareID)+query)
|
||||
}
|
||||
|
||||
func (s *httpdServer) handleClientPing(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
render.PlainText(w, r, "PONG")
|
||||
}
|
||||
|
||||
func checkShareRedirectURL(next, base string) (bool, string) {
|
||||
if !strings.HasPrefix(next, base) {
|
||||
return false, ""
|
||||
}
|
||||
if next == base {
|
||||
return true, path.Join(next, "download")
|
||||
}
|
||||
baseURL, err := url.Parse(base)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
nextURL, err := url.Parse(next)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
if nextURL.Path == baseURL.Path {
|
||||
redirectURL := nextURL.JoinPath("download")
|
||||
return true, redirectURL.String()
|
||||
}
|
||||
return true, next
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
let submitButton = document.querySelector('#sign_in_submit');
|
||||
submitButton.setAttribute('data-kt-indicator', 'on');
|
||||
submitButton.disabled = true;
|
||||
return true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -148,7 +148,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
let submitButton = document.querySelector('#form_submit');
|
||||
submitButton.setAttribute('data-kt-indicator', 'on');
|
||||
submitButton.disabled = true;
|
||||
return true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -220,7 +220,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
let submitButton = document.querySelector('#form_submit');
|
||||
submitButton.setAttribute('data-kt-indicator', 'on');
|
||||
submitButton.disabled = true;
|
||||
return true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
39
templates/webclient/sharedownload.html
Normal file
39
templates/webclient/sharedownload.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!--
|
||||
Copyright (C) 2023 Nicola Murino
|
||||
|
||||
This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
|
||||
|
||||
https://keenthemes.com/products/templates-mega-bundle
|
||||
|
||||
KeenThemes HTML/CSS/JS components are allowed for use only within the
|
||||
SFTPGo product and restricted to be used in a resealable HTML template
|
||||
that can compete with KeenThemes products anyhow.
|
||||
|
||||
This WebUI is allowed for use only within the SFTPGo product and
|
||||
therefore cannot be used in derivative works/products without an
|
||||
explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||
-->
|
||||
{{template "base" .}}
|
||||
{{- define "title"}}{{.Title}}{{- end}}
|
||||
{{- define "page_body"}}
|
||||
<div class="d-flex flex-center flex-column flex-column-fluid p-10 pb-lg-20">
|
||||
<div class="mb-12">
|
||||
<span>
|
||||
<img alt="Logo" src="{{.StaticURL}}{{.Branding.LogoPath}}" class="h-80px h-md-90px h-lg-100px" />
|
||||
</span>
|
||||
<span class="text-gray-900 fs-1 fw-bold ms-3 ps-5">
|
||||
{{.Branding.ShortName}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card shadow-sm w-lg-600px">
|
||||
<div class="card-header bg-light">
|
||||
<h3 class="card-title text-primary">Your download is ready</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<a href="{{.DownloadLink}}" class="btn btn-primary btn-user-custom btn-block">Download</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end}}
|
|
@ -171,10 +171,10 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
$('#expiredShare').hide();
|
||||
$('#writeShare').hide();
|
||||
$('#readShare').show();
|
||||
$('#readLink').attr("href", shareURL);
|
||||
$('#readLink').attr("title", shareURL);
|
||||
$('#readUncompressedLink').attr("href", shareURL + "?compress=false");
|
||||
$('#readUncompressedLink').attr("title", shareURL + "?compress=false");
|
||||
$('#readLink').attr("href", shareURL + "/download");
|
||||
$('#readLink').attr("title", shareURL + "/download");
|
||||
$('#readUncompressedLink').attr("href", shareURL + "/download?compress=false");
|
||||
$('#readUncompressedLink').attr("title", shareURL + "/download?compress=false");
|
||||
$('#readBrowseLink').attr("href", shareURL + "/browse");
|
||||
$('#readBrowseLink').attr("title", shareURL + "/browse");
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue