mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-10 12:49:52 +01:00
[GITEA] Configurable clone methods
Adds `[repository].DOWNLOAD_OR_CLONE_METHODS` (defaulting to "download-zip,download-targz,download-bundle,vscode-clone"), which lets an instance administrator override the additional clone methods displayed on the repository home view. This is purely display-only, the clone methods not listed here are still available, unless disabled elsewhere. They're just not displayed. Fixes #710. Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu> (cherry picked from commit2aadcf4946
) (cherry picked from commit42ac34fbf9
) (cherry picked from commitbd231b0245
) (cherry picked from commit3d3366dbbe
)
This commit is contained in:
parent
1d5153aaf6
commit
0157fb9b88
6 changed files with 118 additions and 7 deletions
|
@ -7,6 +7,7 @@ import (
|
|||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -19,6 +20,8 @@ const (
|
|||
RepoCreatingPublic = "public"
|
||||
)
|
||||
|
||||
var RecognisedRepositoryDownloadOrCloneMethods = []string{"download-zip", "download-targz", "download-bundle", "vscode-clone", "vscodium-clone", "cite"}
|
||||
|
||||
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
|
||||
const ItemsPerPage = 40
|
||||
|
||||
|
@ -43,6 +46,7 @@ var (
|
|||
DisabledRepoUnits []string
|
||||
DefaultRepoUnits []string
|
||||
DefaultForkRepoUnits []string
|
||||
DownloadOrCloneMethods []string
|
||||
PrefixArchiveFiles bool
|
||||
DisableMigrations bool
|
||||
DisableStars bool `ini:"DISABLE_STARS"`
|
||||
|
@ -161,6 +165,7 @@ var (
|
|||
DisabledRepoUnits: []string{},
|
||||
DefaultRepoUnits: []string{},
|
||||
DefaultForkRepoUnits: []string{},
|
||||
DownloadOrCloneMethods: []string{"download-zip", "download-targz", "download-bundle", "vscode-clone"},
|
||||
PrefixArchiveFiles: true,
|
||||
DisableMigrations: false,
|
||||
DisableStars: false,
|
||||
|
@ -361,4 +366,10 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
|
|||
if err := loadRepoArchiveFrom(rootCfg); err != nil {
|
||||
log.Fatal("loadRepoArchiveFrom: %v", err)
|
||||
}
|
||||
|
||||
for _, method := range Repository.DownloadOrCloneMethods {
|
||||
if !slices.Contains(RecognisedRepositoryDownloadOrCloneMethods, method) {
|
||||
log.Error("Unrecognised repository download or clone method: %s", method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ func CommonTemplateContextData() ContextData {
|
|||
"ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage,
|
||||
"ShowFooterVersion": setting.Other.ShowFooterVersion,
|
||||
"DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives,
|
||||
"DownloadOrCloneMethods": setting.Repository.DownloadOrCloneMethods,
|
||||
|
||||
"EnableSwagger": setting.API.EnableSwagger,
|
||||
"EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,
|
||||
|
|
|
@ -964,6 +964,7 @@ all_branches = All branches
|
|||
fork_no_valid_owners = This repository can not be forked because there are no valid owners.
|
||||
use_template = Use this template
|
||||
clone_in_vsc = Clone in VS Code
|
||||
clone_in_vscodium = Clone in VS Codium
|
||||
download_zip = Download ZIP
|
||||
download_tar = Download TAR.GZ
|
||||
download_bundle = Download BUNDLE
|
||||
|
|
|
@ -38,5 +38,8 @@
|
|||
for (const el of document.getElementsByClassName('js-clone-url-vsc')) {
|
||||
el['href'] = 'vscode://vscode.git/clone?url=' + encodeURIComponent(link);
|
||||
}
|
||||
for (const el of document.getElementsByClassName('js-clone-url-vscodium')) {
|
||||
el['href'] = 'vscodium://vscode.git/clone?url=' + encodeURIComponent(link);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
|
|
@ -131,15 +131,32 @@
|
|||
<button id="more-btn" class="ui basic small compact jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
|
||||
{{svg "octicon-kebab-horizontal"}}
|
||||
<div class="menu">
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_zip"}}</a>
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_tar"}}</a>
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_bundle"}}</a>
|
||||
{{if .CitiationExist}}
|
||||
<a class="item" id="cite-repo-button">{{svg "octicon-cross-reference" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.cite_this_repo"}}</a>
|
||||
{{$citation := .CitationExist}}
|
||||
{{$originLink := .CloneButtonOriginLink}}
|
||||
{{range $.DownloadOrCloneMethods}}
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
{{if eq . "download-zip"}}
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_zip"}}</a>
|
||||
{{end}}
|
||||
{{if eq . "download-targz"}}
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_tar"}}</a>
|
||||
{{end}}
|
||||
{{if eq . "download-bundle"}}
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_bundle"}}</a>
|
||||
{{end}}
|
||||
{{if $citation}}
|
||||
{{if eq . "cite"}}
|
||||
<a class="item" id="cite-repo-button">{{svg "octicon-cross-reference" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.cite_this_repo"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if eq . "vscode-clone"}}
|
||||
<a class="item js-clone-url-vsc" href="vscode://vscode.git/clone?url={{$originLink.HTTPS}}">{{svg "gitea-vscode" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.clone_in_vsc"}}</a>
|
||||
{{end}}
|
||||
{{if eq . "vscodium-clone"}}
|
||||
<a class="item js-clone-url-vscodium" href="vscodium://vscode.git/clone?url={{$originLink.HTTPS}}">{{svg "gitea-vscode" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.clone_in_vscodium"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<a class="item js-clone-url-vsc" href="vscode://vscode.git/clone?url={{.CloneButtonOriginLink.HTTPS}}">{{svg "gitea-vscode" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.clone_in_vsc"}}</a>
|
||||
</div>
|
||||
</button>
|
||||
{{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}}
|
||||
|
|
|
@ -43,6 +43,84 @@ func TestViewRepo(t *testing.T) {
|
|||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func TestViewRepoCloneMethods(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
getCloneMethods := func() []string {
|
||||
req := NewRequest(t, "GET", "/user2/repo1")
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
cloneMoreMethodsHTML := htmlDoc.doc.Find("#more-btn div a")
|
||||
|
||||
var methods []string
|
||||
cloneMoreMethodsHTML.Each(func(i int, s *goquery.Selection) {
|
||||
a, _ := s.Attr("href")
|
||||
methods = append(methods, a)
|
||||
})
|
||||
|
||||
return methods
|
||||
}
|
||||
|
||||
testCloneMethods := func(expected []string) {
|
||||
methods := getCloneMethods()
|
||||
|
||||
assert.Len(t, methods, len(expected))
|
||||
for i, expectedMethod := range expected {
|
||||
assert.Contains(t, methods[i], expectedMethod)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("Defaults", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
testCloneMethods([]string{"/master.zip", "/master.tar.gz", "/master.bundle", "vscode://"})
|
||||
})
|
||||
|
||||
t.Run("Customized methods", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
defer test.MockVariableValue(&setting.Repository.DownloadOrCloneMethods, []string{"vscodium-clone", "download-targz"})()
|
||||
|
||||
testCloneMethods([]string{"vscodium://", "/master.tar.gz"})
|
||||
})
|
||||
|
||||
t.Run("Individual methods", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
singleMethodTest := func(method, expectedURLPart string) {
|
||||
t.Run(method, func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
defer test.MockVariableValue(&setting.Repository.DownloadOrCloneMethods, []string{method})()
|
||||
|
||||
testCloneMethods([]string{expectedURLPart})
|
||||
})
|
||||
}
|
||||
|
||||
cases := map[string]string{
|
||||
"download-zip": "/master.zip",
|
||||
"download-targz": "/master.tar.gz",
|
||||
"download-bundle": "/master.bundle",
|
||||
"vscode-clone": "vscode://",
|
||||
"vscodium-clone": "vscodium://",
|
||||
}
|
||||
for method, expectedURLPart := range cases {
|
||||
singleMethodTest(method, expectedURLPart)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("All methods", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
defer test.MockVariableValue(&setting.Repository.DownloadOrCloneMethods, setting.RecognisedRepositoryDownloadOrCloneMethods)()
|
||||
|
||||
methods := getCloneMethods()
|
||||
// We compare against
|
||||
// len(setting.RecognisedRepositoryDownloadOrCloneMethods) - 1, because
|
||||
// the test environment does not currently set things up for the cite
|
||||
// method to display.
|
||||
assert.GreaterOrEqual(t, len(methods), len(setting.RecognisedRepositoryDownloadOrCloneMethods)-1)
|
||||
})
|
||||
}
|
||||
|
||||
func testViewRepo(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
|
|
Loading…
Reference in a new issue