diff --git a/models/repo/repo.go b/models/repo/repo.go index ee7e62b1196..6ad933d3c9a 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -647,6 +647,10 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML { // CloneLink represents different types of clone URLs of repository. type CloneLink struct { + IsWikiRepo bool + SupportSSH bool + SupportHTTPS bool + SSH string HTTPS string Tea string @@ -698,9 +702,12 @@ func ComposeTeaCloneCommand(ctx context.Context, owner, repo string) string { func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string) *CloneLink { return &CloneLink{ - SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName), - HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName), - Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName), + IsWikiRepo: strings.HasSuffix(repoPathName, ".wiki"), + SupportHTTPS: !setting.Repository.DisableHTTPGit, + SupportSSH: !setting.SSH.Disabled && (doer != nil || setting.SSH.ExposeAnonymous), + SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName), + HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName), + Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName), } } diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index fa20a7261ac..ef022db8c56 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -67,7 +67,7 @@ func prepareHomeSidebarRepoTopics(ctx *context.Context) { ctx.Data["Topics"] = topics } -func prepareOpenWithEditorApps(ctx *context.Context) { +func prepareClonePanel(ctx *context.Context) { var tmplApps []map[string]any apps := setting.Config().Repository.OpenWithEditorApps.Value(ctx) for _, app := range apps { @@ -93,6 +93,12 @@ func prepareOpenWithEditorApps(ctx *context.Context) { }) } ctx.Data["OpenWithEditorApps"] = tmplApps + + if !setting.Repository.DisableDownloadSourceArchives { + // FIXME: here it only uses the shortname in the ref to build the link, it can't distinguish the branch/tag/commit with the same name + // in the future, it's better to use something like "/archive/branch/the-name.zip", "/archive/tag/the-name.zip" */}} + ctx.Data["DownloadArchiveLinkPrefix"] = ctx.Repo.RepoLink + "/archive/" + util.PathEscapeSegments(ctx.Repo.RefFullName.ShortName()) + } } func prepareHomeSidebarCitationFile(entry *git.TreeEntry) func(ctx *context.Context) { @@ -439,7 +445,7 @@ func Home(ctx *context.Context) { isTreePathRoot := ctx.Repo.TreePath == "" prepareFuncs := []func(*context.Context){ - prepareOpenWithEditorApps, + prepareClonePanel, prepareHomeSidebarRepoTopics, checkOutdatedBranch, prepareToRenderDirOrFile(entry), diff --git a/services/context/repo.go b/services/context/repo.go index 87717f730b6..9d8aa97f326 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -589,6 +589,7 @@ func repoAssignmentPrepareTemplateData(ctx *Context, data *repoAssignmentPrepare ctx.Repo.RepoLink = repo.Link() ctx.Data["RepoLink"] = ctx.Repo.RepoLink ctx.Data["FeedURL"] = ctx.Repo.RepoLink + ctx.Data["CloneButtonOriginLink"] = repo.CloneLink(ctx, ctx.Doer) // CloneButtonOriginLink may be rewritten to the WikiCloneLink by the router middleware unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker) if err == nil { @@ -643,18 +644,6 @@ func repoAssignmentPrepareTemplateData(ctx *Context, data *repoAssignmentPrepare // If multiple forks are available or if the user can fork to another account, but there is already a fork: open selection dialog ctx.Data["ShowForkModal"] = len(userAndOrgForks) > 1 || (canSignedUserFork && len(userAndOrgForks) > 0) - ctx.Data["RepoCloneLink"] = repo.CloneLink(ctx, ctx.Doer) - - cloneButtonShowHTTPS := !setting.Repository.DisableHTTPGit - cloneButtonShowSSH := !setting.SSH.Disabled && (ctx.IsSigned || setting.SSH.ExposeAnonymous) - if !cloneButtonShowHTTPS && !cloneButtonShowSSH { - // We have to show at least one link, so we just show the HTTPS - cloneButtonShowHTTPS = true - } - ctx.Data["CloneButtonShowHTTPS"] = cloneButtonShowHTTPS - ctx.Data["CloneButtonShowSSH"] = cloneButtonShowSSH - ctx.Data["CloneButtonOriginLink"] = ctx.Data["RepoCloneLink"] // it may be rewritten to the WikiCloneLink by the router middleware - ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled if setting.Indexer.RepoIndexerEnabled { ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) @@ -778,16 +767,6 @@ func repoAssignmentPrepareRepoTransfer(ctx *Context, data *repoAssignmentPrepare } } -func repoAssignmentHandleGoGet(ctx *Context, data *repoAssignmentPrepareDataStruct) { - repo := data.repo - if ctx.FormString("go-get") == "1" { - ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, repo.Owner.Name, repo.Name) - fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName) - ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}" - ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}" - } -} - // RepoAssignment returns a middleware to handle repository assignment func RepoAssignment(ctx *Context) { repoAssignmentPreCheck(ctx) @@ -804,7 +783,6 @@ func RepoAssignment(ctx *Context) { repoAssignmentPrepareRepoTransfer, repoAssignmentPrepareBranches, repoAssignmentPreparePullRequests, - repoAssignmentHandleGoGet, } for _, f := range funcs { f(ctx, prepareData) diff --git a/services/repository/create.go b/services/repository/create.go index 636f4a803bc..449ab4a5362 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -84,7 +84,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.Readme, err) } - cloneLink := repo.CloneLink(ctx, nil /* no doer so do not generate user-related SSH link */) + cloneLink := repo.CloneLinkGeneral(ctx) match := map[string]string{ "Name": repo.Name, "Description": util.NormalizeStringEOL(repo.Description), diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 58728fd117b..33789130f89 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -9,10 +9,6 @@ {{/* required by: 1. "redirect_to" cookie; 2. cross-origin protection */}} -{{if .GoGetImport}} - - -{{end}} {{if and .EnableFeed .FeedURL}} diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl index 03b7a561daa..24024134f42 100644 --- a/templates/repo/clone_buttons.tmpl +++ b/templates/repo/clone_buttons.tmpl @@ -1,13 +1,16 @@ - -
- {{if $.CloneButtonShowHTTPS}} - + +{{$cloneLink := $.CloneButtonOriginLink}} +{{if or $cloneLink.SupportHTTPS $cloneLink.SupportSSH}} +
+ {{if $cloneLink.SupportHTTPS}} + {{end}} - {{if $.CloneButtonShowSSH}} - + {{if $cloneLink.SupportSSH}} + {{end}} - +
+{{end}} diff --git a/templates/repo/clone_panel.tmpl b/templates/repo/clone_panel.tmpl index e23bc8a19a4..a17a4b3a802 100644 --- a/templates/repo/clone_panel.tmpl +++ b/templates/repo/clone_panel.tmpl @@ -1,48 +1,56 @@ -
-
{{svg "octicon-terminal"}} Clone
- -
- - {{if $.CloneButtonShowHTTPS}} - - {{end}} - {{if $.CloneButtonShowSSH}} - - {{end}} - -
-
- -
-
- -
- {{svg "octicon-copy" 14}} -
-
-
- - {{if not .PageIsWiki}} -
- {{range .OpenWithEditorApps}} - {{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}} + {{if $showCloneLinks}} +
{{svg "octicon-terminal"}} Clone
+
+ + {{if $cloneLink.SupportHTTPS}} + + {{end}} + {{if $cloneLink.SupportSSH}} + + {{end}} + {{if not $cloneLink.IsWikiRepo}} + {{end}}
- - {{if and (not $.DisableDownloadSourceArchives) $.RefFullName}}
-
- {{/* FIXME: here it only uses the shortname in the ref to build the link, it can't distinguish the branch/tag/commit with the same name - in the future, it's better to use something like "/archive/branch/the-name.zip", "/archive/tag/the-name.zip" */}} - {{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}} - {{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}} - {{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}} + +
+
+ +
+ {{svg "octicon-copy" 14}} +
+
+
+ {{end}} + + {{if $showOpenWithEditorApps}} +
+ {{range $app := $openWithEditorApps}} + {{$app.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" $app.DisplayName}} + {{end}} +
+ {{end}} + + {{if $downloadArchiveLinkPrefix}} + {{if $showOpenWithEditorApps}}
{{end}} + - {{end}} {{end}}
+{{end}} diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index fd3cf2b3db6..11408553997 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -21,7 +21,7 @@
{{ctx.Locale.Tr "repo.no_branch"}}
{{else if .CanWriteCode}}

{{ctx.Locale.Tr "repo.quick_guide"}}

-
+

{{ctx.Locale.Tr "repo.clone_this_repo"}} {{ctx.Locale.Tr "repo.clone_helper" "http://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository"}}

@@ -36,12 +36,15 @@ {{end}} {{end}} - {{template "repo/clone_buttons" .}} + {{template "repo/clone_buttons" dict "CloneButtonOriginLink" $.CloneButtonOriginLink}}
- {{if not .Repository.IsArchived}} -
+ {{$cloneLink := $.CloneButtonOriginLink}} + {{$showGitClientCommands := and (or $cloneLink.SupportHTTPS $cloneLink.SupportSSH) (not .Repository.IsArchived)}} + {{/* TODO: when both HTTPS and SSH are disabled, the UI is not that good */}} + {{if $showGitClientCommands}} +

{{ctx.Locale.Tr "repo.create_new_repo_command"}}

@@ -52,19 +55,19 @@ git init{{if ne .Repository.ObjectFormatName "sha1"}} --object-format={{.Reposit {{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}} git add README.md git commit -m "first commit" -git remote add {{$gitRemoteName}} {{$.CloneButtonOriginLink.HTTPS}} +git remote add {{$gitRemoteName}} {{Iif $cloneLink.SupportHTTPS $cloneLink.HTTPS $cloneLink.SSH}} git push -u {{$gitRemoteName}} {{.Repository.DefaultBranch}}
-
-

{{ctx.Locale.Tr "repo.push_exist_repo"}}

-
git remote add {{$gitRemoteName}} {{$.CloneButtonOriginLink.HTTPS}}
+									
git remote add {{$gitRemoteName}} {{Iif $cloneLink.SupportHTTPS $cloneLink.HTTPS $cloneLink.SSH}}
 git push -u {{$gitRemoteName}} {{.Repository.DefaultBranch}}
+ {{else}} +
HTTPS and SSH clones are disabled, you can only modify the repository via Gitea's web UI.
{{end}}
{{else}} diff --git a/templates/repo/view_content.tmpl b/templates/repo/view_content.tmpl index 77e051d7d61..5e52fc2adfb 100644 --- a/templates/repo/view_content.tmpl +++ b/templates/repo/view_content.tmpl @@ -112,7 +112,11 @@ {{end}} {{if $isTreePathRoot}} - {{template "repo/clone_panel" .}} + {{template "repo/clone_panel" (dict + "CloneButtonOriginLink" $.CloneButtonOriginLink + "OpenWithEditorApps" $.OpenWithEditorApps + "DownloadArchiveLinkPrefix" $.DownloadArchiveLinkPrefix + )}} {{end}} {{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl index a9df43ea468..f68ba3349f5 100644 --- a/templates/repo/wiki/revision.tmpl +++ b/templates/repo/wiki/revision.tmpl @@ -15,7 +15,7 @@
- {{template "repo/clone_panel" .}} + {{template "repo/clone_panel" dict "CloneButtonOriginLink" $.CloneButtonOriginLink}}

{{ctx.Locale.Tr "repo.wiki.wiki_page_revisions"}}

diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index 967a8814c96..5ec93276fad 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -28,7 +28,7 @@ - {{template "repo/clone_panel" .}} + {{template "repo/clone_panel" dict "CloneButtonOriginLink" $.CloneButtonOriginLink}}
diff --git a/web_src/js/features/repo-common.ts b/web_src/js/features/repo-common.ts index c3b26fd06ad..8e0af7a91c6 100644 --- a/web_src/js/features/repo-common.ts +++ b/web_src/js/features/repo-common.ts @@ -1,4 +1,4 @@ -import {queryElems} from '../utils/dom.ts'; +import {queryElems, toggleElem} from '../utils/dom.ts'; import {errorMessage} from '../modules/errors.ts'; import {POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; @@ -7,6 +7,7 @@ import RepoActivityTopAuthors from '../components/RepoActivityTopAuthors.vue'; import {createApp} from 'vue'; import {createTippy} from '../modules/tippy.ts'; import {localUserSettings} from '../modules/user-settings.ts'; +import {registerGlobalInitFunc} from '../modules/observer.ts'; async function onDownloadArchive(e: Event) { e.preventDefault(); @@ -51,32 +52,33 @@ export function substituteRepoOpenWithUrl(tmpl: string, url: string): string { return tmpl.replace('{url}', needEncode ? encodeURIComponent(url) : url); } -function initCloneSchemeUrlSelection(parent: Element) { - const elCloneUrlInput = parent.querySelector('.repo-clone-url')!; +function initRepoCloneButtonsCombo(parent: Element) { + // the clone section is not rendered at all when no git transport (HTTPS/SSH) is available + const elCloneUrlInput = parent.querySelector('.repo-clone-url'); + if (!elCloneUrlInput) return; const tabHttps = parent.querySelector('.repo-clone-https'); const tabSsh = parent.querySelector('.repo-clone-ssh'); const tabTea = parent.querySelector('.repo-clone-tea'); + const listOpenWithEditorApps = parent.querySelector('.repo-clone-with-apps'); + + // not every tab exists in every panel, eg: the admin may disable HTTP/SSH, and the empty repo page has no Tea CLI tab + const tabByScheme: Record = {https: tabHttps, ssh: tabSsh, tea: tabTea}; const updateClonePanelUi = function() { let scheme = localUserSettings.getString('repo-clone-protocol'); - if (!['https', 'ssh', 'tea'].includes(scheme)) { - scheme = 'https'; - } - - // Fallbacks if the scheme preference is not available in the tabs, for example: empty repo page, there are only HTTPS and SSH - if (scheme === 'tea' && !tabTea) { - scheme = 'https'; - } - if (scheme === 'https' && !tabHttps) { - scheme = 'ssh'; - } else if (scheme === 'ssh' && !tabSsh) { - scheme = 'https'; + // fall back to the first available tab when the preferred scheme's tab is absent (unset preference, or disabled protocol) + if (!tabByScheme[scheme]) { + scheme = ['https', 'ssh', 'tea'].find((s) => tabByScheme[s]) ?? ''; } const isHttps = scheme === 'https'; const isSsh = scheme === 'ssh'; const isTea = scheme === 'tea'; + if (listOpenWithEditorApps) { + toggleElem(listOpenWithEditorApps, !isTea); // don't show the "Open with editor apps" list when "Tea" clone is selected + } + if (tabHttps) { const link = tabHttps.getAttribute('data-link')!; tabHttps.textContent = link.split(':')[0].toUpperCase(); // show "HTTP" or "HTTPS" @@ -89,16 +91,9 @@ function initCloneSchemeUrlSelection(parent: Element) { tabTea.classList.toggle('active', isTea); } - let tab: Element | null = null; - if (isHttps) { - tab = tabHttps; - } else if (isSsh) { - tab = tabSsh; - } else if (isTea) { - tab = tabTea; - } + const tab = tabByScheme[scheme]; + if (!tab) return; // no protocol available at all, leave the (hidden) input untouched - if (!tab) return; const link = tab.getAttribute('data-link')!; for (const el of document.querySelectorAll('.js-clone-url')) { @@ -132,10 +127,10 @@ function initCloneSchemeUrlSelection(parent: Element) { }); } -function initClonePanelButton(btn: HTMLButtonElement) { +function initRepoClonePanel(btn: HTMLButtonElement) { const elPanel = btn.nextElementSibling!; // "init" must be before the "createTippy" otherwise the "tippy-target" will be removed from the document - initCloneSchemeUrlSelection(elPanel); + initRepoCloneButtonsCombo(elPanel); createTippy(btn, { content: elPanel, trigger: 'click', @@ -147,8 +142,8 @@ function initClonePanelButton(btn: HTMLButtonElement) { } export function initRepoCloneButtons() { - queryElems(document, '.js-btn-clone-panel', initClonePanelButton); - queryElems(document, '.clone-buttons-combo', initCloneSchemeUrlSelection); + registerGlobalInitFunc('initRepoClonePanel', initRepoClonePanel); + registerGlobalInitFunc('initRepoCloneButtonsCombo', initRepoCloneButtonsCombo); } export async function updateIssuesMeta(url: string, action: string, issue_ids: string, id: string) {