From 83fe8c9f445b915ef6e2d992352157f2cfcf580b Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 04:34:45 +0000 Subject: [PATCH 01/48] wip --- options/locale/locale_en-US.ini | 2 +- routers/web/repo/view.go | 21 ++ templates/repo/home.tmpl | 350 ++++++++++++++++++++------------ templates/repo/sub_menu.tmpl | 22 -- web_src/css/repo.css | 21 +- 5 files changed, 261 insertions(+), 155 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index f77fd203a2d6b..cabb72c5b8d9f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1032,7 +1032,7 @@ generate_repo = Generate Repository generate_from = Generate From repo_desc = Description repo_desc_helper = Enter short description (optional) -repo_lang = Language +repo_lang = Languages repo_gitignore_helper = Select .gitignore templates. repo_gitignore_helper_desc = Choose which files not to track from a list of templates for common languages. Typical artifacts generated by each language's build tools are included on .gitignore by default. issue_labels = Issue Labels diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 12d202e4a0d45..106ece0880b1b 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -902,6 +902,22 @@ func renderLanguageStats(ctx *context.Context) { ctx.Data["LanguageStats"] = langs } +func renderLatestRelease(ctx *context.Context) { + release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) + if err != nil && !repo_model.IsErrReleaseNotExist(err) { + ctx.ServerError("GetLatestReleaseByRepoID", err) + return + } + + if release != nil { + if err = release.LoadAttributes(ctx); err != nil { + ctx.ServerError("release.LoadAttributes", err) + return + } + ctx.Data["LatestRelease"] = release + } +} + func renderRepoTopics(ctx *context.Context) { topics, err := db.Find[repo_model.Topic](ctx, &repo_model.FindTopicOptions{ RepoID: ctx.Repo.Repository.ID, @@ -1014,6 +1030,11 @@ func renderHomeCode(ctx *context.Context) { return } + renderLatestRelease(ctx) + if ctx.Written() { + return + } + if entry.IsDir() { renderDirectory(ctx) } else { diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index ff82f2ca80360..489a2f017263b 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -4,154 +4,242 @@
{{template "base/alert" .}} {{template "repo/code/recently_pushed_new_branches" .}} - {{if and (not .HideRepoInfo) (not .IsBlame)}} -
- {{- $description := .Repository.DescriptionHTML ctx -}} - {{if $description}}{{$description | RenderCodeBlock}}{{end}} - {{if .Repository.Website}}{{.Repository.Website}}{{end}} -
-
- {{/* it should match the code in issue-home.js */}} - {{range .Topics}}{{.Name}}{{end}} - {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} -
- {{end}} - {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
- -
- - -
-
- {{end}} - {{if .Repository.IsArchived}} -
- {{if .Repository.ArchivedUnix.IsZero}} - {{ctx.Locale.Tr "repo.archive.title"}} - {{else}} - {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}} - {{end}} -
- {{end}} - {{template "repo/sub_menu" .}} - {{$n := len .TreeNames}} - {{$l := Eval $n "-" 1}} - {{$isHomepage := (eq $n 0)}} -
-
- {{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} - {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} - {{$cmpBranch := ""}} - {{if ne .Repository.ID .BaseRepo.ID}} - {{$cmpBranch = printf "%s/%s:" (.Repository.OwnerName|PathEscape) (.Repository.Name|PathEscape)}} - {{end}} - {{$cmpBranch = print $cmpBranch (.BranchName|PathEscapeSegments)}} - {{$compareLink := printf "%s/compare/%s...%s" .BaseRepo.Link (.BaseRepo.DefaultBranch|PathEscapeSegments) $cmpBranch}} - - {{svg "octicon-git-pull-request"}} - - {{end}} - - {{if $isHomepage}} - {{ctx.Locale.Tr "repo.find_file.go_to_file"}} + +
+
+ {{if .Repository.IsArchived}} +
+ {{if .Repository.ArchivedUnix.IsZero}} + {{ctx.Locale.Tr "repo.archive.title"}} + {{else}} + {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}} + {{end}} +
{{end}} - - {{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}} - + {{end}} + + {{if and $isHomepage (.Repository.IsTemplate)}} + + {{ctx.Locale.Tr "repo.use_template"}} - {{end}} - - {{ctx.Locale.Tr "repo.editor.patch"}} + {{end}} + {{if not $isHomepage}} + + {{StringUtils.EllipsisString .Repository.Name 30}} + {{- range $i, $v := .TreeNames -}} + / + {{- if eq $i $l -}} + {{$v}} + {{- else -}} + {{$p := index $.Paths $i}}{{$v}} + {{- end -}} + {{- end -}} + + {{end}} +
+
+ + {{if $isHomepage}} +
+ {{template "repo/clone_buttons" .}} + + {{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}} +
+ {{template "repo/cite/cite_modal" .}} + {{end}} + {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} + + {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} -
- + {{end}} +
+
+ {{if .IsViewFile}} + {{template "repo/view_file" .}} + {{else if .IsBlame}} + {{template "repo/blame" .}} + {{else}}{{/* IsViewDirectory */}} + {{template "repo/view_list" .}} {{end}} +
- {{if and $isHomepage (.Repository.IsTemplate)}} - - {{ctx.Locale.Tr "repo.use_template"}} - - {{end}} - {{if $isHomepage}} - {{/* only show the "code search" on the repo home page, it only does global search, - so do not show it when viewing file or directory to avoid misleading users (it doesn't search in a directory) */}} -
+ {{if $isHomepage}} +
+
{{template "shared/search/button"}}
- {{else}} - - {{StringUtils.EllipsisString .Repository.Name 30}} - {{- range $i, $v := .TreeNames -}} - / - {{- if eq $i $l -}} - {{$v}} - {{- else -}} - {{$p := index $.Paths $i}}{{$v}} - {{- end -}} - {{- end -}} - - {{end}} -
-
- - {{if $isHomepage}} -
- {{template "repo/clone_buttons" .}} - {{end}} {{end}} - {{if .CitiationExist}} - {{svg "octicon-cross-reference" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.cite_this_repo"}} + {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} +
+ +
+ + +
+
{{end}} - {{range .OpenWithEditorApps}} - {{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}} + {{if and .ReadmeExist}} + + {{end}} + {{if .CitiationExist}} + {{end}}
- - {{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}} +
+ + {{if .LatestRelease}} +
+
+ +
+
+ {{svg "octicon-tag" 16}} +
+
+
+
+ {{.LatestRelease.Title}} + {{ctx.Locale.Tr "latest"}} +
+
+
+ {{TimeSinceUnix .LatestRelease.CreatedUnix ctx.Locale}} +
+
+
+
+
+ {{end}} + + {{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo) .LanguageStats}} +
+
+
+ {{ctx.Locale.Tr "repo.repo_lang"}} +
+ +
+
+ {{range .LanguageStats}} +
+ {{end}} +
+
+ {{range .LanguageStats}} +
+ + + {{if eq .Language "other"}} + {{ctx.Locale.Tr "repo.language_other"}} + {{else}} + {{.Language}} + {{end}} + + {{.Percentage}}% +
+ {{end}} +
+
+
+
+ {{end}}
- {{template "repo/cite/cite_modal" .}} - {{end}} - {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} - - {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} - - {{end}} - + + {{end}} - {{if .IsViewFile}} - {{template "repo/view_file" .}} - {{else if .IsBlame}} - {{template "repo/blame" .}} - {{else}}{{/* IsViewDirectory */}} - {{template "repo/view_list" .}} - {{end}} {{template "base/footer" .}} diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl index 87d2110314943..df9f59bc002b4 100644 --- a/templates/repo/sub_menu.tmpl +++ b/templates/repo/sub_menu.tmpl @@ -20,27 +20,5 @@ {{end}} - {{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo) .LanguageStats}} - - - {{range .LanguageStats}} -
- {{end}} -
- {{end}} {{end}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 85f33f858e2ec..334061f59436a 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1836,7 +1836,7 @@ td .commit-summary { background: var(--color-secondary); } -.repository .repository-summary .segment.language-stats { +.language-stats { display: flex; gap: 2px; padding: 0; @@ -1844,6 +1844,25 @@ td .commit-summary { white-space: nowrap; border-radius: 0 0 3px 3px !important; overflow: hidden; + width: 100%; + margin-top: 1rem; + margin-bottom: 5px; +} + +.language-stats-details { + display: flex; + flex-wrap: wrap; +} + +.language-stats-details .item { + height: 30px; + line-height: var(--line-height-default); + display: flex; + align-items: center; + justify-content: center; + gap: 0.25em; + padding: 0 0.5em; /* make the UI look better for narrow (mobile) view */ + text-decoration: none; } #cite-repo-modal #citation-panel { From e28398b2889590b95a07582f8ae46d04fb001654 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 04:48:12 +0000 Subject: [PATCH 02/48] fix cite button --- web_src/js/features/citation.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index ffab75c389819..a94686be3c9d5 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -30,6 +30,7 @@ export async function initCitationFileCopyContent() { const citationCopyApa = document.querySelector('#citation-copy-apa'); const citationCopyBibtex = document.querySelector('#citation-copy-bibtex'); const inputContent = document.querySelector('#citation-copy-content'); + const modal = document.querySelector('#cite-repo-modal'); if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return; @@ -42,8 +43,8 @@ export async function initCitationFileCopyContent() { }; document.querySelector('#cite-repo-button')?.addEventListener('click', async (e) => { - const dropdownBtn = e.target.closest('.ui.dropdown.button'); - dropdownBtn.classList.add('is-loading'); + $(modal).modal('show'); + $(modal).addClass('is-loading'); try { try { @@ -68,9 +69,7 @@ export async function initCitationFileCopyContent() { inputContent.select(); }); } finally { - dropdownBtn.classList.remove('is-loading'); + $(modal).removeClass('is-loading'); } - - $('#cite-repo-modal').modal('show'); }); } From ca47b740d6d184f7cc0f69362a6ac53492794046 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 04:58:57 +0000 Subject: [PATCH 03/48] fix --- options/locale/locale_en-US.ini | 1 + templates/repo/home.tmpl | 2 +- web_src/js/features/repo-home.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index cabb72c5b8d9f..99d3bbe52e2d1 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -144,6 +144,7 @@ confirm_delete_selected = Confirm to delete all selected items? name = Name value = Value +readme = Readme filter = Filter filter.clear = Clear Filter diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 489a2f017263b..f870f4413d280 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -160,7 +160,7 @@ {{end}} - {{if and .ReadmeExist}} + {{if .ReadmeExist}}
{{svg "octicon-book"}}{{ctx.Locale.Tr "readme"}} diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index f3e39ddb3c1ae..778155bed7594 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -50,7 +50,7 @@ export function initRepoTopicBar() { link.classList.add('repo-topic', 'ui', 'large', 'label'); link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; link.textContent = topic; - mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button + viewDiv.append(link); // insert all new topics before manage button } } hideElem(editDiv); From 4265297c86796acd23b3e38b995f83fb411e7a21 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 05:10:18 +0000 Subject: [PATCH 04/48] fix --- templates/repo/home.tmpl | 6 +++--- web_src/js/features/repo-home.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index f870f4413d280..f5a81bc27bd16 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -145,18 +145,18 @@ {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} {{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
+ {{end}} diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 778155bed7594..afb5090251722 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -46,11 +46,12 @@ export function initRepoTopicBar() { topicArray.sort(); for (const topic of topicArray) { // it should match the code in repo/home.tmpl + // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div const link = document.createElement('a'); link.classList.add('repo-topic', 'ui', 'large', 'label'); link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; link.textContent = topic; - viewDiv.append(link); // insert all new topics before manage button + viewDiv.append(link); } } hideElem(editDiv); From f217245244f82b2ea4db024a99575d66873cab8d Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 05:13:16 +0000 Subject: [PATCH 05/48] simpify mobile view --- templates/repo/home.tmpl | 2 +- web_src/css/modules/flexcontainer.css | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index f5a81bc27bd16..9275825d658ff 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -118,7 +118,7 @@
{{if $isHomepage}} -
+
diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 5d4e12cc129b1..338ca4d890966 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -21,6 +21,10 @@ min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */ } +.flex-container-repo { + width: 25%; +} + @media (max-width: 767.98px) { .flex-container { flex-direction: column; @@ -30,4 +34,7 @@ order: -1; width: auto; } + .flex-container-repo { + width: 100%; + } } From 3652576f9587c66a89bca793b5b3d36a167f8144 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 05:30:37 +0000 Subject: [PATCH 06/48] improve --- options/locale/locale_en-US.ini | 1 + templates/repo/home.tmpl | 17 +++++++++++++---- templates/repo/sub_menu.tmpl | 5 ----- web_src/css/modules/flexcontainer.css | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7ca08cadb8201..34ddf805b4e5e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1036,6 +1036,7 @@ generate_repo = Generate Repository generate_from = Generate From repo_desc = Description repo_desc_helper = Enter short description (optional) +repo_no_desc = No description provided repo_lang = Languages repo_gitignore_helper = Select .gitignore templates. repo_gitignore_helper_desc = Choose which files not to track from a list of templates for common languages. Typical artifacts generated by each language's build tools are included on .gitignore by default. diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 9275825d658ff..3038ede7b9285 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -133,9 +133,9 @@ {{ctx.Locale.Tr "repo.repo_desc"}}
{{if and (not .HideRepoInfo) (not .IsBlame)}} -
+
{{- $description := .Repository.DescriptionHTML ctx -}} - {{if $description}}{{$description | RenderCodeBlock}}{{end}} + {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}}
@@ -161,15 +161,24 @@
{{end}} {{if .ReadmeExist}} - diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl index 5196a6f7d2a65..ccb98b94ad2f8 100644 --- a/templates/repo/sub_menu.tmpl +++ b/templates/repo/sub_menu.tmpl @@ -13,11 +13,6 @@ {{svg "octicon-tag"}} {{ctx.Locale.PrettyNumber .NumTags}} {{ctx.Locale.TrN .NumTags "repo.tag" "repo.tags"}} {{end}} - {{if .DetectedRepoLicenses}} - - {{svg "octicon-law"}} {{if eq (len .DetectedRepoLicenses) 1}}{{index .DetectedRepoLicenses 0}}{{else}}{{ctx.Locale.Tr "repo.multiple_licenses"}}{{end}} - - {{end}} {{$fileSizeFormatted := FileSize .Repository.Size}}{{/* the formatted string is always "{val} {unit}" */}} {{$fileSizeFields := StringUtils.Split $fileSizeFormatted " "}} diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 338ca4d890966..02fc702f58211 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -35,6 +35,6 @@ width: auto; } .flex-container-repo { - width: 100%; + width: auto; } } From 4018f544e1ff8f5bb679cb3a57c5ece1080e631f Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 05:37:37 +0000 Subject: [PATCH 07/48] fix lint --- web_src/js/features/citation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index a94686be3c9d5..366fbaf59a6b6 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -42,7 +42,7 @@ export async function initCitationFileCopyContent() { citationCopyApa.classList.toggle('primary', !isBibtex); }; - document.querySelector('#cite-repo-button')?.addEventListener('click', async (e) => { + document.querySelector('#cite-repo-button')?.addEventListener('click', async () => { $(modal).modal('show'); $(modal).addClass('is-loading'); From 69ab70901a04d90ee99abe49905ceb7fc802818a Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 8 Oct 2024 06:32:01 +0000 Subject: [PATCH 08/48] fix lint --- templates/repo/home.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 3038ede7b9285..33a3beed16d4b 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -4,7 +4,7 @@
{{template "base/alert" .}} {{template "repo/code/recently_pushed_new_branches" .}} - +
{{if .Repository.IsArchived}} @@ -219,7 +219,7 @@
{{ctx.Locale.Tr "repo.repo_lang"}}
- +
{{range .LanguageStats}} From c2c9a1efbaffc3aba5a4b846018d2c14e3f5e404 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 21 Oct 2024 00:25:19 +0000 Subject: [PATCH 09/48] improve --- templates/repo/home.tmpl | 78 ++++++-------------------- templates/repo/home_search_code.tmpl | 6 ++ templates/repo/home_share_sidebar.tmpl | 56 ++++++++++++++++++ 3 files changed, 78 insertions(+), 62 deletions(-) create mode 100644 templates/repo/home_search_code.tmpl create mode 100644 templates/repo/home_share_sidebar.tmpl diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 33a3beed16d4b..b6306e32351b5 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -16,10 +16,20 @@ {{end}}
{{end}} - {{template "repo/sub_menu" .}} {{$n := len .TreeNames}} {{$l := Eval $n "-" 1}} {{$isHomepage := (eq $n 0)}} + + {{if $isHomepage}} +
+ {{template "repo/home_search_code" .}} +
+
+ {{template "repo/home_share_sidebar" .}} +
+ {{end}} + {{template "repo/sub_menu" .}} +
{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} @@ -119,69 +129,13 @@ {{if $isHomepage}}
- -
- - {{template "shared/search/button"}} -
- +
+ {{template "repo/home_search_code" .}} +
-
-
-
- {{ctx.Locale.Tr "repo.repo_desc"}} -
- {{if and (not .HideRepoInfo) (not .IsBlame)}} -
- {{- $description := .Repository.DescriptionHTML ctx -}} - {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} - {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}} -
-
- {{/* it should match the code in issue-home.js */}} - {{range .Topics}}{{.Name}}{{end}} -
- {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} - {{end}} - {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
- -
- - -
-
- {{end}} - {{if .ReadmeExist}} - - {{end}} - {{if .DetectedRepoLicenses}} - - {{end}} - {{if .CitiationExist}} - - {{end}} -
+
+ {{template "repo/home_share_sidebar" .}}
{{if .LatestRelease}} diff --git a/templates/repo/home_search_code.tmpl b/templates/repo/home_search_code.tmpl new file mode 100644 index 0000000000000..84dfad947cbb8 --- /dev/null +++ b/templates/repo/home_search_code.tmpl @@ -0,0 +1,6 @@ +
+
+ + {{template "shared/search/button"}} +
+
\ No newline at end of file diff --git a/templates/repo/home_share_sidebar.tmpl b/templates/repo/home_share_sidebar.tmpl new file mode 100644 index 0000000000000..674c0d68ef40d --- /dev/null +++ b/templates/repo/home_share_sidebar.tmpl @@ -0,0 +1,56 @@ +
+
+
+ {{ctx.Locale.Tr "repo.repo_desc"}} +
+ {{if and (not .HideRepoInfo) (not .IsBlame)}} +
+ {{- $description := .Repository.DescriptionHTML ctx -}} + {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} + {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}} +
+
+ {{/* it should match the code in issue-home.js */}} + {{range .Topics}}{{.Name}}{{end}} +
+ {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} + {{end}} + {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} +
+ +
+ + +
+
+ {{end}} + {{if .ReadmeExist}} + + {{end}} + {{if .DetectedRepoLicenses}} + + {{end}} + {{if .CitiationExist}} + + {{end}} +
+
\ No newline at end of file From c4b2b95138064dd678fc78440933a69f77ade9f1 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 21 Oct 2024 00:35:54 +0000 Subject: [PATCH 10/48] fix lint --- templates/repo/home_search_code.tmpl | 10 +-- templates/repo/home_share_sidebar.tmpl | 110 ++++++++++++------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/templates/repo/home_search_code.tmpl b/templates/repo/home_search_code.tmpl index 84dfad947cbb8..63ff6c148d2bd 100644 --- a/templates/repo/home_search_code.tmpl +++ b/templates/repo/home_search_code.tmpl @@ -1,6 +1,6 @@
-
- - {{template "shared/search/button"}} -
-
\ No newline at end of file +
+ + {{template "shared/search/button"}} +
+ diff --git a/templates/repo/home_share_sidebar.tmpl b/templates/repo/home_share_sidebar.tmpl index 674c0d68ef40d..c539d4190b409 100644 --- a/templates/repo/home_share_sidebar.tmpl +++ b/templates/repo/home_share_sidebar.tmpl @@ -1,56 +1,56 @@
-
-
- {{ctx.Locale.Tr "repo.repo_desc"}} -
- {{if and (not .HideRepoInfo) (not .IsBlame)}} -
- {{- $description := .Repository.DescriptionHTML ctx -}} - {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} - {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}} -
-
- {{/* it should match the code in issue-home.js */}} - {{range .Topics}}{{.Name}}{{end}} -
- {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} - {{end}} - {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
- -
- - -
-
- {{end}} - {{if .ReadmeExist}} - - {{end}} - {{if .DetectedRepoLicenses}} - - {{end}} - {{if .CitiationExist}} - - {{end}} -
-
\ No newline at end of file +
+
+ {{ctx.Locale.Tr "repo.repo_desc"}} +
+ {{if and (not .HideRepoInfo) (not .IsBlame)}} +
+ {{- $description := .Repository.DescriptionHTML ctx -}} + {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} + {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}} +
+
+ {{/* it should match the code in issue-home.js */}} + {{range .Topics}}{{.Name}}{{end}} +
+ {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} + {{end}} + {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} +
+ +
+ + +
+
+ {{end}} + {{if .ReadmeExist}} + + {{end}} + {{if .DetectedRepoLicenses}} + + {{end}} + {{if .CitiationExist}} + + {{end}} +
+
From 156fdec89953e55e62d8f214d581cc912e555bb7 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 21 Oct 2024 00:48:32 +0000 Subject: [PATCH 11/48] fix --- templates/repo/home.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index b6306e32351b5..e8e05ffb405de 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -21,10 +21,10 @@ {{$isHomepage := (eq $n 0)}} {{if $isHomepage}} -
+
{{template "repo/home_search_code" .}}
-
+
{{template "repo/home_share_sidebar" .}}
{{end}} From 72d2bbe303346ec1b5b38d1e621a0e056cd83ee6 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 21 Oct 2024 01:24:28 +0000 Subject: [PATCH 12/48] fix edit topic --- web_src/js/features/repo-home.ts | 238 ++++++++++++++++--------------- 1 file changed, 120 insertions(+), 118 deletions(-) diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index c01b01fac5c3a..3f92b65b68382 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -7,142 +7,144 @@ import {showErrorToast} from '../modules/toast.ts'; const {appSubUrl} = window.config; export function initRepoTopicBar() { - const mgrBtn = document.querySelector('#manage_topic'); - if (!mgrBtn) return; + const mgrBtns = document.querySelectorAll('#manage_topic'); + if (mgrBtns.length === 0) return; - const editDiv = document.querySelector('#topic_edit'); - const viewDiv = document.querySelector('#repo-topics'); - const topicDropdown = editDiv.querySelector('.ui.dropdown'); - let lastErrorToast; + for (const mgrBtn of mgrBtns) { + const editDiv = mgrBtn.parentNode.querySelector('#topic_edit'); + const viewDiv = mgrBtn.parentNode.querySelector('#repo-topics'); + const topicDropdown = editDiv.querySelector('.ui.dropdown'); + let lastErrorToast; - mgrBtn.addEventListener('click', () => { - hideElem(viewDiv); - showElem(editDiv); - topicDropdown.querySelector('input.search').focus(); - }); + mgrBtn.addEventListener('click', () => { + hideElem(viewDiv); + showElem(editDiv); + topicDropdown.querySelector('input.search').focus(); + }); - document.querySelector('#cancel_topic_edit').addEventListener('click', () => { - lastErrorToast?.hideToast(); - hideElem(editDiv); - showElem(viewDiv); - mgrBtn.focus(); - }); + mgrBtn.parentNode.querySelector('#cancel_topic_edit').addEventListener('click', () => { + lastErrorToast?.hideToast(); + hideElem(editDiv); + showElem(viewDiv); + mgrBtn.focus(); + }); - document.querySelector('#save_topic').addEventListener('click', async (e) => { - lastErrorToast?.hideToast(); - const topics = editDiv.querySelector('input[name=topics]').value; + mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e) => { + lastErrorToast?.hideToast(); + const topics = editDiv.querySelector('input[name=topics]').value; - const data = new FormData(); - data.append('topics', topics); + const data = new FormData(); + data.append('topics', topics); - const response = await POST(e.target.getAttribute('data-link'), {data}); + const response = await POST(e.target.getAttribute('data-link'), {data}); - if (response.ok) { - const responseData = await response.json(); - if (responseData.status === 'ok') { - queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove()); - if (topics.length) { - const topicArray = topics.split(','); - topicArray.sort(); - for (const topic of topicArray) { - // it should match the code in repo/home.tmpl - // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div - const link = document.createElement('a'); - link.classList.add('repo-topic', 'ui', 'large', 'label'); - link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; - link.textContent = topic; - viewDiv.append(link); + if (response.ok) { + const responseData = await response.json(); + if (responseData.status === 'ok') { + queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove()); + if (topics.length) { + const topicArray = topics.split(','); + topicArray.sort(); + for (const topic of topicArray) { + // it should match the code in repo/home.tmpl + // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div + const link = document.createElement('a'); + link.classList.add('repo-topic', 'ui', 'large', 'label'); + link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; + link.textContent = topic; + viewDiv.append(link); + } } + hideElem(editDiv); + showElem(viewDiv); } - hideElem(editDiv); - showElem(viewDiv); - } - } else if (response.status === 422) { - // how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save" - const responseData = await response.json(); - lastErrorToast = showErrorToast(responseData.message, {duration: 5000}); - if (responseData.invalidTopics && responseData.invalidTopics.length > 0) { - const {invalidTopics} = responseData; - const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label'); - for (const [index, value] of topics.split(',').entries()) { - if (invalidTopics.includes(value)) { - topicLabels[index].classList.remove('green'); - topicLabels[index].classList.add('red'); + } else if (response.status === 422) { + // how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save" + const responseData = await response.json(); + lastErrorToast = showErrorToast(responseData.message, {duration: 5000}); + if (responseData.invalidTopics && responseData.invalidTopics.length > 0) { + const {invalidTopics} = responseData; + const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label'); + for (const [index, value] of topics.split(',').entries()) { + if (invalidTopics.includes(value)) { + topicLabels[index].classList.remove('green'); + topicLabels[index].classList.add('red'); + } } } } - } - }); + }); - $(topicDropdown).dropdown({ - allowAdditions: true, - forceSelection: false, - fullTextSearch: 'exact', - fields: {name: 'description', value: 'data-value'}, - saveRemoteData: false, - label: { - transition: 'horizontal flip', - duration: 200, - variation: false, - }, - apiSettings: { - url: `${appSubUrl}/explore/topics/search?q={query}`, - throttle: 500, - cache: false, - onResponse(res) { - const formattedResponse = { - success: false, - results: [], - }; - const query = stripTags(this.urlData.query.trim()); - let found_query = false; - const current_topics = []; - for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) { - current_topics.push(el.getAttribute('data-value')); - } + $(topicDropdown).dropdown({ + allowAdditions: true, + forceSelection: false, + fullTextSearch: 'exact', + fields: {name: 'description', value: 'data-value'}, + saveRemoteData: false, + label: { + transition: 'horizontal flip', + duration: 200, + variation: false, + }, + apiSettings: { + url: `${appSubUrl}/explore/topics/search?q={query}`, + throttle: 500, + cache: false, + onResponse(res) { + const formattedResponse = { + success: false, + results: [], + }; + const query = stripTags(this.urlData.query.trim()); + let found_query = false; + const current_topics = []; + for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) { + current_topics.push(el.getAttribute('data-value')); + } - if (res.topics) { - let found = false; - for (const {topic_name} of res.topics) { - // skip currently added tags - if (current_topics.includes(topic_name)) { - continue; - } + if (res.topics) { + let found = false; + for (const {topic_name} of res.topics) { + // skip currently added tags + if (current_topics.includes(topic_name)) { + continue; + } - if (topic_name.toLowerCase() === query.toLowerCase()) { - found_query = true; + if (topic_name.toLowerCase() === query.toLowerCase()) { + found_query = true; + } + formattedResponse.results.push({description: topic_name, 'data-value': topic_name}); + found = true; } - formattedResponse.results.push({description: topic_name, 'data-value': topic_name}); - found = true; + formattedResponse.success = found; } - formattedResponse.success = found; - } - if (query.length > 0 && !found_query) { - formattedResponse.success = true; - formattedResponse.results.unshift({description: query, 'data-value': query}); - } else if (query.length > 0 && found_query) { - formattedResponse.results.sort((a, b) => { - if (a.description.toLowerCase() === query.toLowerCase()) return -1; - if (b.description.toLowerCase() === query.toLowerCase()) return 1; - if (a.description > b.description) return -1; - if (a.description < b.description) return 1; - return 0; - }); - } + if (query.length > 0 && !found_query) { + formattedResponse.success = true; + formattedResponse.results.unshift({description: query, 'data-value': query}); + } else if (query.length > 0 && found_query) { + formattedResponse.results.sort((a, b) => { + if (a.description.toLowerCase() === query.toLowerCase()) return -1; + if (b.description.toLowerCase() === query.toLowerCase()) return 1; + if (a.description > b.description) return -1; + if (a.description < b.description) return 1; + return 0; + }); + } - return formattedResponse; + return formattedResponse; + }, + }, + onLabelCreate(value) { + value = value.toLowerCase().trim(); + this.attr('data-value', value).contents().first().replaceWith(value); + return $(this); + }, + onAdd(addedValue, _addedText, $addedChoice) { + addedValue = addedValue.toLowerCase().trim(); + $addedChoice[0].setAttribute('data-value', addedValue); + $addedChoice[0].setAttribute('data-text', addedValue); }, - }, - onLabelCreate(value) { - value = value.toLowerCase().trim(); - this.attr('data-value', value).contents().first().replaceWith(value); - return $(this); - }, - onAdd(addedValue, _addedText, $addedChoice) { - addedValue = addedValue.toLowerCase().trim(); - $addedChoice[0].setAttribute('data-value', addedValue); - $addedChoice[0].setAttribute('data-text', addedValue); - }, - }); + }); + } } From 56dd03417bae0da52aff697f71e66a44bf1d307b Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 7 Nov 2024 04:26:27 +0000 Subject: [PATCH 13/48] add changes from #32383 #32409 --- templates/repo/home.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index e8e05ffb405de..deeda163008d6 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -12,7 +12,7 @@ {{if .Repository.ArchivedUnix.IsZero}} {{ctx.Locale.Tr "repo.archive.title"}} {{else}} - {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}} + {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}} {{end}}
{{end}} @@ -159,7 +159,7 @@
- {{TimeSinceUnix .LatestRelease.CreatedUnix ctx.Locale}} + {{DateUtils.TimeSince .LatestRelease.CreatedUnix}}
From 16bf69c92aeb99d0d1040d051fbe5ba34e35cda6 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 27 Nov 2024 23:52:05 +0000 Subject: [PATCH 14/48] Add 'Copy path' button to file view #32584 --- templates/repo/home.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index deeda163008d6..6c3188babb09f 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -82,6 +82,7 @@ / {{- if eq $i $l -}} {{$v}} + {{- else -}} {{$p := index $.Paths $i}}{{$v}} {{- end -}} From 0dd7935ea83809131f4c2452dedbd40c5cdfe9a7 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 27 Nov 2024 23:53:04 +0000 Subject: [PATCH 15/48] Fix issues with inconsistent spacing in areas #32607 --- templates/repo/home.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 6c3188babb09f..3a3c8b9fcf22b 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -32,7 +32,7 @@
- {{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} + {{template "repo/branch_dropdown" dict "root" .}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{$cmpBranch := ""}} {{if ne .Repository.ID .BaseRepo.ID}} From 176c90dbca102b2ceb4505282fce9f4ccb216b72 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 28 Nov 2024 00:09:49 +0000 Subject: [PATCH 16/48] fix --- web_src/js/features/citation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index 1289229d0fde2..bdc4dc7024c76 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -30,7 +30,7 @@ export async function initCitationFileCopyContent() { const citationCopyApa = document.querySelector('#citation-copy-apa'); const citationCopyBibtex = document.querySelector('#citation-copy-bibtex'); const inputContent = document.querySelector('#citation-copy-content'); - const modal = document.querySelector('#cite-repo-modal'); + const modal = fomanticQuery('#cite-repo-modal'); if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return; @@ -43,8 +43,8 @@ export async function initCitationFileCopyContent() { }; document.querySelector('#cite-repo-button')?.addEventListener('click', async () => { - $(modal).modal('show'); - $(modal).addClass('is-loading'); + modal.modal('show'); + modal.addClass('is-loading'); try { try { @@ -69,7 +69,7 @@ export async function initCitationFileCopyContent() { inputContent.select(); }); } finally { - $(modal).removeClass('is-loading'); + modal.removeClass('is-loading'); } }); } From 2e3c4d3f7bb6516da8bb68925794f6bfe9651e67 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 28 Nov 2024 00:20:03 +0000 Subject: [PATCH 17/48] fix ts type issues --- web_src/js/features/repo-home.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 0cc5ee6b887b8..8b42a1dcc18d1 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -7,7 +7,7 @@ import {fomanticQuery} from '../modules/fomantic/base.ts'; const {appSubUrl} = window.config; export function initRepoTopicBar() { - const mgrBtns = document.querySelectorAll('#manage_topic'); + const mgrBtns = document.querySelectorAll('#manage_topic'); if (mgrBtns.length === 0) return; for (const mgrBtn of mgrBtns) { @@ -19,7 +19,7 @@ export function initRepoTopicBar() { mgrBtn.addEventListener('click', () => { hideElem(viewDiv); showElem(editDiv); - topicDropdown.querySelector('input.search').focus(); + topicDropdown.querySelector('input.search').focus(); }); mgrBtn.parentNode.querySelector('#cancel_topic_edit').addEventListener('click', () => { @@ -29,9 +29,9 @@ export function initRepoTopicBar() { mgrBtn.focus(); }); - mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e) => { + mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLAnchorElement}) => { lastErrorToast?.hideToast(); - const topics = editDiv.querySelector('input[name=topics]').value; + const topics = editDiv.querySelector('input[name=topics]').value; const data = new FormData(); data.append('topics', topics); From 4e2480bf180cf6a705565e98f4e642a33185adbd Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 28 Nov 2024 00:20:50 +0000 Subject: [PATCH 18/48] Remove jQuery import from some files #32512 --- web_src/js/features/repo-home.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 8b42a1dcc18d1..e53311f58f4eb 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -138,7 +138,7 @@ export function initRepoTopicBar() { onLabelCreate(value) { value = value.toLowerCase().trim(); this.attr('data-value', value).contents().first().replaceWith(value); - return $(this); + return fomanticQuery(this); }, onAdd(addedValue, _addedText, $addedChoice) { addedValue = addedValue.toLowerCase().trim(); From 3cc6973d1b602905c2a931325ae5174b825d5cbd Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 28 Nov 2024 00:38:28 +0000 Subject: [PATCH 19/48] follow hiifong's suggestion --- templates/repo/home.tmpl | 2 +- web_src/css/index.css | 1 + web_src/css/repo/home.css | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 web_src/css/repo/home.css diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 3a3c8b9fcf22b..5e2e5d51ea63c 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -155,7 +155,7 @@
- {{.LatestRelease.Title}} + {{.LatestRelease.Title}} {{ctx.Locale.Tr "latest"}}
diff --git a/web_src/css/index.css b/web_src/css/index.css index 817f6997da2a2..029518541bab9 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -64,6 +64,7 @@ @import "./repo/linebutton.css"; @import "./repo/wiki.css"; @import "./repo/header.css"; +@import "./repo/home.css"; @import "./repo/reactions.css"; @import "./editor/fileeditor.css"; diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css new file mode 100644 index 0000000000000..0b8f05f5fae5e --- /dev/null +++ b/web_src/css/repo/home.css @@ -0,0 +1,8 @@ +.latest-releases { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file From eb65b8730ef4d08ce7cbe440c4db4bb16f1b7d58 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 28 Nov 2024 00:54:51 +0000 Subject: [PATCH 20/48] fix lint --- web_src/css/repo/home.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index 0b8f05f5fae5e..be185c88f1656 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -1,8 +1,8 @@ .latest-releases { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - line-clamp: 2; - overflow: hidden; - text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; } \ No newline at end of file From 44559359c13c0232576020853440c70009e741be Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 3 Dec 2024 06:13:55 +0000 Subject: [PATCH 21/48] fix files table width --- templates/repo/view_list.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index c4d61edad8b36..3edfbb347456f 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -1,4 +1,4 @@ - +
From 3720d0e630234a5f5b6d7999c58132d66c47ebe6 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 3 Dec 2024 06:51:43 +0000 Subject: [PATCH 22/48] fix branch-selector-dropdown width --- web_src/css/repo.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 6f7c18f6fd7ad..8a2e4fead301f 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2195,7 +2195,7 @@ td .commit-summary { flex-grow: 0; } -@media (max-width: 991px) { +@media (max-width: 1200px) { .repository:not(.wiki) .repo-button-row { flex-direction: column; align-items: stretch; @@ -2839,9 +2839,9 @@ tbody.commit-list { /* FIXME: These media selectors are not ideal (just keep them from old code). There are many different pages, some need the max-width while some others don't, they should be tested and improved in the future. */ -@media (min-width: 768px) and (max-width: 991.98px) { +@media (min-width: 768px) and (max-width: 1235px) { .branch-selector-dropdown .branch-dropdown-button { - max-width: 185px; + max-width: 301px; } } From ac44d90e040bfd54d9770b69143a98f633dcb546 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 3 Dec 2024 07:12:15 +0000 Subject: [PATCH 23/48] fix lint --- templates/repo/home.tmpl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index f463228f67237..ce370d21f2a66 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -32,23 +32,23 @@
- {{$branchDropdownCurrentRefType := "branch"}} - {{$branchDropdownCurrentRefShortName := .BranchName}} - {{if .IsViewTag}} - {{$branchDropdownCurrentRefType = "tag"}} - {{$branchDropdownCurrentRefShortName = .TagName}} - {{end}} - {{template "repo/branch_dropdown" dict - "Repository" .Repository - "ShowTabBranches" true - "ShowTabTags" true - "CurrentRefType" $branchDropdownCurrentRefType - "CurrentRefShortName" $branchDropdownCurrentRefShortName - "CurrentTreePath" .TreePath - "RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}" - "AllowCreateNewRef" .CanCreateBranch - "ShowViewAllRefsEntry" true - }} + {{$branchDropdownCurrentRefType := "branch"}} + {{$branchDropdownCurrentRefShortName := .BranchName}} + {{if .IsViewTag}} + {{$branchDropdownCurrentRefType = "tag"}} + {{$branchDropdownCurrentRefShortName = .TagName}} + {{end}} + {{template "repo/branch_dropdown" dict + "Repository" .Repository + "ShowTabBranches" true + "ShowTabTags" true + "CurrentRefType" $branchDropdownCurrentRefType + "CurrentRefShortName" $branchDropdownCurrentRefShortName + "CurrentTreePath" .TreePath + "RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}" + "AllowCreateNewRef" .CanCreateBranch + "ShowViewAllRefsEntry" true + }} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{$cmpBranch := ""}} {{if ne .Repository.ID .BaseRepo.ID}} From c562e11091616a675177fc91320541eeea673962 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 3 Dec 2024 07:16:55 +0000 Subject: [PATCH 24/48] fix topic overflow bug --- templates/repo/home.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index ce370d21f2a66..2b1f617891849 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -151,7 +151,7 @@
-
+
{{template "repo/home_share_sidebar" .}}
From 5c0e899b4c02b6a2360dea5d96bf780fed0f9f6f Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 3 Dec 2024 07:24:39 +0000 Subject: [PATCH 25/48] fix label word break --- templates/repo/home_share_sidebar.tmpl | 2 +- web_src/css/repo.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/repo/home_share_sidebar.tmpl b/templates/repo/home_share_sidebar.tmpl index c539d4190b409..1bde9bbfee78e 100644 --- a/templates/repo/home_share_sidebar.tmpl +++ b/templates/repo/home_share_sidebar.tmpl @@ -11,7 +11,7 @@
{{/* it should match the code in issue-home.js */}} - {{range .Topics}}{{.Name}}{{end}} + {{range .Topics}}{{.Name}}{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} {{end}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 8a2e4fead301f..d499310875c69 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2321,6 +2321,7 @@ tbody.commit-list { font-weight: var(--font-weight-normal); cursor: pointer; margin: 0; + display: inline-block !important; } #new-dependency-drop-list.ui.selection.dropdown { From 536ec4a4424c02f1dcc2455bf1968b6e46016a5c Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 4 Dec 2024 23:30:32 +0000 Subject: [PATCH 26/48] move to home.css --- web_src/css/repo.css | 29 ----------------------------- web_src/css/repo/home.css | 31 ++++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/web_src/css/repo.css b/web_src/css/repo.css index d499310875c69..052fd8cb62d22 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1822,35 +1822,6 @@ td .commit-summary { background: var(--color-secondary); } -.language-stats { - display: flex; - gap: 2px; - padding: 0; - height: 10px; - white-space: nowrap; - border-radius: 0 0 3px 3px !important; - overflow: hidden; - width: 100%; - margin-top: 1rem; - margin-bottom: 5px; -} - -.language-stats-details { - display: flex; - flex-wrap: wrap; -} - -.language-stats-details .item { - height: 30px; - line-height: var(--line-height-default); - display: flex; - align-items: center; - justify-content: center; - gap: 0.25em; - padding: 0 0.5em; /* make the UI look better for narrow (mobile) view */ - text-decoration: none; -} - #cite-repo-modal #citation-panel { display: flex; width: 100%; diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index be185c88f1656..c47228ef8ddb2 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -5,4 +5,33 @@ line-clamp: 2; overflow: hidden; text-overflow: ellipsis; -} \ No newline at end of file +} + +.language-stats { + display: flex; + gap: 2px; + padding: 0; + height: 10px; + white-space: nowrap; + border-radius: 0 0 3px 3px !important; + overflow: hidden; + width: 100%; + margin-top: 1rem; + margin-bottom: 5px; +} + +.language-stats-details { + display: flex; + flex-wrap: wrap; +} + +.language-stats-details .item { + height: 30px; + line-height: var(--line-height-default); + display: flex; + align-items: center; + justify-content: center; + gap: 0.25em; + padding: 0 0.5em; /* make the UI look better for narrow (mobile) view */ + text-decoration: none; +} From d11bf78fa1ed53f003092224ff71561330d1e662 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 4 Dec 2024 23:43:16 +0000 Subject: [PATCH 27/48] add release unit enable check --- routers/web/repo/view.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 7cd277d155c7e..530328c46fb67 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -888,6 +888,10 @@ func renderLanguageStats(ctx *context.Context) { } func renderLatestRelease(ctx *context.Context) { + if !ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeReleases) || unit_model.TypeReleases.UnitGlobalDisabled() { + return + } + release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) if err != nil && !repo_model.IsErrReleaseNotExist(err) { ctx.ServerError("GetLatestReleaseByRepoID", err) From f65127258da72bccd616653c1bb6e5e225c904e0 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 5 Dec 2024 01:18:07 +0000 Subject: [PATCH 28/48] simplify the condition --- routers/web/repo/view.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 530328c46fb67..3b95624ce9c78 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -888,7 +888,7 @@ func renderLanguageStats(ctx *context.Context) { } func renderLatestRelease(ctx *context.Context) { - if !ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeReleases) || unit_model.TypeReleases.UnitGlobalDisabled() { + if !ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeReleases) { return } From 67026e1d2a7a768e2871f9ff06ed1e9a15cde5d3 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 5 Dec 2024 01:19:48 +0000 Subject: [PATCH 29/48] simplify the condiftion --- templates/repo/home.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 2b1f617891849..7ae9348712a12 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -184,7 +184,7 @@
{{end}} - {{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo) .LanguageStats}} + {{if and (not .IsEmptyRepo) .LanguageStats}}
From 4373ad1a623e9756507cd6356edf388017f3b239 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 5 Dec 2024 01:29:05 +0000 Subject: [PATCH 30/48] remove jQuery --- web_src/js/features/citation.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index bdc4dc7024c76..af8cedffbd995 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -30,7 +30,8 @@ export async function initCitationFileCopyContent() { const citationCopyApa = document.querySelector('#citation-copy-apa'); const citationCopyBibtex = document.querySelector('#citation-copy-bibtex'); const inputContent = document.querySelector('#citation-copy-content'); - const modal = fomanticQuery('#cite-repo-modal'); + const elModal = document.querySelector('#cite-repo-modal'); + const $modal = fomanticQuery('#cite-repo-modal'); if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return; @@ -43,8 +44,8 @@ export async function initCitationFileCopyContent() { }; document.querySelector('#cite-repo-button')?.addEventListener('click', async () => { - modal.modal('show'); - modal.addClass('is-loading'); + $modal.modal('show'); + elModal.classList.add('is-loading'); try { try { @@ -69,7 +70,7 @@ export async function initCitationFileCopyContent() { inputContent.select(); }); } finally { - modal.removeClass('is-loading'); + elModal.classList.remove('is-loading'); } }); } From 0a2d81c6ee7c62d9caa45e52e7ab305dc10409aa Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 5 Dec 2024 01:30:43 +0000 Subject: [PATCH 31/48] fix --- web_src/js/features/repo-home.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index e53311f58f4eb..2ceb378e1be01 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -29,7 +29,7 @@ export function initRepoTopicBar() { mgrBtn.focus(); }); - mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLAnchorElement}) => { + mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLButtonElement}) => { lastErrorToast?.hideToast(); const topics = editDiv.querySelector('input[name=topics]').value; From 156a1954ef4b2b3c6f91e41c3e9beeb1cecac4ef Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 5 Dec 2024 09:35:16 +0800 Subject: [PATCH 32/48] Update web_src/js/features/citation.ts --- web_src/js/features/citation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index af8cedffbd995..7f89102b5745d 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -44,7 +44,7 @@ export async function initCitationFileCopyContent() { }; document.querySelector('#cite-repo-button')?.addEventListener('click', async () => { - $modal.modal('show'); + fomanticQuery(elModal).modal('show'); elModal.classList.add('is-loading'); try { From db453b5e6f812becac6f8f798cfc3d999f022703 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 5 Dec 2024 09:35:23 +0800 Subject: [PATCH 33/48] Update web_src/js/features/citation.ts --- web_src/js/features/citation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index 7f89102b5745d..64b2612b6acef 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -30,7 +30,6 @@ export async function initCitationFileCopyContent() { const citationCopyApa = document.querySelector('#citation-copy-apa'); const citationCopyBibtex = document.querySelector('#citation-copy-bibtex'); const inputContent = document.querySelector('#citation-copy-content'); - const elModal = document.querySelector('#cite-repo-modal'); const $modal = fomanticQuery('#cite-repo-modal'); if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return; From be1875e4675103e7eef8c93834fbf7a6c1c6f503 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 5 Dec 2024 09:40:36 +0800 Subject: [PATCH 34/48] Update web_src/js/features/citation.ts --- web_src/js/features/citation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index 64b2612b6acef..705554ce7af42 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -30,7 +30,7 @@ export async function initCitationFileCopyContent() { const citationCopyApa = document.querySelector('#citation-copy-apa'); const citationCopyBibtex = document.querySelector('#citation-copy-bibtex'); const inputContent = document.querySelector('#citation-copy-content'); - const $modal = fomanticQuery('#cite-repo-modal'); + const elModal = document.querySelector('#cite-repo-modal'); if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return; From 8a865de6530e6cc83d7791853d66aa044f05aa05 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 5 Dec 2024 01:52:41 +0000 Subject: [PATCH 35/48] remvoe unnecessary code --- templates/repo/home.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 7ae9348712a12..e13f3e2083ad0 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -6,7 +6,7 @@ {{template "repo/code/recently_pushed_new_branches" .}}
-
+
{{if .Repository.IsArchived}}
{{if .Repository.ArchivedUnix.IsZero}} From a4b0b0ee8d4166ebdbc620e7975bd2cbd7ae1aaa Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 5 Dec 2024 06:29:41 +0000 Subject: [PATCH 36/48] improve --- web_src/js/features/citation.ts | 54 +++++++++++++++++--------------- web_src/js/features/repo-home.ts | 8 +++-- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index 705554ce7af42..b703a84f675ec 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -42,34 +42,38 @@ export async function initCitationFileCopyContent() { citationCopyApa.classList.toggle('primary', !isBibtex); }; - document.querySelector('#cite-repo-button')?.addEventListener('click', async () => { - fomanticQuery(elModal).modal('show'); - elModal.classList.add('is-loading'); + const citeBtnMobile = document.querySelector('.only-mobile #cite-repo-button'); + const citeBtnDesktop = document.querySelector('.not-mobile #cite-repo-button'); + for (const citeBtn of [citeBtnMobile, citeBtnDesktop]) { + citeBtn?.addEventListener('click', async () => { + fomanticQuery(elModal).modal('show'); + elModal.classList.add('is-loading'); - try { try { - await initInputCitationValue(citationCopyApa, citationCopyBibtex); - } catch (e) { - console.error(`initCitationFileCopyContent error: ${e}`, e); - return; - } - updateUi(); - - citationCopyApa.addEventListener('click', () => { - localStorage.setItem('citation-copy-format', 'apa'); + try { + await initInputCitationValue(citationCopyApa, citationCopyBibtex); + } catch (e) { + console.error(`initCitationFileCopyContent error: ${e}`, e); + return; + } updateUi(); - }); - citationCopyBibtex.addEventListener('click', () => { - localStorage.setItem('citation-copy-format', 'bibtex'); - updateUi(); - }); + citationCopyApa.addEventListener('click', () => { + localStorage.setItem('citation-copy-format', 'apa'); + updateUi(); + }); - inputContent.addEventListener('click', () => { - inputContent.select(); - }); - } finally { - elModal.classList.remove('is-loading'); - } - }); + citationCopyBibtex.addEventListener('click', () => { + localStorage.setItem('citation-copy-format', 'bibtex'); + updateUi(); + }); + + inputContent.addEventListener('click', () => { + inputContent.select(); + }); + } finally { + elModal.classList.remove('is-loading'); + } + }); + } } diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 2ceb378e1be01..6713a4eeb91e5 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -7,10 +7,12 @@ import {fomanticQuery} from '../modules/fomantic/base.ts'; const {appSubUrl} = window.config; export function initRepoTopicBar() { - const mgrBtns = document.querySelectorAll('#manage_topic'); - if (mgrBtns.length === 0) return; + const mgrBtnMobile = document.querySelector('.only-mobile #manage_topic'); + const mgrBtnDesktop = document.querySelector('.not-mobile #manage_topic'); + + for (const mgrBtn of [mgrBtnMobile, mgrBtnDesktop]) { + if (!mgrBtn) continue; - for (const mgrBtn of mgrBtns) { const editDiv = mgrBtn.parentNode.querySelector('#topic_edit'); const viewDiv = mgrBtn.parentNode.querySelector('#repo-topics'); const topicDropdown = editDiv.querySelector('.ui.dropdown'); From 5dbb11053fdba391be814028ead0ff7806c4a69e Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 6 Dec 2024 04:33:28 +0000 Subject: [PATCH 37/48] convert into one line --- templates/repo/home.tmpl | 2 +- web_src/css/repo/home.css | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index e13f3e2083ad0..75096915c4d2e 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -171,7 +171,7 @@
- {{.LatestRelease.Title}} + {{.LatestRelease.Title}} {{ctx.Locale.Tr "latest"}}
diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index c47228ef8ddb2..5cfc8bede37b4 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -1,12 +1,3 @@ -.latest-releases { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - line-clamp: 2; - overflow: hidden; - text-overflow: ellipsis; -} - .language-stats { display: flex; gap: 2px; From 20cf941757f4c503fb80e2d4db515e3627f65898 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 6 Dec 2024 05:57:50 +0000 Subject: [PATCH 38/48] improve --- templates/repo/home.tmpl | 90 +------ templates/repo/home_search_code.tmpl | 6 - templates/repo/home_sidebar_foot.tmpl | 61 +++++ ...re_sidebar.tmpl => home_sidebar_head.tmpl} | 11 +- web_src/css/modules/flexcontainer.css | 7 - web_src/css/repo/home.css | 49 ++++ web_src/js/features/citation.ts | 52 ++-- web_src/js/features/repo-home.ts | 240 +++++++++--------- 8 files changed, 266 insertions(+), 250 deletions(-) delete mode 100644 templates/repo/home_search_code.tmpl create mode 100644 templates/repo/home_sidebar_foot.tmpl rename templates/repo/{home_share_sidebar.tmpl => home_sidebar_head.tmpl} (90%) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 75096915c4d2e..89c281fd40f5d 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -5,8 +5,8 @@ {{template "base/alert" .}} {{template "repo/code/recently_pushed_new_branches" .}} -
-
+
+
{{if .Repository.IsArchived}}
{{if .Repository.ArchivedUnix.IsZero}} @@ -20,14 +20,6 @@ {{$l := Eval $n "-" 1}} {{$isHomepage := (eq $n 0)}} - {{if $isHomepage}} -
- {{template "repo/home_search_code" .}} -
-
- {{template "repo/home_share_sidebar" .}} -
- {{end}} {{template "repo/sub_menu" .}}
@@ -143,80 +135,12 @@ {{template "repo/view_list" .}} {{end}}
- {{if $isHomepage}} -
-
- {{template "repo/home_search_code" .}} -
- -
-
- {{template "repo/home_share_sidebar" .}} -
- - {{if .LatestRelease}} -
-
- -
-
- {{svg "octicon-tag" 16}} -
-
-
-
- {{.LatestRelease.Title}} - {{ctx.Locale.Tr "latest"}} -
-
-
- {{DateUtils.TimeSince .LatestRelease.CreatedUnix}} -
-
-
-
-
- {{end}} - - {{if and (not .IsEmptyRepo) .LanguageStats}} -
-
-
- {{ctx.Locale.Tr "repo.repo_lang"}} -
- -
-
- {{range .LanguageStats}} -
- {{end}} -
-
- {{range .LanguageStats}} -
- - - {{if eq .Language "other"}} - {{ctx.Locale.Tr "repo.language_other"}} - {{else}} - {{.Language}} - {{end}} - - {{.Percentage}}% -
- {{end}} -
-
-
-
- {{end}} -
+
+ {{template "repo/home_sidebar_head" .}} +
+
+ {{template "repo/home_sidebar_foot" .}}
{{end}}
diff --git a/templates/repo/home_search_code.tmpl b/templates/repo/home_search_code.tmpl deleted file mode 100644 index 63ff6c148d2bd..0000000000000 --- a/templates/repo/home_search_code.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -
-
- - {{template "shared/search/button"}} -
-
diff --git a/templates/repo/home_sidebar_foot.tmpl b/templates/repo/home_sidebar_foot.tmpl new file mode 100644 index 0000000000000..990339a8d0a32 --- /dev/null +++ b/templates/repo/home_sidebar_foot.tmpl @@ -0,0 +1,61 @@ +{{if .LatestRelease}} +
+
+ +
+
+ {{svg "octicon-tag" 16}} +
+
+
+
+ {{.LatestRelease.Title}} + {{ctx.Locale.Tr "latest"}} +
+
+
+ {{DateUtils.TimeSince .LatestRelease.CreatedUnix}} +
+
+
+
+
+{{end}} + +{{if and (not .IsEmptyRepo) .LanguageStats}} +
+
+
+ {{ctx.Locale.Tr "repo.repo_lang"}} +
+ +
+
+ {{range .LanguageStats}} +
+ {{end}} +
+
+ {{range .LanguageStats}} +
+ + + {{if eq .Language "other"}} + {{ctx.Locale.Tr "repo.language_other"}} + {{else}} + {{.Language}} + {{end}} + + {{.Percentage}}% +
+ {{end}} +
+
+
+
+{{end}} \ No newline at end of file diff --git a/templates/repo/home_share_sidebar.tmpl b/templates/repo/home_sidebar_head.tmpl similarity index 90% rename from templates/repo/home_share_sidebar.tmpl rename to templates/repo/home_sidebar_head.tmpl index 1bde9bbfee78e..4ea6f2dce844c 100644 --- a/templates/repo/home_share_sidebar.tmpl +++ b/templates/repo/home_sidebar_head.tmpl @@ -1,3 +1,12 @@ +
+
+
+ + {{template "shared/search/button"}} +
+
+
+
@@ -53,4 +62,4 @@
{{end}}
-
+
\ No newline at end of file diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 02fc702f58211..5d4e12cc129b1 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -21,10 +21,6 @@ min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */ } -.flex-container-repo { - width: 25%; -} - @media (max-width: 767.98px) { .flex-container { flex-direction: column; @@ -34,7 +30,4 @@ order: -1; width: auto; } - .flex-container-repo { - width: auto; - } } diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index 5cfc8bede37b4..3884718b4eb17 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -1,3 +1,52 @@ +.repo-home-content { + display: grid; + grid-template-columns: auto 300px; + grid-template-rows: auto auto 1fr; +} + +.repo-home-main { + grid-column: 1; + grid-row: 1 / 4; +} + +.repo-home-head { + grid-column: 2; + grid-row: 1; + padding-left: 10px; +} + +.repo-home-foot { + grid-column: 2; + grid-row: 2; + padding-left: 10px; +} + +.repo-home-foot > :first-child { + border-top: 1px solid var(--color-secondary); /* same to .flex-list > .flex-item + .flex-item { */ +} + +@media (max-width: 767.98px) { + .repo-home-content { + grid-template-columns: 100%; + grid-template-rows: auto auto auto; + } + .repo-home-main { + grid-column: 1; + grid-row: 2; + } + .repo-home-head { + grid-column: 1; + grid-row: 1; + } + .repo-home-foot { + grid-column: 1; + grid-row: 3; + } + .repo-home-foot > :first-child { + border-top: 0; + } +} + .language-stats { display: flex; gap: 2px; diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts index b703a84f675ec..fc5bb38f0aeca 100644 --- a/web_src/js/features/citation.ts +++ b/web_src/js/features/citation.ts @@ -30,7 +30,6 @@ export async function initCitationFileCopyContent() { const citationCopyApa = document.querySelector('#citation-copy-apa'); const citationCopyBibtex = document.querySelector('#citation-copy-bibtex'); const inputContent = document.querySelector('#citation-copy-content'); - const elModal = document.querySelector('#cite-repo-modal'); if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return; @@ -42,38 +41,29 @@ export async function initCitationFileCopyContent() { citationCopyApa.classList.toggle('primary', !isBibtex); }; - const citeBtnMobile = document.querySelector('.only-mobile #cite-repo-button'); - const citeBtnDesktop = document.querySelector('.not-mobile #cite-repo-button'); - for (const citeBtn of [citeBtnMobile, citeBtnDesktop]) { - citeBtn?.addEventListener('click', async () => { - fomanticQuery(elModal).modal('show'); - elModal.classList.add('is-loading'); + document.querySelector('#cite-repo-button')?.addEventListener('click', async () => { + try { + await initInputCitationValue(citationCopyApa, citationCopyBibtex); + } catch (e) { + console.error(`initCitationFileCopyContent error: ${e}`, e); + return; + } + updateUi(); - try { - try { - await initInputCitationValue(citationCopyApa, citationCopyBibtex); - } catch (e) { - console.error(`initCitationFileCopyContent error: ${e}`, e); - return; - } - updateUi(); - - citationCopyApa.addEventListener('click', () => { - localStorage.setItem('citation-copy-format', 'apa'); - updateUi(); - }); + citationCopyApa.addEventListener('click', () => { + localStorage.setItem('citation-copy-format', 'apa'); + updateUi(); + }); - citationCopyBibtex.addEventListener('click', () => { - localStorage.setItem('citation-copy-format', 'bibtex'); - updateUi(); - }); + citationCopyBibtex.addEventListener('click', () => { + localStorage.setItem('citation-copy-format', 'bibtex'); + updateUi(); + }); - inputContent.addEventListener('click', () => { - inputContent.select(); - }); - } finally { - elModal.classList.remove('is-loading'); - } + inputContent.addEventListener('click', () => { + inputContent.select(); }); - } + + fomanticQuery('#cite-repo-modal').modal('show'); + }); } diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 6713a4eeb91e5..793a4d5722395 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -7,146 +7,142 @@ import {fomanticQuery} from '../modules/fomantic/base.ts'; const {appSubUrl} = window.config; export function initRepoTopicBar() { - const mgrBtnMobile = document.querySelector('.only-mobile #manage_topic'); - const mgrBtnDesktop = document.querySelector('.not-mobile #manage_topic'); + const mgrBtn = document.querySelector('#manage_topic'); + if (!mgrBtn) return; - for (const mgrBtn of [mgrBtnMobile, mgrBtnDesktop]) { - if (!mgrBtn) continue; + const editDiv = mgrBtn.parentNode.querySelector('#topic_edit'); + const viewDiv = mgrBtn.parentNode.querySelector('#repo-topics'); + const topicDropdown = editDiv.querySelector('.ui.dropdown'); + let lastErrorToast; - const editDiv = mgrBtn.parentNode.querySelector('#topic_edit'); - const viewDiv = mgrBtn.parentNode.querySelector('#repo-topics'); - const topicDropdown = editDiv.querySelector('.ui.dropdown'); - let lastErrorToast; + mgrBtn.addEventListener('click', () => { + hideElem(viewDiv); + showElem(editDiv); + topicDropdown.querySelector('input.search').focus(); + }); - mgrBtn.addEventListener('click', () => { - hideElem(viewDiv); - showElem(editDiv); - topicDropdown.querySelector('input.search').focus(); - }); + mgrBtn.parentNode.querySelector('#cancel_topic_edit').addEventListener('click', () => { + lastErrorToast?.hideToast(); + hideElem(editDiv); + showElem(viewDiv); + mgrBtn.focus(); + }); - mgrBtn.parentNode.querySelector('#cancel_topic_edit').addEventListener('click', () => { - lastErrorToast?.hideToast(); - hideElem(editDiv); - showElem(viewDiv); - mgrBtn.focus(); - }); + mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLButtonElement}) => { + lastErrorToast?.hideToast(); + const topics = editDiv.querySelector('input[name=topics]').value; - mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLButtonElement}) => { - lastErrorToast?.hideToast(); - const topics = editDiv.querySelector('input[name=topics]').value; + const data = new FormData(); + data.append('topics', topics); - const data = new FormData(); - data.append('topics', topics); + const response = await POST(e.target.getAttribute('data-link'), {data}); - const response = await POST(e.target.getAttribute('data-link'), {data}); - - if (response.ok) { - const responseData = await response.json(); - if (responseData.status === 'ok') { - queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove()); - if (topics.length) { - const topicArray = topics.split(','); - topicArray.sort(); - for (const topic of topicArray) { - // it should match the code in repo/home.tmpl - // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div - const link = document.createElement('a'); - link.classList.add('repo-topic', 'ui', 'large', 'label'); - link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; - link.textContent = topic; - viewDiv.append(link); - } + if (response.ok) { + const responseData = await response.json(); + if (responseData.status === 'ok') { + queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove()); + if (topics.length) { + const topicArray = topics.split(','); + topicArray.sort(); + for (const topic of topicArray) { + // it should match the code in repo/home.tmpl + // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div + const link = document.createElement('a'); + link.classList.add('repo-topic', 'ui', 'large', 'label'); + link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; + link.textContent = topic; + viewDiv.append(link); } - hideElem(editDiv); - showElem(viewDiv); } - } else if (response.status === 422) { - // how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save" - const responseData = await response.json(); - lastErrorToast = showErrorToast(responseData.message, {duration: 5000}); - if (responseData.invalidTopics && responseData.invalidTopics.length > 0) { - const {invalidTopics} = responseData; - const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label'); - for (const [index, value] of topics.split(',').entries()) { - if (invalidTopics.includes(value)) { - topicLabels[index].classList.remove('green'); - topicLabels[index].classList.add('red'); - } + hideElem(editDiv); + showElem(viewDiv); + } + } else if (response.status === 422) { + // how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save" + const responseData = await response.json(); + lastErrorToast = showErrorToast(responseData.message, {duration: 5000}); + if (responseData.invalidTopics && responseData.invalidTopics.length > 0) { + const {invalidTopics} = responseData; + const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label'); + for (const [index, value] of topics.split(',').entries()) { + if (invalidTopics.includes(value)) { + topicLabels[index].classList.remove('green'); + topicLabels[index].classList.add('red'); } } } - }); + } + }); - fomanticQuery(topicDropdown).dropdown({ - allowAdditions: true, - forceSelection: false, - fullTextSearch: 'exact', - fields: {name: 'description', value: 'data-value'}, - saveRemoteData: false, - label: { - transition: 'horizontal flip', - duration: 200, - variation: false, - }, - apiSettings: { - url: `${appSubUrl}/explore/topics/search?q={query}`, - throttle: 500, - cache: false, - onResponse(res) { - const formattedResponse = { - success: false, - results: [], - }; - const query = stripTags(this.urlData.query.trim()); - let found_query = false; - const current_topics = []; - for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) { - current_topics.push(el.getAttribute('data-value')); - } + fomanticQuery(topicDropdown).dropdown({ + allowAdditions: true, + forceSelection: false, + fullTextSearch: 'exact', + fields: {name: 'description', value: 'data-value'}, + saveRemoteData: false, + label: { + transition: 'horizontal flip', + duration: 200, + variation: false, + }, + apiSettings: { + url: `${appSubUrl}/explore/topics/search?q={query}`, + throttle: 500, + cache: false, + onResponse(res) { + const formattedResponse = { + success: false, + results: [], + }; + const query = stripTags(this.urlData.query.trim()); + let found_query = false; + const current_topics = []; + for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) { + current_topics.push(el.getAttribute('data-value')); + } - if (res.topics) { - let found = false; - for (const {topic_name} of res.topics) { - // skip currently added tags - if (current_topics.includes(topic_name)) { - continue; - } + if (res.topics) { + let found = false; + for (const {topic_name} of res.topics) { + // skip currently added tags + if (current_topics.includes(topic_name)) { + continue; + } - if (topic_name.toLowerCase() === query.toLowerCase()) { - found_query = true; - } - formattedResponse.results.push({description: topic_name, 'data-value': topic_name}); - found = true; + if (topic_name.toLowerCase() === query.toLowerCase()) { + found_query = true; } - formattedResponse.success = found; + formattedResponse.results.push({description: topic_name, 'data-value': topic_name}); + found = true; } + formattedResponse.success = found; + } - if (query.length > 0 && !found_query) { - formattedResponse.success = true; - formattedResponse.results.unshift({description: query, 'data-value': query}); - } else if (query.length > 0 && found_query) { - formattedResponse.results.sort((a, b) => { - if (a.description.toLowerCase() === query.toLowerCase()) return -1; - if (b.description.toLowerCase() === query.toLowerCase()) return 1; - if (a.description > b.description) return -1; - if (a.description < b.description) return 1; - return 0; - }); - } + if (query.length > 0 && !found_query) { + formattedResponse.success = true; + formattedResponse.results.unshift({description: query, 'data-value': query}); + } else if (query.length > 0 && found_query) { + formattedResponse.results.sort((a, b) => { + if (a.description.toLowerCase() === query.toLowerCase()) return -1; + if (b.description.toLowerCase() === query.toLowerCase()) return 1; + if (a.description > b.description) return -1; + if (a.description < b.description) return 1; + return 0; + }); + } - return formattedResponse; - }, - }, - onLabelCreate(value) { - value = value.toLowerCase().trim(); - this.attr('data-value', value).contents().first().replaceWith(value); - return fomanticQuery(this); - }, - onAdd(addedValue, _addedText, $addedChoice) { - addedValue = addedValue.toLowerCase().trim(); - $addedChoice[0].setAttribute('data-value', addedValue); - $addedChoice[0].setAttribute('data-text', addedValue); + return formattedResponse; }, - }); - } + }, + onLabelCreate(value) { + value = value.toLowerCase().trim(); + this.attr('data-value', value).contents().first().replaceWith(value); + return fomanticQuery(this); + }, + onAdd(addedValue, _addedText, $addedChoice) { + addedValue = addedValue.toLowerCase().trim(); + $addedChoice[0].setAttribute('data-value', addedValue); + $addedChoice[0].setAttribute('data-text', addedValue); + }, + }); } From e67c4c1e8b2495ec6b8991cddb11074ddf2164b2 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 6 Dec 2024 06:00:54 +0000 Subject: [PATCH 39/48] improve --- web_src/js/features/repo-home.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 793a4d5722395..f80a9a0a1b336 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -10,8 +10,8 @@ export function initRepoTopicBar() { const mgrBtn = document.querySelector('#manage_topic'); if (!mgrBtn) return; - const editDiv = mgrBtn.parentNode.querySelector('#topic_edit'); - const viewDiv = mgrBtn.parentNode.querySelector('#repo-topics'); + const editDiv = document.querySelector('#topic_edit'); + const viewDiv = document.querySelector('#repo-topics'); const topicDropdown = editDiv.querySelector('.ui.dropdown'); let lastErrorToast; @@ -21,14 +21,14 @@ export function initRepoTopicBar() { topicDropdown.querySelector('input.search').focus(); }); - mgrBtn.parentNode.querySelector('#cancel_topic_edit').addEventListener('click', () => { + document.querySelector('#cancel_topic_edit').addEventListener('click', () => { lastErrorToast?.hideToast(); hideElem(editDiv); showElem(viewDiv); mgrBtn.focus(); }); - mgrBtn.parentNode.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLButtonElement}) => { + document.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLButtonElement}) => { lastErrorToast?.hideToast(); const topics = editDiv.querySelector('input[name=topics]').value; @@ -51,7 +51,7 @@ export function initRepoTopicBar() { link.classList.add('repo-topic', 'ui', 'large', 'label'); link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; link.textContent = topic; - viewDiv.append(link); + mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button } } hideElem(editDiv); From 15a16000d01a0fedbe057fdc23a43ee8a27c41f2 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 6 Dec 2024 06:12:40 +0000 Subject: [PATCH 40/48] fix lint --- templates/repo/home_sidebar_foot.tmpl | 110 +++++++++++++------------- templates/repo/home_sidebar_head.tmpl | 2 +- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/templates/repo/home_sidebar_foot.tmpl b/templates/repo/home_sidebar_foot.tmpl index 990339a8d0a32..af2ce00539754 100644 --- a/templates/repo/home_sidebar_foot.tmpl +++ b/templates/repo/home_sidebar_foot.tmpl @@ -1,61 +1,61 @@ {{if .LatestRelease}} -
-
- -
-
- {{svg "octicon-tag" 16}} -
-
-
-
- {{.LatestRelease.Title}} - {{ctx.Locale.Tr "latest"}} -
-
-
- {{DateUtils.TimeSince .LatestRelease.CreatedUnix}} -
-
-
-
-
+
+
+ +
+
+ {{svg "octicon-tag" 16}} +
+
+
+
+ {{.LatestRelease.Title}} + {{ctx.Locale.Tr "latest"}} +
+
+
+ {{DateUtils.TimeSince .LatestRelease.CreatedUnix}} +
+
+
+
+
{{end}} {{if and (not .IsEmptyRepo) .LanguageStats}} -
-
-
- {{ctx.Locale.Tr "repo.repo_lang"}} -
+
+
+
+ {{ctx.Locale.Tr "repo.repo_lang"}} +
-
-
- {{range .LanguageStats}} -
- {{end}} -
-
- {{range .LanguageStats}} -
- - - {{if eq .Language "other"}} - {{ctx.Locale.Tr "repo.language_other"}} - {{else}} - {{.Language}} - {{end}} - - {{.Percentage}}% -
- {{end}} -
-
-
-
+
+
+ {{range .LanguageStats}} +
+ {{end}} +
+
+ {{range .LanguageStats}} +
+ + + {{if eq .Language "other"}} + {{ctx.Locale.Tr "repo.language_other"}} + {{else}} + {{.Language}} + {{end}} + + {{.Percentage}}% +
+ {{end}} +
+
+
+
{{end}} \ No newline at end of file diff --git a/templates/repo/home_sidebar_head.tmpl b/templates/repo/home_sidebar_head.tmpl index 4ea6f2dce844c..c2554335a0fa6 100644 --- a/templates/repo/home_sidebar_head.tmpl +++ b/templates/repo/home_sidebar_head.tmpl @@ -62,4 +62,4 @@
{{end}}
-
\ No newline at end of file +
From 16d77d8b7169052d9cf00cb4fb65be521c7bae15 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 6 Dec 2024 06:41:10 +0000 Subject: [PATCH 41/48] fix lint --- templates/repo/home_sidebar_foot.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/home_sidebar_foot.tmpl b/templates/repo/home_sidebar_foot.tmpl index af2ce00539754..039764ee35439 100644 --- a/templates/repo/home_sidebar_foot.tmpl +++ b/templates/repo/home_sidebar_foot.tmpl @@ -58,4 +58,4 @@
-{{end}} \ No newline at end of file +{{end}} From b83d4d01b472c9bd2c11e2c55c416589220a511f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 16:15:00 +0800 Subject: [PATCH 42/48] fix layout --- web_src/css/repo/home.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index 3884718b4eb17..65cb05ca5f43c 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -1,5 +1,6 @@ .repo-home-content { display: grid; + gap: 10px; grid-template-columns: auto 300px; grid-template-rows: auto auto 1fr; } @@ -22,7 +23,7 @@ } .repo-home-foot > :first-child { - border-top: 1px solid var(--color-secondary); /* same to .flex-list > .flex-item + .flex-item { */ + border-top: 1px solid var(--color-secondary); /* same to .flex-list > .flex-item + .flex-item */ } @media (max-width: 767.98px) { @@ -53,7 +54,7 @@ padding: 0; height: 10px; white-space: nowrap; - border-radius: 0 0 3px 3px !important; + border-radius: 5px; overflow: hidden; width: 100%; margin-top: 1rem; From 2aa1b193173f5c73eeb47a2700a6dc5be84c7e25 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 16:19:45 +0800 Subject: [PATCH 43/48] the code SHOULD and MUST match --- templates/repo/home_sidebar_head.tmpl | 2 +- web_src/js/features/repo-home.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/home_sidebar_head.tmpl b/templates/repo/home_sidebar_head.tmpl index c2554335a0fa6..fa3eec1d8c2fa 100644 --- a/templates/repo/home_sidebar_head.tmpl +++ b/templates/repo/home_sidebar_head.tmpl @@ -19,7 +19,7 @@ {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}}
- {{/* it should match the code in issue-home.js */}} + {{/* !!!! it SHOULD and MUST match the code in issue-home.js */}} {{range .Topics}}{{.Name}}{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index f80a9a0a1b336..048b1c8673a70 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -45,10 +45,10 @@ export function initRepoTopicBar() { const topicArray = topics.split(','); topicArray.sort(); for (const topic of topicArray) { - // it should match the code in repo/home.tmpl // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div + // !!!! it SHOULD and MUST match the code in "home_sidebar_head.tmpl" !!!! const link = document.createElement('a'); - link.classList.add('repo-topic', 'ui', 'large', 'label'); + link.classList.add('repo-topic', 'ui', 'large', 'label', 'gt-ellipsis'); link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; link.textContent = topic; mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button From 0538c5a92928b47788ea9ef1e4f2a151b0057656 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 19:24:07 +0800 Subject: [PATCH 44/48] temp fix --- templates/repo/home.tmpl | 83 ++++++++++--------- ...bar_foot.tmpl => home_sidebar_bottom.tmpl} | 16 ++-- templates/repo/home_sidebar_head.tmpl | 65 --------------- templates/repo/home_sidebar_top.tmpl | 67 +++++++++++++++ web_src/css/repo.css | 12 --- web_src/css/repo/home.css | 30 ++++--- web_src/js/features/repo-home.ts | 4 +- 7 files changed, 132 insertions(+), 145 deletions(-) rename templates/repo/{home_sidebar_foot.tmpl => home_sidebar_bottom.tmpl} (88%) delete mode 100644 templates/repo/home_sidebar_head.tmpl create mode 100644 templates/repo/home_sidebar_top.tmpl diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 89c281fd40f5d..63bf3eef0f5c3 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -3,43 +3,43 @@ {{template "repo/header" .}}
{{template "base/alert" .}} - {{template "repo/code/recently_pushed_new_branches" .}} -
-
- {{if .Repository.IsArchived}} -
- {{if .Repository.ArchivedUnix.IsZero}} - {{ctx.Locale.Tr "repo.archive.title"}} - {{else}} - {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}} - {{end}} -
+ {{if .Repository.IsArchived}} +
+ {{if .Repository.ArchivedUnix.IsZero}} + {{ctx.Locale.Tr "repo.archive.title"}} + {{else}} + {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}} {{end}} - {{$n := len .TreeNames}} - {{$l := Eval $n "-" 1}} - {{$isHomepage := (eq $n 0)}} +
+ {{end}} - {{template "repo/sub_menu" .}} + {{template "repo/code/recently_pushed_new_branches" .}} -
+ {{$treeNamesLen := len .TreeNames}} + {{$isTreePathRoot := eq $treeNamesLen 0}} + {{$showSidebar := $isTreePathRoot}} +
+
+ {{template "repo/sub_menu" .}} +
{{$branchDropdownCurrentRefType := "branch"}} {{$branchDropdownCurrentRefShortName := .BranchName}} {{if .IsViewTag}} - {{$branchDropdownCurrentRefType = "tag"}} - {{$branchDropdownCurrentRefShortName = .TagName}} + {{$branchDropdownCurrentRefType = "tag"}} + {{$branchDropdownCurrentRefShortName = .TagName}} {{end}} {{template "repo/branch_dropdown" dict - "Repository" .Repository - "ShowTabBranches" true - "ShowTabTags" true - "CurrentRefType" $branchDropdownCurrentRefType - "CurrentRefShortName" $branchDropdownCurrentRefShortName - "CurrentTreePath" .TreePath - "RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}" - "AllowCreateNewRef" .CanCreateBranch - "ShowViewAllRefsEntry" true + "Repository" .Repository + "ShowTabBranches" true + "ShowTabTags" true + "CurrentRefType" $branchDropdownCurrentRefType + "CurrentRefShortName" $branchDropdownCurrentRefShortName + "CurrentTreePath" .TreePath + "RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}" + "AllowCreateNewRef" .CanCreateBranch + "ShowViewAllRefsEntry" true }} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{$cmpBranch := ""}} @@ -53,8 +53,9 @@ {{svg "octicon-git-pull-request"}} {{end}} - - {{if $isHomepage}} + + + {{if $isTreePathRoot}} {{ctx.Locale.Tr "repo.find_file.go_to_file"}} {{end}} @@ -78,17 +79,19 @@ {{end}} - {{if and $isHomepage (.Repository.IsTemplate)}} + {{if and $isTreePathRoot .Repository.IsTemplate}} {{ctx.Locale.Tr "repo.use_template"}} {{end}} - {{if not $isHomepage}} + + {{if not $isTreePathRoot}} + {{$treeNameIdxLast := Eval $treeNamesLen "-" 1}} {{StringUtils.EllipsisString .Repository.Name 30}} {{- range $i, $v := .TreeNames -}} / - {{- if eq $i $l -}} + {{- if eq $i $treeNameIdxLast -}} {{$v}} {{- else -}} @@ -98,9 +101,10 @@ {{end}}
+
- {{if $isHomepage}} + {{if $isTreePathRoot}}
{{template "repo/clone_buttons" .}}
{{template "repo/cite/cite_modal" .}} {{end}} - {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} + {{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} @@ -135,13 +139,10 @@ {{template "repo/view_list" .}} {{end}}
- {{if $isHomepage}} -
- {{template "repo/home_sidebar_head" .}} -
-
- {{template "repo/home_sidebar_foot" .}} -
+ + {{if $showSidebar}} +
{{template "repo/home_sidebar_top" .}}
+
{{template "repo/home_sidebar_bottom" .}}
{{end}}
diff --git a/templates/repo/home_sidebar_foot.tmpl b/templates/repo/home_sidebar_bottom.tmpl similarity index 88% rename from templates/repo/home_sidebar_foot.tmpl rename to templates/repo/home_sidebar_bottom.tmpl index 039764ee35439..fbc277a6c011e 100644 --- a/templates/repo/home_sidebar_foot.tmpl +++ b/templates/repo/home_sidebar_bottom.tmpl @@ -1,4 +1,5 @@ -{{if .LatestRelease}} +
+ {{if .LatestRelease}}
@@ -25,9 +26,9 @@
-{{end}} + {{end}} -{{if and (not .IsEmptyRepo) .LanguageStats}} + {{if and (not .IsEmptyRepo) .LanguageStats}}
@@ -45,11 +46,7 @@
- {{if eq .Language "other"}} - {{ctx.Locale.Tr "repo.language_other"}} - {{else}} - {{.Language}} - {{end}} + {{Iif (eq .Language "other") (ctx.Locale.Tr "repo.language_other") .Language}} {{.Percentage}}%
@@ -58,4 +55,5 @@
-{{end}} + {{end}} +
diff --git a/templates/repo/home_sidebar_head.tmpl b/templates/repo/home_sidebar_head.tmpl deleted file mode 100644 index fa3eec1d8c2fa..0000000000000 --- a/templates/repo/home_sidebar_head.tmpl +++ /dev/null @@ -1,65 +0,0 @@ -
-
-
- - {{template "shared/search/button"}} -
-
-
- -
-
-
- {{ctx.Locale.Tr "repo.repo_desc"}} -
- {{if and (not .HideRepoInfo) (not .IsBlame)}} -
- {{- $description := .Repository.DescriptionHTML ctx -}} - {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} - {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}} -
-
- {{/* !!!! it SHOULD and MUST match the code in issue-home.js */}} - {{range .Topics}}{{.Name}}{{end}} -
- {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} - {{end}} - {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
- -
- - -
-
- {{end}} - {{if .ReadmeExist}} - - {{end}} - {{if .DetectedRepoLicenses}} - - {{end}} - {{if .CitiationExist}} - - {{end}} -
-
diff --git a/templates/repo/home_sidebar_top.tmpl b/templates/repo/home_sidebar_top.tmpl new file mode 100644 index 0000000000000..c428f1fbddbe7 --- /dev/null +++ b/templates/repo/home_sidebar_top.tmpl @@ -0,0 +1,67 @@ +
+
+ + {{template "shared/search/button"}} +
+
+ +
+
+
+
+ {{ctx.Locale.Tr "repo.repo_desc"}} +
+ {{if and (not .HideRepoInfo) (not .IsBlame)}} +
+ {{- $description := .Repository.DescriptionHTML ctx -}} + {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.repo_no_desc"}}{{end}} + {{if .Repository.Website}}{{svg "octicon-link"}}{{.Repository.Website}}{{end}} +
+
+ {{/* !!!! it SHOULD and MUST match the code in issue-home.js */}} + {{range .Topics}}{{.Name}}{{end}} +
+ {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} + + {{end}} + {{end}} + {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} +
+ +
+ + +
+
+ {{end}} + {{if .ReadmeExist}} + + {{end}} + {{if .DetectedRepoLicenses}} + + {{end}} + {{if .CitiationExist}} + + {{end}} +
+
+
diff --git a/web_src/css/repo.css b/web_src/css/repo.css index da55b986c3c71..3eebc0c477dc1 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -422,14 +422,6 @@ td .commit-summary { border-radius: 0 0 var(--border-radius) var(--border-radius); } -.repository.file.list .sidebar { - padding-left: 0; -} - -.repository.file.list .sidebar .svg { - width: 16px; -} - .repo-editor-header { width: 100%; } @@ -2162,10 +2154,6 @@ td .commit-summary { justify-content: flex-end; } -.repo-button-row[data-is-homepage="false"] .repo-button-row-right { - flex-grow: 0; -} - @media (max-width: 1200px) { .repository:not(.wiki) .repo-button-row { flex-direction: column; diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index 65cb05ca5f43c..f461c605a418d 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -1,49 +1,48 @@ -.repo-home-content { +.repo-grid-filelist-sidebar { display: grid; - gap: 10px; grid-template-columns: auto 300px; grid-template-rows: auto auto 1fr; } -.repo-home-main { +.repo-grid-filelist-sidebar .repo-home-filelist { grid-column: 1; grid-row: 1 / 4; } -.repo-home-head { +.repo-grid-filelist-sidebar .repo-home-sidebar-top { grid-column: 2; grid-row: 1; - padding-left: 10px; + padding-left: 1em; } - -.repo-home-foot { +.repo-grid-filelist-sidebar .repo-home-sidebar-bottom { grid-column: 2; grid-row: 2; - padding-left: 10px; + padding-left: 1em; } - -.repo-home-foot > :first-child { +.repo-home-sidebar-bottom > :first-child { border-top: 1px solid var(--color-secondary); /* same to .flex-list > .flex-item + .flex-item */ } @media (max-width: 767.98px) { - .repo-home-content { + .repo-grid-filelist-sidebar { grid-template-columns: 100%; grid-template-rows: auto auto auto; } - .repo-home-main { + .repo-grid-filelist-sidebar .repo-home-filelist { grid-column: 1; grid-row: 2; } - .repo-home-head { + .repo-grid-filelist-sidebar .repo-home-sidebar-top { grid-column: 1; grid-row: 1; + padding-left: 0; } - .repo-home-foot { + .repo-grid-filelist-sidebar .repo-home-sidebar-bottom { grid-column: 1; grid-row: 3; + padding-left: 0; } - .repo-home-foot > :first-child { + .repo-home-sidebar-bottom > :first-child { border-top: 0; } } @@ -68,7 +67,6 @@ .language-stats-details .item { height: 30px; - line-height: var(--line-height-default); display: flex; align-items: center; justify-content: center; diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index 048b1c8673a70..df52b87f5ae13 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -46,12 +46,12 @@ export function initRepoTopicBar() { topicArray.sort(); for (const topic of topicArray) { // TODO: sort items in topicDropdown, or items in edit div will have different order to the items in view div - // !!!! it SHOULD and MUST match the code in "home_sidebar_head.tmpl" !!!! + // !!!! it SHOULD and MUST match the code in "home_sidebar_top.tmpl" !!!! const link = document.createElement('a'); link.classList.add('repo-topic', 'ui', 'large', 'label', 'gt-ellipsis'); link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; link.textContent = topic; - mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button + viewDiv.append(link); } } hideElem(editDiv); From ca5bfa68f3062897be25a19302450e34b6b44491 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 20:26:07 +0800 Subject: [PATCH 45/48] refactor --- routers/web/repo/blame.go | 6 - routers/web/repo/branch.go | 1 - routers/web/repo/commit.go | 4 - routers/web/repo/release.go | 6 - routers/web/repo/view.go | 803 -------------------------------- routers/web/repo/view_file.go | 310 ++++++++++++ routers/web/repo/view_home.go | 348 ++++++++++++++ routers/web/repo/view_readme.go | 215 +++++++++ services/context/repo.go | 7 - 9 files changed, 873 insertions(+), 827 deletions(-) create mode 100644 routers/web/repo/view_file.go create mode 100644 routers/web/repo/view_home.go create mode 100644 routers/web/repo/view_readme.go diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 51da80e4d5be4..ad790875136c3 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -114,12 +114,6 @@ func RefBlame(ctx *context.Context) { ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile - // Get Topics of this repo - renderRepoTopics(ctx) - if ctx.Written() { - return - } - commitNames := processBlameParts(ctx, result.Parts) if ctx.Written() { return diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 4a62237838a4a..dc170742b93e4 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -89,7 +89,6 @@ func Branches(ctx *context.Context) { pager := context.NewPagination(int(branchesCount), pageSize, page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName ctx.HTML(http.StatusOK, tplBranch) } diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index c5652784fa012..6d53df7c10917 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -102,7 +102,6 @@ func Commits(ctx *context.Context) { pager := context.NewPagination(int(commitsCount), pageSize, page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName ctx.HTML(http.StatusOK, tplCommits) } @@ -219,8 +218,6 @@ func SearchCommits(ctx *context.Context) { } ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name - ctx.Data["RefName"] = ctx.Repo.RefName - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName ctx.HTML(http.StatusOK, tplCommits) } @@ -266,7 +263,6 @@ func FileHistory(ctx *context.Context) { pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName ctx.HTML(http.StatusOK, tplCommits) } diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index c178ba249166c..aaeb35bc8c29c 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -31,7 +31,6 @@ import ( "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" releaseservice "code.gitea.io/gitea/services/release" - repo_service "code.gitea.io/gitea/services/repository" ) const ( @@ -193,9 +192,6 @@ func Releases(ctx *context.Context) { pager := context.NewPagination(int(numReleases), listOptions.PageSize, listOptions.Page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager - - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName - ctx.HTML(http.StatusOK, tplReleasesList) } @@ -254,8 +250,6 @@ func TagsList(ctx *context.Context) { pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases) - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName - ctx.HTML(http.StatusOK, tplTagsList) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 3b95624ce9c78..e43841acd365c 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -5,18 +5,13 @@ package repo import ( - "bytes" gocontext "context" - "encoding/base64" "errors" "fmt" "html/template" - "image" "io" "net/http" "net/url" - "path" - "slices" "strings" "time" @@ -29,33 +24,21 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" - issue_model "code.gitea.io/gitea/models/issues" - access_model "code.gitea.io/gitea/models/perm/access" - "code.gitea.io/gitea/models/renderhelper" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" - repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/web/feed" "code.gitea.io/gitea/services/context" - issue_service "code.gitea.io/gitea/services/issue" repo_service "code.gitea.io/gitea/services/repository" - files_service "code.gitea.io/gitea/services/repository/files" - - "github.com/nektos/act/pkg/model" _ "golang.org/x/image/bmp" // for processing bmp images _ "golang.org/x/image/webp" // for processing webp images @@ -70,140 +53,6 @@ const ( tplMigrating base.TplName = "repo/migrate/migrating" ) -// locate a README for a tree in one of the supported paths. -// -// entries is passed to reduce calls to ListEntries(), so -// this has precondition: -// -// entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries() -// -// FIXME: There has to be a more efficient way of doing this -func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, tryWellKnownDirs bool) (string, *git.TreeEntry, error) { - // Create a list of extensions in priority order - // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md - // 2. Txt files - e.g. README.txt - // 3. No extension - e.g. README - exts := append(localizedExtensions(".md", ctx.Locale.Language()), ".txt", "") // sorted by priority - extCount := len(exts) - readmeFiles := make([]*git.TreeEntry, extCount+1) - - docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/) - for _, entry := range entries { - if tryWellKnownDirs && entry.IsDir() { - // as a special case for the top-level repo introduction README, - // fall back to subfolders, looking for e.g. docs/README.md, .gitea/README.zh-CN.txt, .github/README.txt, ... - // (note that docsEntries is ignored unless we are at the root) - lowerName := strings.ToLower(entry.Name()) - switch lowerName { - case "docs": - if entry.Name() == "docs" || docsEntries[0] == nil { - docsEntries[0] = entry - } - case ".gitea": - if entry.Name() == ".gitea" || docsEntries[1] == nil { - docsEntries[1] = entry - } - case ".github": - if entry.Name() == ".github" || docsEntries[2] == nil { - docsEntries[2] = entry - } - } - continue - } - if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok { - log.Debug("Potential readme file: %s", entry.Name()) - if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) { - if entry.IsLink() { - target, err := entry.FollowLinks() - if err != nil && !git.IsErrBadLink(err) { - return "", nil, err - } else if target != nil && (target.IsExecutable() || target.IsRegular()) { - readmeFiles[i] = entry - } - } else { - readmeFiles[i] = entry - } - } - } - } - var readmeFile *git.TreeEntry - for _, f := range readmeFiles { - if f != nil { - readmeFile = f - break - } - } - - if ctx.Repo.TreePath == "" && readmeFile == nil { - for _, subTreeEntry := range docsEntries { - if subTreeEntry == nil { - continue - } - subTree := subTreeEntry.Tree() - if subTree == nil { - // this should be impossible; if subTreeEntry exists so should this. - continue - } - childEntries, err := subTree.ListEntries() - if err != nil { - return "", nil, err - } - - subfolder, readmeFile, err := findReadmeFileInEntries(ctx, childEntries, false) - if err != nil && !git.IsErrNotExist(err) { - return "", nil, err - } - if readmeFile != nil { - return path.Join(subTreeEntry.Name(), subfolder), readmeFile, nil - } - } - } - - return "", readmeFile, nil -} - -func renderDirectory(ctx *context.Context) { - entries := renderDirectoryFiles(ctx, 1*time.Second) - if ctx.Written() { - return - } - - if ctx.Repo.TreePath != "" { - ctx.Data["HideRepoInfo"] = true - ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) - } - - subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true) - if err != nil { - ctx.ServerError("findReadmeFileInEntries", err) - return - } - - renderReadmeFile(ctx, subfolder, readmeFile) -} - -// localizedExtensions prepends the provided language code with and without a -// regional identifier to the provided extension. -// Note: the language code will always be lower-cased, if a region is present it must be separated with a `-` -// Note: ext should be prefixed with a `.` -func localizedExtensions(ext, languageCode string) (localizedExts []string) { - if len(languageCode) < 1 { - return []string{ext} - } - - lowerLangCode := "." + strings.ToLower(languageCode) - - if strings.Contains(lowerLangCode, "-") { - underscoreLangCode := strings.ReplaceAll(lowerLangCode, "-", "_") - indexOfDash := strings.Index(lowerLangCode, "-") - // e.g. [.zh-cn.md, .zh_cn.md, .zh.md, _zh.md, .md] - return []string{lowerLangCode + ext, underscoreLangCode + ext, lowerLangCode[:indexOfDash] + ext, "_" + lowerLangCode[1:indexOfDash] + ext, ext} - } - - // e.g. [.en.md, .md] - return []string{lowerLangCode + ext, ext} -} - type fileInfo struct { isTextFile bool isLFSFile bool @@ -261,85 +110,6 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil } -func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) { - target := readmeFile - if readmeFile != nil && readmeFile.IsLink() { - target, _ = readmeFile.FollowLinks() - } - if target == nil { - // if findReadmeFile() failed and/or gave us a broken symlink (which it shouldn't) - // simply skip rendering the README - return - } - - ctx.Data["RawFileLink"] = "" - ctx.Data["ReadmeInList"] = true - ctx.Data["ReadmeExist"] = true - ctx.Data["FileIsSymlink"] = readmeFile.IsLink() - - buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, target.Blob()) - if err != nil { - ctx.ServerError("getFileReader", err) - return - } - defer dataRc.Close() - - ctx.Data["FileIsText"] = fInfo.isTextFile - ctx.Data["FileName"] = path.Join(subfolder, readmeFile.Name()) - ctx.Data["FileSize"] = fInfo.fileSize - ctx.Data["IsLFSFile"] = fInfo.isLFSFile - - if fInfo.isLFSFile { - filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.Name())) - ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.Link(), url.PathEscape(fInfo.lfsMeta.Oid), url.PathEscape(filenameBase64)) - } - - if !fInfo.isTextFile { - return - } - - if fInfo.fileSize >= setting.UI.MaxDisplayFileSize { - // Pretend that this is a normal text file to display 'This file is too large to be shown' - ctx.Data["IsFileTooLarge"] = true - ctx.Data["IsTextFile"] = true - return - } - - rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) - - if markupType := markup.DetectMarkupTypeByFileName(readmeFile.Name()); markupType != "" { - ctx.Data["IsMarkup"] = true - ctx.Data["MarkupType"] = markupType - - rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ - CurrentRefPath: ctx.Repo.BranchNameSubURL(), - CurrentTreePath: path.Join(ctx.Repo.TreePath, subfolder), - }). - WithMarkupType(markupType). - WithRelativePath(path.Join(ctx.Repo.TreePath, subfolder, readmeFile.Name())) // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path). - - ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd) - if err != nil { - log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err) - delete(ctx.Data, "IsMarkup") - } - } - - if ctx.Data["IsMarkup"] != true { - ctx.Data["IsPlainText"] = true - content, err := io.ReadAll(rd) - if err != nil { - log.Error("Read readme content failed: %v", err) - } - contentEscaped := template.HTMLEscapeString(util.UnsafeBytesToString(content)) - ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale) - } - - if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { - ctx.Data["CanEditReadmeFile"] = true - } -} - func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool { // Show latest commit info of repository in table header, // or of directory if not in root directory. @@ -371,287 +141,6 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool { return true } -func renderFile(ctx *context.Context, entry *git.TreeEntry) { - ctx.Data["IsViewFile"] = true - ctx.Data["HideRepoInfo"] = true - blob := entry.Blob() - buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob) - if err != nil { - ctx.ServerError("getFileReader", err) - return - } - defer dataRc.Close() - - ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) - ctx.Data["FileIsSymlink"] = entry.IsLink() - ctx.Data["FileName"] = blob.Name() - ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) - - commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath) - if err != nil { - ctx.ServerError("GetCommitByPath", err) - return - } - - if !loadLatestCommitData(ctx, commit) { - return - } - - if ctx.Repo.TreePath == ".editorconfig" { - _, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit) - if editorconfigWarning != nil { - ctx.Data["FileWarning"] = strings.TrimSpace(editorconfigWarning.Error()) - } - if editorconfigErr != nil { - ctx.Data["FileError"] = strings.TrimSpace(editorconfigErr.Error()) - } - } else if issue_service.IsTemplateConfig(ctx.Repo.TreePath) { - _, issueConfigErr := issue_service.GetTemplateConfig(ctx.Repo.GitRepo, ctx.Repo.TreePath, ctx.Repo.Commit) - if issueConfigErr != nil { - ctx.Data["FileError"] = strings.TrimSpace(issueConfigErr.Error()) - } - } else if actions.IsWorkflow(ctx.Repo.TreePath) { - content, err := actions.GetContentFromEntry(entry) - if err != nil { - log.Error("actions.GetContentFromEntry: %v", err) - } - _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content)) - if workFlowErr != nil { - ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) - } - } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { - if data, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize); err == nil { - _, warnings := issue_model.GetCodeOwnersFromContent(ctx, data) - if len(warnings) > 0 { - ctx.Data["FileWarning"] = strings.Join(warnings, "\n") - } - } - } - - isDisplayingSource := ctx.FormString("display") == "source" - isDisplayingRendered := !isDisplayingSource - - if fInfo.isLFSFile { - ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) - } - - isRepresentableAsText := fInfo.st.IsRepresentableAsText() - if !isRepresentableAsText { - // If we can't show plain text, always try to render. - isDisplayingSource = false - isDisplayingRendered = true - } - ctx.Data["IsLFSFile"] = fInfo.isLFSFile - ctx.Data["FileSize"] = fInfo.fileSize - ctx.Data["IsTextFile"] = fInfo.isTextFile - ctx.Data["IsRepresentableAsText"] = isRepresentableAsText - ctx.Data["IsDisplayingSource"] = isDisplayingSource - ctx.Data["IsDisplayingRendered"] = isDisplayingRendered - ctx.Data["IsExecutable"] = entry.IsExecutable() - - isTextSource := fInfo.isTextFile || isDisplayingSource - ctx.Data["IsTextSource"] = isTextSource - if isTextSource { - ctx.Data["CanCopyContent"] = true - } - - // Check LFS Lock - lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath) - ctx.Data["LFSLock"] = lfsLock - if err != nil { - ctx.ServerError("GetTreePathLock", err) - return - } - if lfsLock != nil { - u, err := user_model.GetUserByID(ctx, lfsLock.OwnerID) - if err != nil { - ctx.ServerError("GetTreePathLock", err) - return - } - ctx.Data["LFSLockOwner"] = u.Name - ctx.Data["LFSLockOwnerHomeLink"] = u.HomeLink() - ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked") - } - - // Assume file is not editable first. - if fInfo.isLFSFile { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files") - } else if !isRepresentableAsText { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") - } - - switch { - case isRepresentableAsText: - if fInfo.fileSize >= setting.UI.MaxDisplayFileSize { - ctx.Data["IsFileTooLarge"] = true - break - } - - if fInfo.st.IsSvgImage() { - ctx.Data["IsImageFile"] = true - ctx.Data["CanCopyContent"] = true - ctx.Data["HasSourceRenderedToggle"] = true - } - - rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) - - shouldRenderSource := ctx.FormString("display") == "source" - readmeExist := util.IsReadmeFileName(blob.Name()) - ctx.Data["ReadmeExist"] = readmeExist - - markupType := markup.DetectMarkupTypeByFileName(blob.Name()) - if markupType == "" { - markupType = markup.DetectRendererType(blob.Name(), bytes.NewReader(buf)) - } - if markupType != "" { - ctx.Data["HasSourceRenderedToggle"] = true - } - if markupType != "" && !shouldRenderSource { - ctx.Data["IsMarkup"] = true - ctx.Data["MarkupType"] = markupType - metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx) - metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() - rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ - CurrentRefPath: ctx.Repo.BranchNameSubURL(), - CurrentTreePath: path.Dir(ctx.Repo.TreePath), - }). - WithMarkupType(markupType). - WithRelativePath(ctx.Repo.TreePath). - WithMetas(metas) - - ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd) - if err != nil { - ctx.ServerError("Render", err) - return - } - // to prevent iframe load third-party url - ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'") - } else { - buf, _ := io.ReadAll(rd) - - // The Open Group Base Specification: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html - // empty: 0 lines; "a": 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line; - // Gitea uses the definition (like most modern editors): - // empty: 0 lines; "a": 1 line; "a\n": 2 lines; "a\nb": 2 lines; - // When rendering, the last empty line is not rendered in UI, while the line-number is still counted, to tell users that the file contains a trailing EOL. - // To make the UI more consistent, it could use an icon mark to indicate that there is no trailing EOL, and show line-number as the rendered lines. - // This NumLines is only used for the display on the UI: "xxx lines" - if len(buf) == 0 { - ctx.Data["NumLines"] = 0 - } else { - ctx.Data["NumLines"] = bytes.Count(buf, []byte{'\n'}) + 1 - } - - language, err := files_service.TryGetContentLanguage(ctx.Repo.GitRepo, ctx.Repo.CommitID, ctx.Repo.TreePath) - if err != nil { - log.Error("Unable to get file language for %-v:%s. Error: %v", ctx.Repo.Repository, ctx.Repo.TreePath, err) - } - - fileContent, lexerName, err := highlight.File(blob.Name(), language, buf) - ctx.Data["LexerName"] = lexerName - if err != nil { - log.Error("highlight.File failed, fallback to plain text: %v", err) - fileContent = highlight.PlainText(buf) - } - status := &charset.EscapeStatus{} - statuses := make([]*charset.EscapeStatus, len(fileContent)) - for i, line := range fileContent { - statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale) - status = status.Or(statuses[i]) - } - ctx.Data["EscapeStatus"] = status - ctx.Data["FileContent"] = fileContent - ctx.Data["LineEscapeStatus"] = statuses - } - if !fInfo.isLFSFile { - if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { - if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { - ctx.Data["CanEditFile"] = false - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") - } else { - ctx.Data["CanEditFile"] = true - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") - } - } else if !ctx.Repo.IsViewBranch { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit") - } - } - - case fInfo.st.IsPDF(): - ctx.Data["IsPDFFile"] = true - case fInfo.st.IsVideo(): - ctx.Data["IsVideoFile"] = true - case fInfo.st.IsAudio(): - ctx.Data["IsAudioFile"] = true - case fInfo.st.IsImage() && (setting.UI.SVG.Enabled || !fInfo.st.IsSvgImage()): - ctx.Data["IsImageFile"] = true - ctx.Data["CanCopyContent"] = true - default: - if fInfo.fileSize >= setting.UI.MaxDisplayFileSize { - ctx.Data["IsFileTooLarge"] = true - break - } - - // TODO: this logic duplicates with "isRepresentableAsText=true", it is not the same as "LFSFileGet" in "lfs.go" - // It is used by "external renders", markupRender will execute external programs to get rendered content. - if markupType := markup.DetectMarkupTypeByFileName(blob.Name()); markupType != "" { - rd := io.MultiReader(bytes.NewReader(buf), dataRc) - ctx.Data["IsMarkup"] = true - ctx.Data["MarkupType"] = markupType - - rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ - CurrentRefPath: ctx.Repo.BranchNameSubURL(), - CurrentTreePath: path.Dir(ctx.Repo.TreePath), - }). - WithMarkupType(markupType). - WithRelativePath(ctx.Repo.TreePath) - - ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd) - if err != nil { - ctx.ServerError("Render", err) - return - } - } - } - - if ctx.Repo.GitRepo != nil { - checker, deferable := ctx.Repo.GitRepo.CheckAttributeReader(ctx.Repo.CommitID) - if checker != nil { - defer deferable() - attrs, err := checker.CheckPath(ctx.Repo.TreePath) - if err == nil { - ctx.Data["IsVendored"] = git.AttributeToBool(attrs, git.AttributeLinguistVendored).Value() - ctx.Data["IsGenerated"] = git.AttributeToBool(attrs, git.AttributeLinguistGenerated).Value() - } - } - } - - if fInfo.st.IsImage() && !fInfo.st.IsSvgImage() { - img, _, err := image.DecodeConfig(bytes.NewReader(buf)) - if err == nil { - // There are Image formats go can't decode - // Instead of throwing an error in that case, we show the size only when we can decode - ctx.Data["ImageSize"] = fmt.Sprintf("%dx%dpx", img.Width, img.Height) - } - } - - if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { - if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { - ctx.Data["CanDeleteFile"] = false - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") - } else { - ctx.Data["CanDeleteFile"] = true - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") - } - } else if !ctx.Repo.IsViewBranch { - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access") - } -} - func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input io.Reader) (escaped *charset.EscapeStatus, output template.HTML, err error) { markupRd, markupWr := io.Pipe() defer markupWr.Close() @@ -728,59 +217,6 @@ func checkHomeCodeViewable(ctx *context.Context) { ctx.NotFound("Home", errors.New(ctx.Locale.TrString("units.error.no_unit_allowed_repo"))) } -func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) { - if entry.Name() != "" { - return - } - tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath) - if err != nil { - HandleGitError(ctx, "Repo.Commit.SubTree", err) - return - } - allEntries, err := tree.ListEntries() - if err != nil { - ctx.ServerError("ListEntries", err) - return - } - for _, entry := range allEntries { - if entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" { - // Read Citation file contents - if content, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { - log.Error("checkCitationFile: GetBlobContent: %v", err) - } else { - ctx.Data["CitiationExist"] = true - ctx.PageData["citationFileContent"] = content - break - } - } - } -} - -// Home render repository home page -func Home(ctx *context.Context) { - if setting.Other.EnableFeed { - isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam(":reponame"), ctx.Req) - if isFeed { - switch { - case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType): - feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType) - case ctx.Repo.TreePath == "": - feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType) - case ctx.Repo.TreePath != "": - feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType) - } - return - } - } - - checkHomeCodeViewable(ctx) - if ctx.Written() { - return - } - - renderHomeCode(ctx) -} - // LastCommit returns lastCommit data for the provided branch/tag/commit and directory (in url) and filenames in body func LastCommit(ctx *context.Context) { checkHomeCodeViewable(ctx) @@ -877,245 +313,6 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri return allEntries } -func renderLanguageStats(ctx *context.Context) { - langs, err := repo_model.GetTopLanguageStats(ctx, ctx.Repo.Repository, 5) - if err != nil { - ctx.ServerError("Repo.GetTopLanguageStats", err) - return - } - - ctx.Data["LanguageStats"] = langs -} - -func renderLatestRelease(ctx *context.Context) { - if !ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeReleases) { - return - } - - release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) - if err != nil && !repo_model.IsErrReleaseNotExist(err) { - ctx.ServerError("GetLatestReleaseByRepoID", err) - return - } - - if release != nil { - if err = release.LoadAttributes(ctx); err != nil { - ctx.ServerError("release.LoadAttributes", err) - return - } - ctx.Data["LatestRelease"] = release - } -} - -func renderRepoTopics(ctx *context.Context) { - topics, err := db.Find[repo_model.Topic](ctx, &repo_model.FindTopicOptions{ - RepoID: ctx.Repo.Repository.ID, - }) - if err != nil { - ctx.ServerError("models.FindTopics", err) - return - } - ctx.Data["Topics"] = topics -} - -func prepareOpenWithEditorApps(ctx *context.Context) { - var tmplApps []map[string]any - apps := setting.Config().Repository.OpenWithEditorApps.Value(ctx) - if len(apps) == 0 { - apps = setting.DefaultOpenWithEditorApps() - } - for _, app := range apps { - schema, _, _ := strings.Cut(app.OpenURL, ":") - var iconHTML template.HTML - if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" { - iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16, "tw-mr-2") - } else { - iconHTML = svg.RenderHTML("gitea-git", 16, "tw-mr-2") // TODO: it could support user's customized icon in the future - } - tmplApps = append(tmplApps, map[string]any{ - "DisplayName": app.DisplayName, - "OpenURL": app.OpenURL, - "IconHTML": iconHTML, - }) - } - ctx.Data["OpenWithEditorApps"] = tmplApps -} - -func renderHomeCode(ctx *context.Context) { - ctx.Data["PageIsViewCode"] = true - ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled - prepareOpenWithEditorApps(ctx) - - if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() { - showEmpty := true - var err error - if ctx.Repo.GitRepo != nil { - showEmpty, err = ctx.Repo.GitRepo.IsEmpty() - if err != nil { - log.Error("GitRepo.IsEmpty: %v", err) - ctx.Repo.Repository.Status = repo_model.RepositoryBroken - showEmpty = true - ctx.Flash.Error(ctx.Tr("error.occurred"), true) - } - } - if showEmpty { - ctx.HTML(http.StatusOK, tplRepoEMPTY) - return - } - - // the repo is not really empty, so we should update the modal in database - // such problem may be caused by: - // 1) an error occurs during pushing/receiving. 2) the user replaces an empty git repo manually - // and even more: the IsEmpty flag is deeply broken and should be removed with the UI changed to manage to cope with empty repos. - // it's possible for a repository to be non-empty by that flag but still 500 - // because there are no branches - only tags -or the default branch is non-extant as it has been 0-pushed. - ctx.Repo.Repository.IsEmpty = false - if err = repo_model.UpdateRepositoryCols(ctx, ctx.Repo.Repository, "is_empty"); err != nil { - ctx.ServerError("UpdateRepositoryCols", err) - return - } - if err = repo_module.UpdateRepoSize(ctx, ctx.Repo.Repository); err != nil { - ctx.ServerError("UpdateRepoSize", err) - return - } - - // the repo's IsEmpty has been updated, redirect to this page to make sure middlewares can get the correct values - link := ctx.Link - if ctx.Req.URL.RawQuery != "" { - link += "?" + ctx.Req.URL.RawQuery - } - ctx.Redirect(link) - return - } - - title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name - if len(ctx.Repo.Repository.Description) > 0 { - title += ": " + ctx.Repo.Repository.Description - } - ctx.Data["Title"] = title - - // Get Topics of this repo - renderRepoTopics(ctx) - if ctx.Written() { - return - } - - // Get current entry user currently looking at. - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) - if err != nil { - HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err) - return - } - - checkOutdatedBranch(ctx) - - checkCitationFile(ctx, entry) - if ctx.Written() { - return - } - - renderLanguageStats(ctx) - if ctx.Written() { - return - } - - renderLatestRelease(ctx) - if ctx.Written() { - return - } - - if entry.IsDir() { - renderDirectory(ctx) - } else { - renderFile(ctx, entry) - } - if ctx.Written() { - return - } - - if ctx.Doer != nil { - if err := ctx.Repo.Repository.GetBaseRepo(ctx); err != nil { - ctx.ServerError("GetBaseRepo", err) - return - } - - opts := &git_model.FindRecentlyPushedNewBranchesOptions{ - Repo: ctx.Repo.Repository, - BaseRepo: ctx.Repo.Repository, - } - if ctx.Repo.Repository.IsFork { - opts.BaseRepo = ctx.Repo.Repository.BaseRepo - } - - baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, opts.BaseRepo, ctx.Doer) - if err != nil { - ctx.ServerError("GetUserRepoPermission", err) - return - } - - if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror && - opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) && - baseRepoPerm.CanRead(unit_model.TypePullRequests) { - ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts) - if err != nil { - log.Error("FindRecentlyPushedNewBranches failed: %v", err) - } - } - } - - var treeNames []string - paths := make([]string, 0, 5) - if len(ctx.Repo.TreePath) > 0 { - treeNames = strings.Split(ctx.Repo.TreePath, "/") - for i := range treeNames { - paths = append(paths, strings.Join(treeNames[:i+1], "/")) - } - - ctx.Data["HasParentPath"] = true - if len(paths)-2 >= 0 { - ctx.Data["ParentPath"] = "/" + paths[len(paths)-2] - } - } - - ctx.Data["Paths"] = paths - - branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() - treeLink := branchLink - if len(ctx.Repo.TreePath) > 0 { - treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) - } - ctx.Data["TreeLink"] = treeLink - ctx.Data["TreeNames"] = treeNames - ctx.Data["BranchLink"] = branchLink - ctx.Data["LicenseFileName"] = repo_service.LicenseFileName - ctx.HTML(http.StatusOK, tplRepoHome) -} - -func checkOutdatedBranch(ctx *context.Context) { - if !(ctx.Repo.IsAdmin() || ctx.Repo.IsOwner()) { - return - } - - // get the head commit of the branch since ctx.Repo.CommitID is not always the head commit of `ctx.Repo.BranchName` - commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.BranchName) - if err != nil { - log.Error("GetBranchCommitID: %v", err) - // Don't return an error page, as it can be rechecked the next time the user opens the page. - return - } - - dbBranch, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, ctx.Repo.BranchName) - if err != nil { - log.Error("GetBranch: %v", err) - // Don't return an error page, as it can be rechecked the next time the user opens the page. - return - } - - if dbBranch.CommitID != commit.ID.String() { - ctx.Flash.Warning(ctx.Tr("repo.error.broken_git_hook", "https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running"), true) - } -} - // RenderUserCards render a page show users according the input template func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOptions) ([]*user_model.User, error), tpl base.TplName) { page := ctx.FormInt("page") diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go new file mode 100644 index 0000000000000..1cdd76131afeb --- /dev/null +++ b/routers/web/repo/view_file.go @@ -0,0 +1,310 @@ +package repo + +import ( + "bytes" + "fmt" + "image" + "io" + "path" + "slices" + "strings" + + git_model "code.gitea.io/gitea/models/git" + issue_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/models/renderhelper" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/highlight" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" + issue_service "code.gitea.io/gitea/services/issue" + files_service "code.gitea.io/gitea/services/repository/files" + + "github.com/nektos/act/pkg/model" +) + +func renderFile(ctx *context.Context, entry *git.TreeEntry) { + ctx.Data["IsViewFile"] = true + ctx.Data["HideRepoInfo"] = true + blob := entry.Blob() + buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob) + if err != nil { + ctx.ServerError("getFileReader", err) + return + } + defer dataRc.Close() + + ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) + ctx.Data["FileIsSymlink"] = entry.IsLink() + ctx.Data["FileName"] = blob.Name() + ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + + commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath) + if err != nil { + ctx.ServerError("GetCommitByPath", err) + return + } + + if !loadLatestCommitData(ctx, commit) { + return + } + + if ctx.Repo.TreePath == ".editorconfig" { + _, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit) + if editorconfigWarning != nil { + ctx.Data["FileWarning"] = strings.TrimSpace(editorconfigWarning.Error()) + } + if editorconfigErr != nil { + ctx.Data["FileError"] = strings.TrimSpace(editorconfigErr.Error()) + } + } else if issue_service.IsTemplateConfig(ctx.Repo.TreePath) { + _, issueConfigErr := issue_service.GetTemplateConfig(ctx.Repo.GitRepo, ctx.Repo.TreePath, ctx.Repo.Commit) + if issueConfigErr != nil { + ctx.Data["FileError"] = strings.TrimSpace(issueConfigErr.Error()) + } + } else if actions.IsWorkflow(ctx.Repo.TreePath) { + content, err := actions.GetContentFromEntry(entry) + if err != nil { + log.Error("actions.GetContentFromEntry: %v", err) + } + _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content)) + if workFlowErr != nil { + ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) + } + } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { + if data, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize); err == nil { + _, warnings := issue_model.GetCodeOwnersFromContent(ctx, data) + if len(warnings) > 0 { + ctx.Data["FileWarning"] = strings.Join(warnings, "\n") + } + } + } + + isDisplayingSource := ctx.FormString("display") == "source" + isDisplayingRendered := !isDisplayingSource + + if fInfo.isLFSFile { + ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + } + + isRepresentableAsText := fInfo.st.IsRepresentableAsText() + if !isRepresentableAsText { + // If we can't show plain text, always try to render. + isDisplayingSource = false + isDisplayingRendered = true + } + ctx.Data["IsLFSFile"] = fInfo.isLFSFile + ctx.Data["FileSize"] = fInfo.fileSize + ctx.Data["IsTextFile"] = fInfo.isTextFile + ctx.Data["IsRepresentableAsText"] = isRepresentableAsText + ctx.Data["IsDisplayingSource"] = isDisplayingSource + ctx.Data["IsDisplayingRendered"] = isDisplayingRendered + ctx.Data["IsExecutable"] = entry.IsExecutable() + + isTextSource := fInfo.isTextFile || isDisplayingSource + ctx.Data["IsTextSource"] = isTextSource + if isTextSource { + ctx.Data["CanCopyContent"] = true + } + + // Check LFS Lock + lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath) + ctx.Data["LFSLock"] = lfsLock + if err != nil { + ctx.ServerError("GetTreePathLock", err) + return + } + if lfsLock != nil { + u, err := user_model.GetUserByID(ctx, lfsLock.OwnerID) + if err != nil { + ctx.ServerError("GetTreePathLock", err) + return + } + ctx.Data["LFSLockOwner"] = u.Name + ctx.Data["LFSLockOwnerHomeLink"] = u.HomeLink() + ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked") + } + + // Assume file is not editable first. + if fInfo.isLFSFile { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files") + } else if !isRepresentableAsText { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") + } + + switch { + case isRepresentableAsText: + if fInfo.fileSize >= setting.UI.MaxDisplayFileSize { + ctx.Data["IsFileTooLarge"] = true + break + } + + if fInfo.st.IsSvgImage() { + ctx.Data["IsImageFile"] = true + ctx.Data["CanCopyContent"] = true + ctx.Data["HasSourceRenderedToggle"] = true + } + + rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) + + shouldRenderSource := ctx.FormString("display") == "source" + readmeExist := util.IsReadmeFileName(blob.Name()) + ctx.Data["ReadmeExist"] = readmeExist + + markupType := markup.DetectMarkupTypeByFileName(blob.Name()) + if markupType == "" { + markupType = markup.DetectRendererType(blob.Name(), bytes.NewReader(buf)) + } + if markupType != "" { + ctx.Data["HasSourceRenderedToggle"] = true + } + if markupType != "" && !shouldRenderSource { + ctx.Data["IsMarkup"] = true + ctx.Data["MarkupType"] = markupType + metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx) + metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() + rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ + CurrentRefPath: ctx.Repo.BranchNameSubURL(), + CurrentTreePath: path.Dir(ctx.Repo.TreePath), + }). + WithMarkupType(markupType). + WithRelativePath(ctx.Repo.TreePath). + WithMetas(metas) + + ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd) + if err != nil { + ctx.ServerError("Render", err) + return + } + // to prevent iframe load third-party url + ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'") + } else { + buf, _ := io.ReadAll(rd) + + // The Open Group Base Specification: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html + // empty: 0 lines; "a": 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line; + // Gitea uses the definition (like most modern editors): + // empty: 0 lines; "a": 1 line; "a\n": 2 lines; "a\nb": 2 lines; + // When rendering, the last empty line is not rendered in UI, while the line-number is still counted, to tell users that the file contains a trailing EOL. + // To make the UI more consistent, it could use an icon mark to indicate that there is no trailing EOL, and show line-number as the rendered lines. + // This NumLines is only used for the display on the UI: "xxx lines" + if len(buf) == 0 { + ctx.Data["NumLines"] = 0 + } else { + ctx.Data["NumLines"] = bytes.Count(buf, []byte{'\n'}) + 1 + } + + language, err := files_service.TryGetContentLanguage(ctx.Repo.GitRepo, ctx.Repo.CommitID, ctx.Repo.TreePath) + if err != nil { + log.Error("Unable to get file language for %-v:%s. Error: %v", ctx.Repo.Repository, ctx.Repo.TreePath, err) + } + + fileContent, lexerName, err := highlight.File(blob.Name(), language, buf) + ctx.Data["LexerName"] = lexerName + if err != nil { + log.Error("highlight.File failed, fallback to plain text: %v", err) + fileContent = highlight.PlainText(buf) + } + status := &charset.EscapeStatus{} + statuses := make([]*charset.EscapeStatus, len(fileContent)) + for i, line := range fileContent { + statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale) + status = status.Or(statuses[i]) + } + ctx.Data["EscapeStatus"] = status + ctx.Data["FileContent"] = fileContent + ctx.Data["LineEscapeStatus"] = statuses + } + if !fInfo.isLFSFile { + if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { + if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { + ctx.Data["CanEditFile"] = false + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") + } else { + ctx.Data["CanEditFile"] = true + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") + } + } else if !ctx.Repo.IsViewBranch { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") + } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit") + } + } + + case fInfo.st.IsPDF(): + ctx.Data["IsPDFFile"] = true + case fInfo.st.IsVideo(): + ctx.Data["IsVideoFile"] = true + case fInfo.st.IsAudio(): + ctx.Data["IsAudioFile"] = true + case fInfo.st.IsImage() && (setting.UI.SVG.Enabled || !fInfo.st.IsSvgImage()): + ctx.Data["IsImageFile"] = true + ctx.Data["CanCopyContent"] = true + default: + if fInfo.fileSize >= setting.UI.MaxDisplayFileSize { + ctx.Data["IsFileTooLarge"] = true + break + } + + // TODO: this logic duplicates with "isRepresentableAsText=true", it is not the same as "LFSFileGet" in "lfs.go" + // It is used by "external renders", markupRender will execute external programs to get rendered content. + if markupType := markup.DetectMarkupTypeByFileName(blob.Name()); markupType != "" { + rd := io.MultiReader(bytes.NewReader(buf), dataRc) + ctx.Data["IsMarkup"] = true + ctx.Data["MarkupType"] = markupType + + rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ + CurrentRefPath: ctx.Repo.BranchNameSubURL(), + CurrentTreePath: path.Dir(ctx.Repo.TreePath), + }). + WithMarkupType(markupType). + WithRelativePath(ctx.Repo.TreePath) + + ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd) + if err != nil { + ctx.ServerError("Render", err) + return + } + } + } + + if ctx.Repo.GitRepo != nil { + checker, deferable := ctx.Repo.GitRepo.CheckAttributeReader(ctx.Repo.CommitID) + if checker != nil { + defer deferable() + attrs, err := checker.CheckPath(ctx.Repo.TreePath) + if err == nil { + ctx.Data["IsVendored"] = git.AttributeToBool(attrs, git.AttributeLinguistVendored).Value() + ctx.Data["IsGenerated"] = git.AttributeToBool(attrs, git.AttributeLinguistGenerated).Value() + } + } + } + + if fInfo.st.IsImage() && !fInfo.st.IsSvgImage() { + img, _, err := image.DecodeConfig(bytes.NewReader(buf)) + if err == nil { + // There are Image formats go can't decode + // Instead of throwing an error in that case, we show the size only when we can decode + ctx.Data["ImageSize"] = fmt.Sprintf("%dx%dpx", img.Width, img.Height) + } + } + + if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { + if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { + ctx.Data["CanDeleteFile"] = false + ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") + } else { + ctx.Data["CanDeleteFile"] = true + ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") + } + } else if !ctx.Repo.IsViewBranch { + ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") + } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { + ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access") + } +} diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go new file mode 100644 index 0000000000000..2c77a85b76fec --- /dev/null +++ b/routers/web/repo/view_home.go @@ -0,0 +1,348 @@ +package repo + +import ( + "fmt" + "html/template" + "net/http" + "path" + "strings" + "time" + + "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" + access_model "code.gitea.io/gitea/models/perm/access" + repo_model "code.gitea.io/gitea/models/repo" + unit_model "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/routers/web/feed" + "code.gitea.io/gitea/services/context" + repo_service "code.gitea.io/gitea/services/repository" +) + +func checkOutdatedBranch(ctx *context.Context) { + if !(ctx.Repo.IsAdmin() || ctx.Repo.IsOwner()) { + return + } + + // get the head commit of the branch since ctx.Repo.CommitID is not always the head commit of `ctx.Repo.BranchName` + commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.BranchName) + if err != nil { + log.Error("GetBranchCommitID: %v", err) + // Don't return an error page, as it can be rechecked the next time the user opens the page. + return + } + + dbBranch, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, ctx.Repo.BranchName) + if err != nil { + log.Error("GetBranch: %v", err) + // Don't return an error page, as it can be rechecked the next time the user opens the page. + return + } + + if dbBranch.CommitID != commit.ID.String() { + ctx.Flash.Warning(ctx.Tr("repo.error.broken_git_hook", "https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running"), true) + } +} + +func prepareHomeSidebarRepoTopics(ctx *context.Context) { + topics, err := db.Find[repo_model.Topic](ctx, &repo_model.FindTopicOptions{ + RepoID: ctx.Repo.Repository.ID, + }) + if err != nil { + ctx.ServerError("models.FindTopics", err) + return + } + ctx.Data["Topics"] = topics +} + +func prepareOpenWithEditorApps(ctx *context.Context) { + var tmplApps []map[string]any + apps := setting.Config().Repository.OpenWithEditorApps.Value(ctx) + if len(apps) == 0 { + apps = setting.DefaultOpenWithEditorApps() + } + for _, app := range apps { + schema, _, _ := strings.Cut(app.OpenURL, ":") + var iconHTML template.HTML + if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" { + iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16, "tw-mr-2") + } else { + iconHTML = svg.RenderHTML("gitea-git", 16, "tw-mr-2") // TODO: it could support user's customized icon in the future + } + tmplApps = append(tmplApps, map[string]any{ + "DisplayName": app.DisplayName, + "OpenURL": app.OpenURL, + "IconHTML": iconHTML, + }) + } + ctx.Data["OpenWithEditorApps"] = tmplApps +} + +func prepareHomeSidebarCitationFile(ctx *context.Context, entry *git.TreeEntry) { + if entry.Name() != "" { + return + } + tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath) + if err != nil { + HandleGitError(ctx, "Repo.Commit.SubTree", err) + return + } + allEntries, err := tree.ListEntries() + if err != nil { + ctx.ServerError("ListEntries", err) + return + } + for _, entry := range allEntries { + if entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" { + // Read Citation file contents + if content, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { + log.Error("checkCitationFile: GetBlobContent: %v", err) + } else { + ctx.Data["CitiationExist"] = true + ctx.PageData["citationFileContent"] = content + break + } + } + } +} + +func prepareHomeSidebarLicenses(ctx *context.Context) { + repoLicenses, err := repo_model.GetRepoLicenses(ctx, ctx.Repo.Repository) + if err != nil { + ctx.ServerError("GetRepoLicenses", err) + return + } + ctx.Data["DetectedRepoLicenses"] = repoLicenses.StringList() + ctx.Data["LicenseFileName"] = repo_service.LicenseFileName +} + +func prepareToRenderDirectory(ctx *context.Context) { + entries := renderDirectoryFiles(ctx, 1*time.Second) + if ctx.Written() { + return + } + + if ctx.Repo.TreePath != "" { + ctx.Data["HideRepoInfo"] = true + ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) + } + + subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true) + if err != nil { + ctx.ServerError("findReadmeFileInEntries", err) + return + } + + prepareToRenderReadmeFile(ctx, subfolder, readmeFile) +} + +func prepareHomeSidebarLanguageStats(ctx *context.Context) { + langs, err := repo_model.GetTopLanguageStats(ctx, ctx.Repo.Repository, 5) + if err != nil { + ctx.ServerError("Repo.GetTopLanguageStats", err) + return + } + + ctx.Data["LanguageStats"] = langs +} + +func prepareHomeSidebarLatestRelease(ctx *context.Context) { + if !ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeReleases) { + return + } + + release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) + if err != nil && !repo_model.IsErrReleaseNotExist(err) { + ctx.ServerError("GetLatestReleaseByRepoID", err) + return + } + + if release != nil { + if err = release.LoadAttributes(ctx); err != nil { + ctx.ServerError("release.LoadAttributes", err) + return + } + ctx.Data["LatestRelease"] = release + } +} + +func renderHomeCode(ctx *context.Context) { + ctx.Data["PageIsViewCode"] = true + ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled + prepareOpenWithEditorApps(ctx) + + if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() { + showEmpty := true + var err error + if ctx.Repo.GitRepo != nil { + showEmpty, err = ctx.Repo.GitRepo.IsEmpty() + if err != nil { + log.Error("GitRepo.IsEmpty: %v", err) + ctx.Repo.Repository.Status = repo_model.RepositoryBroken + showEmpty = true + ctx.Flash.Error(ctx.Tr("error.occurred"), true) + } + } + if showEmpty { + ctx.HTML(http.StatusOK, tplRepoEMPTY) + return + } + + // the repo is not really empty, so we should update the modal in database + // such problem may be caused by: + // 1) an error occurs during pushing/receiving. 2) the user replaces an empty git repo manually + // and even more: the IsEmpty flag is deeply broken and should be removed with the UI changed to manage to cope with empty repos. + // it's possible for a repository to be non-empty by that flag but still 500 + // because there are no branches - only tags -or the default branch is non-extant as it has been 0-pushed. + ctx.Repo.Repository.IsEmpty = false + if err = repo_model.UpdateRepositoryCols(ctx, ctx.Repo.Repository, "is_empty"); err != nil { + ctx.ServerError("UpdateRepositoryCols", err) + return + } + if err = repo_module.UpdateRepoSize(ctx, ctx.Repo.Repository); err != nil { + ctx.ServerError("UpdateRepoSize", err) + return + } + + // the repo's IsEmpty has been updated, redirect to this page to make sure middlewares can get the correct values + link := ctx.Link + if ctx.Req.URL.RawQuery != "" { + link += "?" + ctx.Req.URL.RawQuery + } + ctx.Redirect(link) + return + } + + title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name + if len(ctx.Repo.Repository.Description) > 0 { + title += ": " + ctx.Repo.Repository.Description + } + ctx.Data["Title"] = title + + // Get Topics of this repo + prepareHomeSidebarRepoTopics(ctx) + if ctx.Written() { + return + } + + // Get current entry user currently looking at. + entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + if err != nil { + HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err) + return + } + + checkOutdatedBranch(ctx) + + if entry.IsDir() { + prepareToRenderDirectory(ctx) + } else { + renderFile(ctx, entry) + } + if ctx.Written() { + return + } + + if ctx.Doer != nil { + if err := ctx.Repo.Repository.GetBaseRepo(ctx); err != nil { + ctx.ServerError("GetBaseRepo", err) + return + } + + opts := &git_model.FindRecentlyPushedNewBranchesOptions{ + Repo: ctx.Repo.Repository, + BaseRepo: ctx.Repo.Repository, + } + if ctx.Repo.Repository.IsFork { + opts.BaseRepo = ctx.Repo.Repository.BaseRepo + } + + baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, opts.BaseRepo, ctx.Doer) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return + } + + if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror && + opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) && + baseRepoPerm.CanRead(unit_model.TypePullRequests) { + ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts) + if err != nil { + log.Error("FindRecentlyPushedNewBranches failed: %v", err) + } + } + } + + var treeNames, paths []string + branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + treeLink := branchLink + if ctx.Repo.TreePath != "" { + treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + treeNames = strings.Split(ctx.Repo.TreePath, "/") + for i := range treeNames { + paths = append(paths, strings.Join(treeNames[:i+1], "/")) + } + ctx.Data["HasParentPath"] = true + if len(paths)-2 >= 0 { + ctx.Data["ParentPath"] = "/" + paths[len(paths)-2] + } + } + + isTreePathRoot := ctx.Repo.TreePath == "" + if isTreePathRoot { + prepareHomeSidebarLicenses(ctx) + if ctx.Written() { + return + } + prepareHomeSidebarCitationFile(ctx, entry) + if ctx.Written() { + return + } + + prepareHomeSidebarLanguageStats(ctx) + if ctx.Written() { + return + } + + prepareHomeSidebarLatestRelease(ctx) + if ctx.Written() { + return + } + } + + ctx.Data["Paths"] = paths + ctx.Data["TreeLink"] = treeLink + ctx.Data["TreeNames"] = treeNames + ctx.Data["BranchLink"] = branchLink + ctx.HTML(http.StatusOK, tplRepoHome) +} + +// Home render repository home page +func Home(ctx *context.Context) { + if setting.Other.EnableFeed { + isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam(":reponame"), ctx.Req) + if isFeed { + switch { + case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType): + feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType) + case ctx.Repo.TreePath == "": + feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType) + case ctx.Repo.TreePath != "": + feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType) + } + return + } + } + + checkHomeCodeViewable(ctx) + if ctx.Written() { + return + } + + renderHomeCode(ctx) +} diff --git a/routers/web/repo/view_readme.go b/routers/web/repo/view_readme.go new file mode 100644 index 0000000000000..f8ea99275ca7d --- /dev/null +++ b/routers/web/repo/view_readme.go @@ -0,0 +1,215 @@ +package repo + +import ( + "bytes" + "encoding/base64" + "fmt" + "html/template" + "io" + "net/url" + "path" + "strings" + + "code.gitea.io/gitea/models/renderhelper" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" +) + +// locate a README for a tree in one of the supported paths. +// +// entries is passed to reduce calls to ListEntries(), so +// this has precondition: +// +// entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries() +// +// FIXME: There has to be a more efficient way of doing this +func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, tryWellKnownDirs bool) (string, *git.TreeEntry, error) { + // Create a list of extensions in priority order + // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md + // 2. Txt files - e.g. README.txt + // 3. No extension - e.g. README + exts := append(localizedExtensions(".md", ctx.Locale.Language()), ".txt", "") // sorted by priority + extCount := len(exts) + readmeFiles := make([]*git.TreeEntry, extCount+1) + + docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/) + for _, entry := range entries { + if tryWellKnownDirs && entry.IsDir() { + // as a special case for the top-level repo introduction README, + // fall back to subfolders, looking for e.g. docs/README.md, .gitea/README.zh-CN.txt, .github/README.txt, ... + // (note that docsEntries is ignored unless we are at the root) + lowerName := strings.ToLower(entry.Name()) + switch lowerName { + case "docs": + if entry.Name() == "docs" || docsEntries[0] == nil { + docsEntries[0] = entry + } + case ".gitea": + if entry.Name() == ".gitea" || docsEntries[1] == nil { + docsEntries[1] = entry + } + case ".github": + if entry.Name() == ".github" || docsEntries[2] == nil { + docsEntries[2] = entry + } + } + continue + } + if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok { + log.Debug("Potential readme file: %s", entry.Name()) + if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) { + if entry.IsLink() { + target, err := entry.FollowLinks() + if err != nil && !git.IsErrBadLink(err) { + return "", nil, err + } else if target != nil && (target.IsExecutable() || target.IsRegular()) { + readmeFiles[i] = entry + } + } else { + readmeFiles[i] = entry + } + } + } + } + var readmeFile *git.TreeEntry + for _, f := range readmeFiles { + if f != nil { + readmeFile = f + break + } + } + + if ctx.Repo.TreePath == "" && readmeFile == nil { + for _, subTreeEntry := range docsEntries { + if subTreeEntry == nil { + continue + } + subTree := subTreeEntry.Tree() + if subTree == nil { + // this should be impossible; if subTreeEntry exists so should this. + continue + } + childEntries, err := subTree.ListEntries() + if err != nil { + return "", nil, err + } + + subfolder, readmeFile, err := findReadmeFileInEntries(ctx, childEntries, false) + if err != nil && !git.IsErrNotExist(err) { + return "", nil, err + } + if readmeFile != nil { + return path.Join(subTreeEntry.Name(), subfolder), readmeFile, nil + } + } + } + + return "", readmeFile, nil +} + +// localizedExtensions prepends the provided language code with and without a +// regional identifier to the provided extension. +// Note: the language code will always be lower-cased, if a region is present it must be separated with a `-` +// Note: ext should be prefixed with a `.` +func localizedExtensions(ext, languageCode string) (localizedExts []string) { + if len(languageCode) < 1 { + return []string{ext} + } + + lowerLangCode := "." + strings.ToLower(languageCode) + + if strings.Contains(lowerLangCode, "-") { + underscoreLangCode := strings.ReplaceAll(lowerLangCode, "-", "_") + indexOfDash := strings.Index(lowerLangCode, "-") + // e.g. [.zh-cn.md, .zh_cn.md, .zh.md, _zh.md, .md] + return []string{lowerLangCode + ext, underscoreLangCode + ext, lowerLangCode[:indexOfDash] + ext, "_" + lowerLangCode[1:indexOfDash] + ext, ext} + } + + // e.g. [.en.md, .md] + return []string{lowerLangCode + ext, ext} +} + +func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) { + target := readmeFile + if readmeFile != nil && readmeFile.IsLink() { + target, _ = readmeFile.FollowLinks() + } + if target == nil { + // if findReadmeFile() failed and/or gave us a broken symlink (which it shouldn't) + // simply skip rendering the README + return + } + + ctx.Data["RawFileLink"] = "" + ctx.Data["ReadmeInList"] = true + ctx.Data["ReadmeExist"] = true + ctx.Data["FileIsSymlink"] = readmeFile.IsLink() + + buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, target.Blob()) + if err != nil { + ctx.ServerError("getFileReader", err) + return + } + defer dataRc.Close() + + ctx.Data["FileIsText"] = fInfo.isTextFile + ctx.Data["FileName"] = path.Join(subfolder, readmeFile.Name()) + ctx.Data["FileSize"] = fInfo.fileSize + ctx.Data["IsLFSFile"] = fInfo.isLFSFile + + if fInfo.isLFSFile { + filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.Name())) + ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.Link(), url.PathEscape(fInfo.lfsMeta.Oid), url.PathEscape(filenameBase64)) + } + + if !fInfo.isTextFile { + return + } + + if fInfo.fileSize >= setting.UI.MaxDisplayFileSize { + // Pretend that this is a normal text file to display 'This file is too large to be shown' + ctx.Data["IsFileTooLarge"] = true + ctx.Data["IsTextFile"] = true + return + } + + rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) + + if markupType := markup.DetectMarkupTypeByFileName(readmeFile.Name()); markupType != "" { + ctx.Data["IsMarkup"] = true + ctx.Data["MarkupType"] = markupType + + rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ + CurrentRefPath: ctx.Repo.BranchNameSubURL(), + CurrentTreePath: path.Join(ctx.Repo.TreePath, subfolder), + }). + WithMarkupType(markupType). + WithRelativePath(path.Join(ctx.Repo.TreePath, subfolder, readmeFile.Name())) // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path). + + ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd) + if err != nil { + log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err) + delete(ctx.Data, "IsMarkup") + } + } + + if ctx.Data["IsMarkup"] != true { + ctx.Data["IsPlainText"] = true + content, err := io.ReadAll(rd) + if err != nil { + log.Error("Read readme content failed: %v", err) + } + contentEscaped := template.HTMLEscapeString(util.UnsafeBytesToString(content)) + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale) + } + + if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { + ctx.Data["CanEditReadmeFile"] = true + } +} diff --git a/services/context/repo.go b/services/context/repo.go index 1eafb7ca48bb4..2df5e03fd2ee7 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -396,13 +396,6 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { ctx.Repo.Repository = repo ctx.Data["RepoName"] = ctx.Repo.Repository.Name ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty - - repoLicenses, err := repo_model.GetRepoLicenses(ctx, ctx.Repo.Repository) - if err != nil { - ctx.ServerError("GetRepoLicenses", err) - return - } - ctx.Data["DetectedRepoLicenses"] = repoLicenses.StringList() } // RepoAssignment returns a middleware to handle repository assignment From 680aee4b53de1d55fcaacb089c2a0fc28e7cfb7b Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 20:40:52 +0800 Subject: [PATCH 46/48] remove dead code and fix lint --- routers/web/repo/release.go | 6 ------ routers/web/repo/view_file.go | 3 +++ routers/web/repo/view_home.go | 3 +++ routers/web/repo/view_readme.go | 3 +++ services/context/repo.go | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index aaeb35bc8c29c..b3a91a6070744 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -152,9 +152,6 @@ func Releases(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.releases") ctx.Data["IsViewBranch"] = false ctx.Data["IsViewTag"] = true - // Disable the showCreateNewBranch form in the dropdown on this page. - ctx.Data["CanCreateBranch"] = false - ctx.Data["HideBranchesInDropdown"] = true listOptions := db.ListOptions{ Page: ctx.FormInt("page"), @@ -201,9 +198,6 @@ func TagsList(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.tags") ctx.Data["IsViewBranch"] = false ctx.Data["IsViewTag"] = true - // Disable the showCreateNewBranch form in the dropdown on this page. - ctx.Data["CanCreateBranch"] = false - ctx.Data["HideBranchesInDropdown"] = true ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived namePattern := ctx.FormTrim("q") diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index 1cdd76131afeb..03f394d7d8262 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -1,3 +1,6 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + package repo import ( diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index 2c77a85b76fec..d1a50800c11e0 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -1,3 +1,6 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + package repo import ( diff --git a/routers/web/repo/view_readme.go b/routers/web/repo/view_readme.go index f8ea99275ca7d..5bd39de96315d 100644 --- a/routers/web/repo/view_readme.go +++ b/routers/web/repo/view_readme.go @@ -1,3 +1,6 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + package repo import ( diff --git a/services/context/repo.go b/services/context/repo.go index 2df5e03fd2ee7..cf328ca97b77c 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -1029,7 +1029,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit - ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() + ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() if err != nil { From b9e5dcc8730b0f92fabb4660eadf96a5616637d4 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 6 Dec 2024 13:19:02 +0000 Subject: [PATCH 47/48] change tooltips to title --- templates/repo/home_sidebar_bottom.tmpl | 2 +- templates/repo/home_sidebar_top.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/home_sidebar_bottom.tmpl b/templates/repo/home_sidebar_bottom.tmpl index fbc277a6c011e..57b4a95ddcc43 100644 --- a/templates/repo/home_sidebar_bottom.tmpl +++ b/templates/repo/home_sidebar_bottom.tmpl @@ -15,7 +15,7 @@
- {{.LatestRelease.Title}} + {{.LatestRelease.Title}} {{ctx.Locale.Tr "latest"}}
diff --git a/templates/repo/home_sidebar_top.tmpl b/templates/repo/home_sidebar_top.tmpl index c428f1fbddbe7..d36c5b0433b6e 100644 --- a/templates/repo/home_sidebar_top.tmpl +++ b/templates/repo/home_sidebar_top.tmpl @@ -50,7 +50,7 @@ {{end}} {{if .DetectedRepoLicenses}} From 6412de600917023fa1d0a443bdfe16de6a2d8d2d Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 6 Dec 2024 22:04:07 +0800 Subject: [PATCH 48/48] fix layout --- web_src/css/repo/home.css | 1 + 1 file changed, 1 insertion(+) diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index f461c605a418d..fd8fac27e27b4 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -5,6 +5,7 @@ } .repo-grid-filelist-sidebar .repo-home-filelist { + min-width: 0; grid-column: 1; grid-row: 1 / 4; }