From 3746a625f55d24fc48a0198c8108bdebfd4edeb3 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 16 Apr 2024 17:46:12 +0200 Subject: [PATCH 01/33] Tweak repo buttons on mobile and labeled button border-radius (#30503) Fixes: https://github.com/go-gitea/gitea/issues/30514 Fixes: https://github.com/go-gitea/gitea/pull/30288#issuecomment-2057466623 - Fix border-radius regression from https://github.com/go-gitea/gitea/pull/30475 - Fix and simplify hover state - Move the modal HTML so it does not interfere with the CSS - Make the star and unwatch text show on mobile. There is still plenty of space, below is iPhone 12 viewport size Screenshot 2024-04-15 at 20 34 03 Screenshot 2024-04-15 at 20 31 42 Screenshot 2024-04-15 at 20 31 47 --------- Co-authored-by: Giteabot --- templates/repo/header.tmpl | 36 +++++++++++++++---------------- templates/repo/star_unstar.tmpl | 2 +- templates/repo/watch_unwatch.tmpl | 2 +- web_src/css/modules/button.css | 10 +++++++++ web_src/css/modules/label.css | 2 +- web_src/css/repo/header.css | 14 +++++++----- 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 5e2774dfa1..bb344bf3d4 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -91,28 +91,28 @@ > {{svg "octicon-repo-forked"}}{{ctx.Locale.Tr "repo.fork"}} - {{CountFmt .NumForks}} + {{end}} {{end}} diff --git a/templates/repo/star_unstar.tmpl b/templates/repo/star_unstar.tmpl index 1cdb98bf27..0f09d8b492 100644 --- a/templates/repo/star_unstar.tmpl +++ b/templates/repo/star_unstar.tmpl @@ -4,7 +4,7 @@ {{if $.IsStaringRepo}}{{$buttonText = ctx.Locale.Tr "repo.unstar"}}{{end}} {{CountFmt .Repository.NumStars}} diff --git a/templates/repo/watch_unwatch.tmpl b/templates/repo/watch_unwatch.tmpl index 64be971416..465cd91c2b 100644 --- a/templates/repo/watch_unwatch.tmpl +++ b/templates/repo/watch_unwatch.tmpl @@ -4,7 +4,7 @@ {{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}} {{CountFmt .Repository.NumWatches}} diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css index 87b9ddf292..4e133852f1 100644 --- a/web_src/css/modules/button.css +++ b/web_src/css/modules/button.css @@ -63,6 +63,8 @@ } .ui.labeled.button > .button { margin: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } .ui.labeled.button > .label { display: flex; @@ -70,6 +72,14 @@ margin: 0 0 0 -1px !important; font-size: 1em; border-color: var(--color-light-border); + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.ui.labeled.button > .label:hover { + background: var(--color-hover); +} +.ui.labeled.button > .button:hover + .label { + border-left-color: var(--color-secondary-dark-2); } .ui.button > .icon:not(.button) { diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css index 2032b2c84b..1e42668aa1 100644 --- a/web_src/css/modules/label.css +++ b/web_src/css/modules/label.css @@ -107,7 +107,7 @@ a.ui.label:hover { a.ui.basic.label:hover { text-decoration: none; color: var(--color-text); - border-color: var(--color-light-border); + border-color: var(--color-secondary-dark-2); background: var(--color-hover); } diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css index 55e704ed10..b70691435f 100644 --- a/web_src/css/repo/header.css +++ b/web_src/css/repo/header.css @@ -36,11 +36,6 @@ gap: 0.25em; } -.repo-buttons .ui.labeled.button > .label:hover { - color: var(--color-primary-light-2); - background: var(--color-light); -} - .repo-buttons button[disabled] ~ .label { opacity: var(--opacity-disabled); color: var(--color-text-dark); @@ -67,3 +62,12 @@ .repo-buttons .ui.labeled.button.disabled > .button { pointer-events: none !important; } + +@media (max-width: 767.98px) { + .repo-buttons .ui.button, + .repo-buttons .ui.label { + padding-left: 8px; + padding-right: 8px; + margin: 0; + } +} From 5ccd042f7080e1f4ef4b96591e1b1002a4826115 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 17 Apr 2024 00:39:19 +0200 Subject: [PATCH 02/33] Tweak and fix toggle checkboxes (#30527) Fixes: https://github.com/go-gitea/gitea/issues/30524. Slightly restyled them so that the "knob" is contained inside the background. Screenshot 2024-04-16 at 21 58 09 Screenshot 2024-04-16 at 21 58 50 --- web_src/css/modules/checkbox.css | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css index d3e45714a4..8d73573bfa 100644 --- a/web_src/css/modules/checkbox.css +++ b/web_src/css/modules/checkbox.css @@ -66,7 +66,7 @@ input[type="radio"] { } .ui.toggle.checkbox input { width: 3.5rem; - height: 1.5rem; + height: 21px; opacity: 0; z-index: 3; } @@ -81,29 +81,30 @@ input[type="radio"] { content: ""; z-index: 1; top: 0; - width: 3.5rem; - height: 1.5rem; + width: 49px; + height: 21px; border-radius: 500rem; left: 0; } .ui.toggle.checkbox label::after { background: var(--color-white); + box-shadow: 1px 1px 4px 1px var(--color-shadow); position: absolute; content: ""; opacity: 1; z-index: 2; - width: 1.5rem; - height: 1.5rem; - top: 0; - left: 0; + width: 18px; + height: 18px; + top: 1.5px; + left: 1.5px; border-radius: 500rem; transition: background 0.3s ease, left 0.3s ease; } .ui.toggle.checkbox input ~ label::after { - left: -0.05rem; + left: 1.5px; } .ui.toggle.checkbox input:checked ~ label::after { - left: 2.15rem; + left: 29px; } .ui.toggle.checkbox input:focus ~ label::before, .ui.toggle.checkbox label::before { From 38147d020de1bc8909e61d65a39ebd184bd296d7 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Wed, 17 Apr 2024 00:24:47 +0000 Subject: [PATCH 03/33] [skip ci] Updated translations via Crowdin --- options/locale/locale_zh-CN.ini | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 3e907eabfd..78460ad2f8 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -163,18 +163,23 @@ no_results_found=未找到结果 search=搜索... type_tooltip=搜索类型 fuzzy=模糊 +fuzzy_tooltip=包含近似匹配搜索词的结果 match=匹配 +match_tooltip=仅包含精确匹配搜索词的结果 repo_kind=搜索仓库... user_kind=搜索用户... org_kind=搜索组织... team_kind=搜索团队... code_kind=搜索代码... +code_search_unavailable=代码搜索当前不可用。请与网站管理员联系。 +code_search_by_git_grep=当前代码搜索结果由“git grep”提供。如果站点管理员启用仓库索引器,可能会有更好的结果。 package_kind=搜索软件包... project_kind=搜索项目... branch_kind=搜索分支... commit_kind=搜索提交记录... runner_kind=搜索runners... no_results=未找到匹配结果 +keyword_search_unavailable=按关键字搜索当前不可用。请联系站点管理员。 [aria] navbar=导航栏 @@ -281,6 +286,7 @@ email_title=电子邮箱设置 smtp_addr=SMTP 主机地址 smtp_port=SMTP 端口 smtp_from=电子邮件发件人 +smtp_from_invalid=`"发送电子邮件为"地址无效` smtp_from_helper=请输入一个用于 Gitea 的电子邮件地址,或者使用完整格式:"名称" mailer_user=SMTP 用户名 mailer_password=SMTP 密码 @@ -389,6 +395,7 @@ forgot_password_title=忘记密码 forgot_password=忘记密码? sign_up_now=还没帐户?马上注册。 sign_up_successful=帐户创建成功。欢迎! +confirmation_mail_sent_prompt_ex=一封新的确认邮件已经发送到 %s请在下一个 %s 中检查您的收件箱以完成注册过程。 如果您的注册电子邮件地址不正确,您可以重新登录并更改它。 must_change_password=更新您的密码 allow_password_change=要求用户更改密码(推荐) reset_password_mail_sent_prompt=确认电子邮件已被发送到 %s。请您在 %s 内检查您的收件箱 ,完成密码重置过程。 @@ -398,6 +405,7 @@ prohibit_login=禁止登录 prohibit_login_desc=您的帐户被禁止登录,请与网站管理员联系。 resent_limit_prompt=您请求发送激活邮件过于频繁,请等待 3 分钟后再试! has_unconfirmed_mail=%s 您好,系统检测到您有一封发送至 %s 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。 +change_unconfirmed_mail_address=如果您的注册电子邮件地址不正确,您可以在此更改并重新发送新的确认电子邮件。 resend_mail=单击此处重新发送确认邮件 email_not_associate=您输入的邮箱地址未被关联到任何帐号! send_reset_mail=发送账户恢复邮件 @@ -578,6 +586,7 @@ team_name_been_taken=团队名称已被使用。 team_no_units_error=至少选择一项仓库单元。 email_been_used=该电子邮件地址已在使用中。 email_invalid=此邮箱地址无效。 +email_domain_is_not_allowed=用户 %s 与EMAIL_DOMAIN_ALLOWLIT 或 EMAIL_DOMAIN_BLOCKLIT 冲突。请确保您的操作是预期的。 openid_been_used=OpenID 地址 "%s" 已被使用。 username_password_incorrect=用户名或密码不正确。 password_complexity=密码未达到复杂程度要求: @@ -589,6 +598,8 @@ enterred_invalid_repo_name=输入的仓库名称不正确 enterred_invalid_org_name=您输入的组织名称不正确。 enterred_invalid_owner_name=新的所有者名称无效。 enterred_invalid_password=输入的密码不正确 +unset_password=登录用户没有设置密码。 +unsupported_login_type=此登录类型不支持手动删除帐户。 user_not_exist=该用户不存在 team_not_exist=团队不存在 last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。 @@ -643,8 +654,24 @@ block.block.user=屏蔽用户 block.block.org=屏蔽用户访问组织 block.block.failure=屏蔽用户失败: %s block.unblock=取消屏蔽 +block.unblock.failure=屏蔽用户失败: %s +block.blocked=您已屏蔽此用户。 block.title=屏蔽一个用户 +block.info=屏蔽用户会阻止他们与仓库进行交互,例如打开或评论合并请求或出现问题。了解更多关于屏蔽用户的信息。 +block.info_1=阻止用户在您的帐户和仓库中进行以下操作: block.info_2=关注你的账号 +block.info_3=通过@提及您的用户名向您发送通知 +block.info_4=邀请您作为协作者到他们的仓库中 +block.info_5=在仓库中点赞、派生或关注 +block.info_6=打开和评论工单或合并请求 +block.info_7=在问题或合并请求中对您的评论做出反应 +block.user_to_block=要屏蔽的用户 +block.note=备注 +block.note.title=可选备注: +block.note.info=该备注对被屏蔽的用户不可见。 +block.note.edit=编辑备注 +block.list=已屏蔽用户 +block.list.none=您没有已屏蔽的用户。 [settings] profile=个人信息 @@ -982,7 +1009,9 @@ fork_visibility_helper=无法更改派生仓库的可见性。 fork_branch=要克隆到 Fork 的分支 all_branches=所有分支 fork_no_valid_owners=这个代码仓库无法被派生,因为没有有效的所有者。 +fork.blocked_user=无法克隆仓库,因为您被仓库所有者屏蔽。 use_template=使用此模板 +open_with_editor=用 %s 打开 download_zip=下载 ZIP download_tar=下载 TAR.GZ download_bundle=下载 BUNDLE @@ -1035,6 +1064,7 @@ watchers=关注者 stargazers=称赞者 stars_remove_warning=这将清除此仓库的所有点赞数。 forks=派生仓库 +stars=点赞数 reactions_more=再加载 %d unit_disabled=站点管理员已禁用此仓库单元。 language_other=其它 @@ -1156,6 +1186,7 @@ watch=关注 unstar=取消点赞 star=点赞 fork=派生 +action.blocked_user=无法执行操作,因为您已被仓库所有者屏蔽。 download_archive=下载此仓库 more_operations=更多操作 @@ -1202,6 +1233,8 @@ file_view_rendered=渲染模式 file_view_raw=查看原始文件 file_permalink=永久链接 file_too_large=文件过大,无法显示。 +code_preview_line_from_to=在 %[3]s 的第 %[1]d 行到 %[2]d 行 +code_preview_line_in=在 %[2]s 的第 %[1]d 行 invisible_runes_header=`此文件含有不可见的 Unicode 字符` invisible_runes_description=`此文件含有人类无法区分的不可见的 Unicode 字符,但可以由计算机进行不同的处理。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。` ambiguous_runes_header=`此文件含有模棱两可的 Unicode 字符` @@ -1284,6 +1317,8 @@ editor.file_editing_no_longer_exists=正在编辑的文件 %s 已不存在。 editor.file_deleting_no_longer_exists=正在删除的文件 %s 已不存在。 editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。单击此处 查看变动的具体内容,或者 再次提交 覆盖已发生的变动。 editor.file_already_exists=此仓库已经存在名为 %s 的文件。 +editor.commit_id_not_matching=提交ID与您开始编辑时的ID不匹配。请提交到补丁分支然后合并。 +editor.push_out_of_date=推送似乎已经过时。 editor.commit_empty_file_header=提交一个空文件 editor.commit_empty_file_text=您要提交的文件是空的,继续吗? editor.no_changes_to_show=没有可以显示的变更。 @@ -1402,6 +1437,8 @@ issues.new.assignees=指派成员 issues.new.clear_assignees=取消指派成员 issues.new.no_assignees=未指派成员 issues.new.no_reviewers=无审核者 +issues.new.blocked_user=无法创建工单,因为您已被仓库所有者屏蔽。 +issues.edit.blocked_user=无法编辑内容,因为您已被仓库所有者或工单创建者屏蔽。 issues.choose.get_started=开始 issues.choose.open_external_link=开启 issues.choose.blank=默认模板 @@ -1516,6 +1553,7 @@ issues.close_comment_issue=评论并关闭 issues.reopen_issue=重新开启 issues.reopen_comment_issue=评论并重新开启 issues.create_comment=评论 +issues.comment.blocked_user=无法创建或编辑评论,因为您已被仓库所有者或工单创建者屏蔽。 issues.closed_at=`于 %[2]s 关闭此工单` issues.reopened_at=`重新打开此问题 %[2]s` issues.commit_ref_at=`于 %[2]s 在代码提交中引用了该工单` @@ -1714,6 +1752,7 @@ compare.compare_head=比较 pulls.desc=启用合并请求和代码评审。 pulls.new=创建合并请求 +pulls.new.blocked_user=无法创建合并请求,因为您已被仓库所有者屏蔽。 pulls.view=查看拉取请求 pulls.compare_changes=创建合并请求 pulls.allow_edits_from_maintainers=允许维护者编辑 @@ -1797,6 +1836,7 @@ pulls.merge_pull_request=创建合并提交 pulls.rebase_merge_pull_request=变基后快进 pulls.rebase_merge_commit_pull_request=变基后创建合并提交 pulls.squash_merge_pull_request=创建压缩提交 +pulls.fast_forward_only_merge_pull_request=仅快进 pulls.merge_manually=手动合并 pulls.merge_commit_id=合并提交 ID pulls.require_signed_wont_sign=分支需要签名的提交,但这个合并将不会被签名 @@ -1933,6 +1973,7 @@ wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', ' wiki.original_git_entry_tooltip=查看原始的 Git 文件而不是使用友好链接。 activity=动态 +activity.navbar.pulse=活动 activity.navbar.code_frequency=代码频率 activity.navbar.contributors=贡献者 activity.navbar.recent_commits=最近的提交 From 6f7d70fb3d2624507c3ccd5640f6d1837259c27d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 17 Apr 2024 09:25:03 +0800 Subject: [PATCH 04/33] Reduce unnecessary database queries on actions table (#30509) --- models/activities/action_list.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/models/activities/action_list.go b/models/activities/action_list.go index 6e23b173b5..aafb7f8a26 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -83,6 +83,9 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]* _, alreadyLoaded := userMap[action.Repo.OwnerID] return action.Repo.OwnerID, !alreadyLoaded }) + if len(missingUserIDs) == 0 { + return nil + } if err := db.GetEngine(ctx). In("id", missingUserIDs). @@ -129,6 +132,9 @@ func (actions ActionList) LoadComments(ctx context.Context) error { commentIDs = append(commentIDs, action.CommentID) } } + if len(commentIDs) == 0 { + return nil + } commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs)) if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil { From 4f276a336355c4bf999034fb79f0fe5c967ceb50 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 17 Apr 2024 09:30:46 +0200 Subject: [PATCH 05/33] Fix install page checkboxes and dropdown width (#30526) Fixes: https://github.com/go-gitea/gitea/issues/30523 1. Fix checkbox rendering 2. Fix width of selection dropdowns (was too small) --------- Co-authored-by: delvh Co-authored-by: Giteabot --- web_src/css/install.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web_src/css/install.css b/web_src/css/install.css index 4ac294e902..ee2395e6c5 100644 --- a/web_src/css/install.css +++ b/web_src/css/install.css @@ -18,7 +18,8 @@ width: auto; } -.page-content.install form.ui.form input { +.page-content.install form.ui.form input:not([type="checkbox"],[type="radio"]), +.page-content.install form.ui.form .ui.selection.dropdown { width: 60%; } From 3e2e76e2484c79715ab5d56f268ea3ad8e1c259b Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 17 Apr 2024 16:31:37 +0800 Subject: [PATCH 06/33] Refactor web routes (#30519) Re-organize the routes in web.go and use ctx constants instead of `context.UnitTypes()` --------- Co-authored-by: Giteabot --- models/organization/team.go | 18 +- models/perm/access/repo_permission.go | 18 +- routers/web/repo/view.go | 6 +- routers/web/web.go | 540 ++++++++++---------- services/context/context.go | 12 + services/context/repo.go | 17 - templates/explore/navbar.tmpl | 2 +- templates/repo/blame.tmpl | 2 +- templates/repo/code_frequency.tmpl | 2 +- templates/repo/commit_page.tmpl | 2 +- templates/repo/contributors.tmpl | 2 +- templates/repo/graph/commits.tmpl | 2 +- templates/repo/header.tmpl | 28 +- templates/repo/issue/view_content/pull.tmpl | 2 +- templates/repo/pulse.tmpl | 12 +- templates/repo/recent_commits.tmpl | 2 +- templates/repo/release/list.tmpl | 10 +- templates/repo/release_tag_header.tmpl | 4 +- templates/repo/settings/navbar.tmpl | 4 +- templates/repo/settings/options.tmpl | 70 +-- templates/repo/sub_menu.tmpl | 6 +- templates/repo/tag/list.tmpl | 8 +- templates/repo/view_file.tmpl | 2 +- 23 files changed, 394 insertions(+), 377 deletions(-) diff --git a/models/organization/team.go b/models/organization/team.go index 17db11c42d..501a43d3a1 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -174,23 +174,27 @@ func (t *Team) LoadMembers(ctx context.Context) (err error) { return err } -// UnitEnabled returns if the team has the given unit type enabled +// UnitEnabled returns true if the team has the given unit type enabled func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool { return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone } -// UnitAccessMode returns if the team has the given unit type enabled +// UnitAccessMode returns the access mode for the given unit type, "none" for non-existent units func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode { + accessMode, _ := t.UnitAccessModeEx(ctx, tp) + return accessMode +} + +func (t *Team) UnitAccessModeEx(ctx context.Context, tp unit.Type) (accessMode perm.AccessMode, exist bool) { if err := t.LoadUnits(ctx); err != nil { log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error()) } - - for _, unit := range t.Units { - if unit.Type == tp { - return unit.AccessMode + for _, u := range t.Units { + if u.Type == tp { + return u.AccessMode, true } } - return perm.AccessModeNone + return perm.AccessModeNone, false } // IsUsableTeamName tests if a name could be as team name diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 4175cb9b92..e4e7579e62 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -102,6 +102,16 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool { return p.CanWrite(unit.TypeIssues) } +func (p *Permission) ReadableUnitTypes() []unit.Type { + types := make([]unit.Type, 0, len(p.Units)) + for _, u := range p.Units { + if p.CanRead(u.Type) { + types = append(types, u.Type) + } + } + return types +} + func (p *Permission) LogString() string { format := " perm_model.AccessModeNone { - m := perm.UnitsMode[u.Type] - if m < teamMode { - perm.UnitsMode[u.Type] = teamMode - } + if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist { + perm.UnitsMode[u.Type] = max(perm.UnitsMode[u.Type], teamMode) found = true } } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 8aa9dbb1be..de35c6b3a2 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -721,12 +721,12 @@ func checkHomeCodeViewable(ctx *context.Context) { } var firstUnit *unit_model.Unit - for _, repoUnit := range ctx.Repo.Units { - if repoUnit.Type == unit_model.TypeCode { + for _, repoUnitType := range ctx.Repo.Permission.ReadableUnitTypes() { + if repoUnitType == unit_model.TypeCode { return } - unit, ok := unit_model.Units[repoUnit.Type] + unit, ok := unit_model.Units[repoUnitType] if ok && (firstUnit == nil || !firstUnit.IsLessThan(unit)) { firstUnit = &unit } diff --git a/routers/web/web.go b/routers/web/web.go index 4fff994e42..f9164568ed 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -493,6 +493,7 @@ func registerRoutes(m *web.Route) { }, explore.Code) m.Get("/topics/search", explore.TopicSearch) }, ignExploreSignIn) + m.Group("/issues", func() { m.Get("", user.Issues) m.Get("/search", repo.SearchIssues) @@ -802,6 +803,7 @@ func registerRoutes(m *web.Route) { reqRepoCodeReader := context.RequireRepoReader(unit.TypeCode) reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases) reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases) + reqRepoWikiReader := context.RequireRepoReader(unit.TypeWiki) reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki) reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues) reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests) @@ -838,12 +840,12 @@ func registerRoutes(m *web.Route) { } } - // ***** START: Organization ***** m.Group("/org", func() { m.Group("/{org}", func() { m.Get("/members", org.Members) }, context.OrgAssignment()) }, ignSignIn) + // end "/org": members m.Group("/org", func() { m.Group("", func() { @@ -958,9 +960,8 @@ func registerRoutes(m *web.Route) { }, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true)) }, context.OrgAssignment(true, true)) }, reqSignIn) - // ***** END: Organization ***** + // end "/org": most org routes - // ***** START: Repository ***** m.Group("/repo", func() { m.Get("/create", repo.Create) m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost) @@ -968,6 +969,7 @@ func registerRoutes(m *web.Route) { m.Post("/migrate", web.Bind(forms.MigrateRepoForm{}), repo.MigratePost) m.Get("/search", repo.SearchRepo) }, reqSignIn) + // end "/repo": create, migrate, search m.Group("/{username}/-", func() { if setting.Packages.Enabled { @@ -1008,7 +1010,6 @@ func registerRoutes(m *web.Route) { m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard) m.Delete("", org.DeleteProjectBoard) m.Post("/default", org.SetDefaultProjectBoard) - m.Post("/move", org.MoveIssues) }) }) @@ -1023,125 +1024,152 @@ func registerRoutes(m *web.Route) { m.Group("", func() { m.Get("/code", user.CodeSearch) }, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker) - }, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code) + }, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) + // end "/{username}/-": packages, projects, code + + m.Group("/{username}/{reponame}/settings", func() { + m.Group("", func() { + m.Combo("").Get(repo_setting.Settings). + Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost) + }, repo_setting.SettingsCtxData) + m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar) + m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar) + + m.Group("/collaboration", func() { + m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost) + m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode) + m.Post("/delete", repo_setting.DeleteCollaboration) + m.Group("/team", func() { + m.Post("", repo_setting.AddTeamPost) + m.Post("/delete", repo_setting.DeleteTeam) + }) + }) + + m.Group("/branches", func() { + m.Post("/", repo_setting.SetDefaultBranchPost) + }, repo.MustBeNotEmpty) + + m.Group("/branches", func() { + m.Get("/", repo_setting.ProtectedBranchRules) + m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch). + Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost) + m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost) + }, repo.MustBeNotEmpty) + + m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost) + + m.Group("/tags", func() { + m.Get("", repo_setting.ProtectedTags) + m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost) + m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost) + m.Get("/{id}", repo_setting.EditProtectedTag) + m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost) + }) + + m.Group("/hooks/git", func() { + m.Get("", repo_setting.GitHooks) + m.Combo("/{name}").Get(repo_setting.GitHooksEdit). + Post(repo_setting.GitHooksEditPost) + }, context.GitHookService()) + + m.Group("/hooks", func() { + m.Get("", repo_setting.Webhooks) + m.Post("/delete", repo_setting.DeleteWebhook) + addWebhookAddRoutes() + m.Group("/{id}", func() { + m.Get("", repo_setting.WebHooksEdit) + m.Post("/test", repo_setting.TestWebhook) + m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) + }) + addWebhookEditRoutes() + }, webhooksEnabled) + + m.Group("/keys", func() { + m.Combo("").Get(repo_setting.DeployKeys). + Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost) + m.Post("/delete", repo_setting.DeleteDeployKey) + }) + + m.Group("/lfs", func() { + m.Get("/", repo_setting.LFSFiles) + m.Get("/show/{oid}", repo_setting.LFSFileGet) + m.Post("/delete/{oid}", repo_setting.LFSDelete) + m.Get("/pointers", repo_setting.LFSPointerFiles) + m.Post("/pointers/associate", repo_setting.LFSAutoAssociate) + m.Get("/find", repo_setting.LFSFileFind) + m.Group("/locks", func() { + m.Get("/", repo_setting.LFSLocks) + m.Post("/", repo_setting.LFSLockFile) + m.Post("/{lid}/unlock", repo_setting.LFSUnlock) + }) + }) + m.Group("/actions", func() { + m.Get("", repo_setting.RedirectToDefaultSetting) + addSettingsRunnersRoutes() + addSettingsSecretsRoutes() + addSettingsVariablesRoutes() + }, actions.MustEnableActions) + // the follow handler must be under "settings", otherwise this incomplete repo can't be accessed + m.Group("/migrate", func() { + m.Post("/retry", repo.MigrateRetryPost) + m.Post("/cancel", repo.MigrateCancelPost) + }) + }, + reqSignIn, context.RepoAssignment, context.RepoRef(), reqRepoAdmin, + ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer), + ) + // end "/{username}/{reponame}/settings" + + // user/org home, including rss feeds + m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home) m.Group("/{username}/{reponame}", func() { - m.Group("/settings", func() { - m.Group("", func() { - m.Combo("").Get(repo_setting.Settings). - Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost) - }, repo_setting.SettingsCtxData) - m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar) - m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar) - - m.Group("/collaboration", func() { - m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost) - m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode) - m.Post("/delete", repo_setting.DeleteCollaboration) - m.Group("/team", func() { - m.Post("", repo_setting.AddTeamPost) - m.Post("/delete", repo_setting.DeleteTeam) - }) - }) - - m.Group("/branches", func() { - m.Post("/", repo_setting.SetDefaultBranchPost) - }, repo.MustBeNotEmpty) - - m.Group("/branches", func() { - m.Get("/", repo_setting.ProtectedBranchRules) - m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch). - Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost) - m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost) - }, repo.MustBeNotEmpty) - - m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost) - - m.Group("/tags", func() { - m.Get("", repo_setting.ProtectedTags) - m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost) - m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost) - m.Get("/{id}", repo_setting.EditProtectedTag) - m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost) - }) - - m.Group("/hooks/git", func() { - m.Get("", repo_setting.GitHooks) - m.Combo("/{name}").Get(repo_setting.GitHooksEdit). - Post(repo_setting.GitHooksEditPost) - }, context.GitHookService()) - - m.Group("/hooks", func() { - m.Get("", repo_setting.Webhooks) - m.Post("/delete", repo_setting.DeleteWebhook) - addWebhookAddRoutes() - m.Group("/{id}", func() { - m.Get("", repo_setting.WebHooksEdit) - m.Post("/test", repo_setting.TestWebhook) - m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) - }) - addWebhookEditRoutes() - }, webhooksEnabled) - - m.Group("/keys", func() { - m.Combo("").Get(repo_setting.DeployKeys). - Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost) - m.Post("/delete", repo_setting.DeleteDeployKey) - }) - - m.Group("/lfs", func() { - m.Get("/", repo_setting.LFSFiles) - m.Get("/show/{oid}", repo_setting.LFSFileGet) - m.Post("/delete/{oid}", repo_setting.LFSDelete) - m.Get("/pointers", repo_setting.LFSPointerFiles) - m.Post("/pointers/associate", repo_setting.LFSAutoAssociate) - m.Get("/find", repo_setting.LFSFileFind) - m.Group("/locks", func() { - m.Get("/", repo_setting.LFSLocks) - m.Post("/", repo_setting.LFSLockFile) - m.Post("/{lid}/unlock", repo_setting.LFSUnlock) - }) - }) - m.Group("/actions", func() { - m.Get("", repo_setting.RedirectToDefaultSetting) - addSettingsRunnersRoutes() - addSettingsSecretsRoutes() - addSettingsVariablesRoutes() - }, actions.MustEnableActions) - // the follow handler must be under "settings", otherwise this incomplete repo can't be accessed - m.Group("/migrate", func() { - m.Post("/retry", repo.MigrateRetryPost) - m.Post("/cancel", repo.MigrateCancelPost) - }) - }, ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer)) - }, reqSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoAdmin, context.RepoRef()) - - m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment, context.UnitTypes(), repo.Action) - - // Grouping for those endpoints not requiring authentication (but should respect ignSignIn) - m.Group("/{username}/{reponame}", func() { - m.Group("/milestone", func() { - m.Get("/{id}", repo.MilestoneIssuesAndPulls) - }, reqRepoIssuesOrPullsReader, context.RepoRef()) m.Get("/find/*", repo.FindFiles) m.Group("/tree-list", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.TreeList) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.TreeList) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.TreeList) }) - m.Get("/compare", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists, ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff) - m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists). + m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff) + m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists). Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff). Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost) + }, ignSignIn, context.RepoAssignment, reqRepoCodeReader) + // end "/{username}/{reponame}": find, compare, list (code related) + + m.Group("/{username}/{reponame}", func() { + m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because it would conflict with other routes like "/pulls/{index}" + m.Get("/pulls/posters", repo.PullPosters) + m.Get("/comments/{id}/attachments", repo.GetCommentAttachments) + m.Get("/labels", repo.RetrieveLabels, repo.Labels) + m.Get("/milestones", repo.Milestones) + m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls) m.Group("/{type:issues|pulls}", func() { m.Group("/{index}", func() { m.Get("/info", repo.GetIssueInfo) + m.Get("/attachments", repo.GetIssueAttachments) + m.Get("/attachments/{uuid}", repo.GetAttachment) + m.Group("/content-history", func() { + m.Get("/overview", repo.GetContentHistoryOverview) + m.Get("/list", repo.GetContentHistoryList) + m.Get("/detail", repo.GetContentHistoryDetail) + }) + }) + }, context.RepoRef()) + }, ignSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) + // end "/{username}/{reponame}": view milestone, label, issue, pull, etc + + m.Group("/{username}/{reponame}", func() { + m.Group("/{type:issues|pulls}", func() { + m.Get("", repo.Issues) + m.Group("/{index}", func() { + m.Get("", repo.ViewIssue) }) }) - }, ignSignIn, context.RepoAssignment, context.UnitTypes()) // for "/{username}/{reponame}" which doesn't require authentication + }, ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker)) + // end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker - // Grouping for those endpoints that do require authentication - m.Group("/{username}/{reponame}", func() { + m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc m.Group("/issues", func() { m.Group("/new", func() { m.Combo("").Get(context.RepoRef(), repo.NewIssue). @@ -1150,6 +1178,7 @@ func registerRoutes(m *web.Route) { }) m.Get("/search", repo.ListIssues) }, context.RepoMustNotBeArchived(), reqRepoIssueReader) + // FIXME: should use different URLs but mostly same logic for comments of issue and pull request. // So they can apply their own enable/disable logic on routers. m.Group("/{type:issues|pulls}", func() { @@ -1179,10 +1208,7 @@ func registerRoutes(m *web.Route) { m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue) m.Post("/delete", reqRepoAdmin, repo.DeleteIssue) }, context.RepoMustNotBeArchived()) - m.Group("/{index}", func() { - m.Get("/attachments", repo.GetIssueAttachments) - m.Get("/attachments/{uuid}", repo.GetAttachment) - }) + m.Group("/{index}", func() { m.Post("/content-history/soft-delete", repo.SoftDeleteContentHistory) }) @@ -1191,25 +1217,25 @@ func registerRoutes(m *web.Route) { m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) - m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) + m.Post("/request_review", repo.UpdatePullReviewRequest) m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview) m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues) - m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.SetShowOutdatedComments, repo.UpdateResolveConversation) + m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation) m.Post("/attachments", repo.UploadIssueAttachment) m.Post("/attachments/remove", repo.DeleteAttachment) m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin) m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove) }, context.RepoMustNotBeArchived()) + m.Group("/comments/{id}", func() { m.Post("", repo.UpdateCommentContent) m.Post("/delete", repo.DeleteComment) m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction) }, context.RepoMustNotBeArchived()) - m.Group("/comments/{id}", func() { - m.Get("/attachments", repo.GetCommentAttachments) - }) + m.Post("/markup", web.Bind(structs.MarkupOption{}), misc.Markup) + m.Group("/labels", func() { m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel) m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel) @@ -1227,7 +1253,10 @@ func registerRoutes(m *web.Route) { m.Group("/pull", func() { m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget) }, context.RepoMustNotBeArchived()) + }, reqSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) + // end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones + m.Group("/{username}/{reponame}", func() { // repo code m.Group("", func() { m.Group("", func() { m.Combo("/_edit/*").Get(repo.EditFile). @@ -1261,26 +1290,26 @@ func registerRoutes(m *web.Route) { m.Post("/restore", repo.RestoreBranchPost) }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) - m.Combo("/fork", reqRepoCodeReader).Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost) - }, reqSignIn, context.RepoAssignment, context.UnitTypes()) + m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost) + }, reqSignIn, context.RepoAssignment, reqRepoCodeReader) + // end "/{username}/{reponame}": repo code - // Tags - m.Group("/{username}/{reponame}", func() { + m.Group("/{username}/{reponame}", func() { // repo tags m.Group("/tags", func() { m.Get("", repo.TagsList) m.Get("/list", repo.GetTagList) m.Get(".rss", feedEnabled, repo.TagsListFeedRSS) m.Get(".atom", feedEnabled, repo.TagsListFeedAtom) }, ctxDataSet("EnableFeed", setting.Other.EnableFeed), - repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true)) + repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, true)) m.Post("/tags/delete", repo.DeleteTag, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) - }, ignSignIn, context.RepoAssignment, context.UnitTypes()) + }, ignSignIn, context.RepoAssignment, reqRepoCodeReader) + // end "/{username}/{reponame}": repo tags - // Releases - m.Group("/{username}/{reponame}", func() { + m.Group("/{username}/{reponame}", func() { // repo releases m.Group("/releases", func() { - m.Get("/", repo.Releases) + m.Get("", repo.Releases) m.Get("/tag/*", repo.SingleRelease) m.Get("/latest", repo.LatestRelease) m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS) @@ -1300,148 +1329,141 @@ func registerRoutes(m *web.Route) { m.Get("/edit/*", repo.EditRelease) m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost) }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache) - }, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader) + }, ignSignIn, context.RepoAssignment, reqRepoReleaseReader) + // end "/{username}/{reponame}": repo releases - // to maintain compatibility with old attachments - m.Group("/{username}/{reponame}", func() { + m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments m.Get("/attachments/{uuid}", repo.GetAttachment) - }, ignSignIn, context.RepoAssignment, context.UnitTypes()) + }, ignSignIn, context.RepoAssignment) + // end "/{username}/{reponame}": compatibility with old attachments m.Group("/{username}/{reponame}", func() { m.Post("/topics", repo.TopicsPost) - }, context.RepoAssignment, context.RepoMustNotBeArchived(), reqRepoAdmin) + }, context.RepoAssignment, reqRepoAdmin, context.RepoMustNotBeArchived()) m.Group("/{username}/{reponame}", func() { - m.Group("", func() { - m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because other routes like "/pulls/{index}" has higher priority - m.Get("/{type:issues|pulls}", repo.Issues) - m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue) - m.Group("/{type:issues|pulls}/{index}/content-history", func() { - m.Get("/overview", repo.GetContentHistoryOverview) - m.Get("/list", repo.GetContentHistoryList) - m.Get("/detail", repo.GetContentHistoryDetail) - }) - m.Get("/labels", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels) - m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones) - }, context.RepoRef()) - if setting.Packages.Enabled { m.Get("/packages", repo.Packages) } + }, ignSignIn, context.RepoAssignment) - m.Group("/projects", func() { - m.Get("", repo.Projects) - m.Get("/{id}", repo.ViewProject) - m.Group("", func() { //nolint:dupl - m.Get("/new", repo.RenderNewProject) - m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) - m.Group("/{id}", func() { - m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost) - m.Post("/delete", repo.DeleteProject) + m.Group("/{username}/{reponame}/projects", func() { + m.Get("", repo.Projects) + m.Get("/{id}", repo.ViewProject) + m.Group("", func() { //nolint:dupl + m.Get("/new", repo.RenderNewProject) + m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) + m.Group("/{id}", func() { + m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost) + m.Post("/delete", repo.DeleteProject) - m.Get("/edit", repo.RenderEditProject) - m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost) - m.Post("/{action:open|close}", repo.ChangeProjectStatus) + m.Get("/edit", repo.RenderEditProject) + m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost) + m.Post("/{action:open|close}", repo.ChangeProjectStatus) - m.Group("/{boardID}", func() { - m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard) - m.Delete("", repo.DeleteProjectBoard) - m.Post("/default", repo.SetDefaultProjectBoard) - - m.Post("/move", repo.MoveIssues) - }) + m.Group("/{boardID}", func() { + m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard) + m.Delete("", repo.DeleteProjectBoard) + m.Post("/default", repo.SetDefaultProjectBoard) + m.Post("/move", repo.MoveIssues) }) - }, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) - }, reqRepoProjectsReader, repo.MustEnableRepoProjects) + }) + }, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) + }, ignSignIn, context.RepoAssignment, reqRepoProjectsReader, repo.MustEnableRepoProjects) + // end "/{username}/{reponame}/projects" - m.Group("/actions", func() { - m.Get("", actions.List) - m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile) - m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile) + m.Group("/{username}/{reponame}/actions", func() { + m.Get("", actions.List) + m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile) + m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile) - m.Group("/runs/{run}", func() { + m.Group("/runs/{run}", func() { + m.Combo(""). + Get(actions.View). + Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) + m.Group("/jobs/{job}", func() { m.Combo(""). Get(actions.View). Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) - m.Group("/jobs/{job}", func() { - m.Combo(""). - Get(actions.View). - Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) - m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) - m.Get("/logs", actions.Logs) - }) - m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) - m.Post("/approve", reqRepoActionsWriter, actions.Approve) - m.Get("/artifacts", actions.ArtifactsView) - m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) - m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) + m.Get("/logs", actions.Logs) }) - m.Group("/workflows/{workflow_name}", func() { - m.Get("/badge.svg", actions.GetWorkflowBadge) - }) - }, reqRepoActionsReader, actions.MustEnableActions) - - m.Group("/wiki", func() { - m.Combo("/"). - Get(repo.Wiki). - Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost) - m.Combo("/*"). - Get(repo.Wiki). - Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost) - m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) - m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff) - }, repo.MustEnableWiki, func(ctx *context.Context) { - ctx.Data["PageIsWiki"] = true - ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink() + m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) + m.Post("/approve", reqRepoActionsWriter, actions.Approve) + m.Get("/artifacts", actions.ArtifactsView) + m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) + m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView) + m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) + m.Group("/workflows/{workflow_name}", func() { + m.Get("/badge.svg", actions.GetWorkflowBadge) + }) + }, ignSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions) + // end "/{username}/{reponame}/actions" - m.Group("/wiki", func() { - m.Get("/raw/*", repo.WikiRaw) - }, repo.MustEnableWiki) + m.Group("/{username}/{reponame}/wiki", func() { + m.Combo(""). + Get(repo.Wiki). + Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost) + m.Combo("/*"). + Get(repo.Wiki). + Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost) + m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) + m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff) + m.Get("/raw/*", repo.WikiRaw) + }, ignSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) { + ctx.Data["PageIsWiki"] = true + ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink() + }) + // end "/{username}/{reponame}/wiki" - m.Group("/activity", func() { - m.Get("", repo.Activity) - m.Get("/{period}", repo.Activity) - m.Group("/contributors", func() { - m.Get("", repo.Contributors) - m.Get("/data", repo.ContributorsData) - }) - m.Group("/code-frequency", func() { - m.Get("", repo.CodeFrequency) - m.Get("/data", repo.CodeFrequencyData) - }) - m.Group("/recent-commits", func() { - m.Get("", repo.RecentCommits) - m.Get("/data", repo.RecentCommitsData) - }) - }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases)) + m.Group("/{username}/{reponame}/activity", func() { + m.Get("", repo.Activity) + m.Get("/{period}", repo.Activity) + m.Group("/contributors", func() { + m.Get("", repo.Contributors) + m.Get("/data", repo.ContributorsData) + }) + m.Group("/code-frequency", func() { + m.Get("", repo.CodeFrequency) + m.Get("/data", repo.CodeFrequencyData) + }) + m.Group("/recent-commits", func() { + m.Get("", repo.RecentCommits) + m.Get("/data", repo.RecentCommitsData) + }) + }, + ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases), + context.RepoRef(), repo.MustBeNotEmpty, + ) + // end "/{username}/{reponame}/activity" + m.Group("/{username}/{reponame}", func() { m.Group("/activity_author_data", func() { m.Get("", repo.ActivityAuthors) m.Get("/{period}", repo.ActivityAuthors) - }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode)) + }, context.RepoRef(), repo.MustBeNotEmpty) m.Group("/archive", func() { m.Get("/*", repo.Download) m.Post("/*", repo.InitiateDownload) - }, repo.MustBeNotEmpty, dlSourceEnabled, reqRepoCodeReader) + }, repo.MustBeNotEmpty, dlSourceEnabled) m.Group("/branches", func() { m.Get("/list", repo.GetBranchesList) m.Get("", repo.Branches) - }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) + }, repo.MustBeNotEmpty, context.RepoRef()) m.Group("/blob_excerpt", func() { m.Get("/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) }, func(ctx *context.Context) gocontext.CancelFunc { + // FIXME: refactor this function, use separate routes for wiki/code if ctx.FormBool("wiki") { ctx.Data["PageIsWiki"] = true repo.MustEnableWiki(ctx) return nil } - reqRepoCodeReader(ctx) if ctx.Written() { return nil } @@ -1454,7 +1476,6 @@ func registerRoutes(m *web.Route) { return cancel }) - m.Get("/pulls/posters", repo.PullPosters) m.Group("/pulls/{index}", func() { m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue) m.Get(".diff", repo.DownloadPullDiff) @@ -1488,7 +1509,7 @@ func registerRoutes(m *web.Route) { m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS) - }, repo.MustBeNotEmpty, reqRepoCodeReader) + }, repo.MustBeNotEmpty) m.Group("/raw", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload) @@ -1497,14 +1518,14 @@ func registerRoutes(m *web.Route) { m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) - }, repo.MustBeNotEmpty, reqRepoCodeReader) + }, repo.MustBeNotEmpty) m.Group("/render", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RenderFile) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RenderFile) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RenderFile) m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.RenderFile) - }, repo.MustBeNotEmpty, reqRepoCodeReader) + }, repo.MustBeNotEmpty) m.Group("/commits", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits) @@ -1512,20 +1533,20 @@ func registerRoutes(m *web.Route) { m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits) - }, repo.MustBeNotEmpty, reqRepoCodeReader) + }, repo.MustBeNotEmpty) m.Group("/blame", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame) - }, repo.MustBeNotEmpty, reqRepoCodeReader) + }, repo.MustBeNotEmpty) m.Group("", func() { m.Get("/graph", repo.Graph) m.Get("/commit/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) m.Get("/commit/{sha:([a-f0-9]{7,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags) m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick) - }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) + }, repo.MustBeNotEmpty, context.RepoRef()) m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed) m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed) @@ -1534,51 +1555,42 @@ func registerRoutes(m *web.Route) { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) - // "/*" route is deprecated, and kept for backward compatibility - m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) + m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) // "/*" route is deprecated, and kept for backward compatibility }, repo.SetEditorconfigIfExists) - m.Group("", func() { - m.Get("/forks", repo.Forks) - }, context.RepoRef(), reqRepoCodeReader) - m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) - }, ignSignIn, context.RepoAssignment, context.UnitTypes()) - - m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit) + m.Get("/forks", context.RepoRef(), repo.Forks) + m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff) + m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit) + }, ignSignIn, context.RepoAssignment, reqRepoCodeReader) + // end "/{username}/{reponame}": repo code m.Group("/{username}/{reponame}", func() { m.Get("/stars", repo.Stars) m.Get("/watchers", repo.Watchers) m.Get("/search", reqRepoCodeReader, repo.Search) - }, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes()) + m.Post("/action/{action}", reqSignIn, repo.Action) + }, ignSignIn, context.RepoAssignment, context.RepoRef()) - m.Group("/{username}", func() { - m.Group("/{reponame}", func() { - m.Get("", repo.SetEditorconfigIfExists, repo.Home) - }, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes()) - - m.Group("/{reponame}", func() { - m.Group("/info/lfs", func() { - m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler) - m.Put("/objects/{oid}/{size}", lfs.UploadHandler) - m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler) - m.Get("/objects/{oid}", lfs.DownloadHandler) - m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler) - m.Group("/locks", func() { - m.Get("/", lfs.GetListLockHandler) - m.Post("/", lfs.PostLockHandler) - m.Post("/verify", lfs.VerifyLockHandler) - m.Post("/{lid}/unlock", lfs.UnLockHandler) - }, lfs.CheckAcceptMediaType) - m.Any("/*", func(ctx *context.Context) { - ctx.NotFound("", nil) - }) - }, ignSignInAndCsrf, lfsServerEnabled) - - gitHTTPRouters(m) - }) + m.Group("/{username}/{reponame}", func() { + m.Group("/info/lfs", func() { + m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler) + m.Put("/objects/{oid}/{size}", lfs.UploadHandler) + m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler) + m.Get("/objects/{oid}", lfs.DownloadHandler) + m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler) + m.Group("/locks", func() { + m.Get("/", lfs.GetListLockHandler) + m.Post("/", lfs.PostLockHandler) + m.Post("/verify", lfs.VerifyLockHandler) + m.Post("/{lid}/unlock", lfs.UnLockHandler) + }, lfs.CheckAcceptMediaType) + m.Any("/*", func(ctx *context.Context) { + ctx.NotFound("", nil) + }) + }, ignSignInAndCsrf, lfsServerEnabled) + gitHTTPRouters(m) }) - // ***** END: Repository ***** + // end "/{username}/{reponame}.git": git support m.Group("/notifications", func() { m.Get("", user.Notifications) diff --git a/services/context/context.go b/services/context/context.go index 9d7787bf4b..1641e995fb 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -102,6 +102,18 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext { tmplCtx["Locale"] = ctx.Base.Locale tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) tmplCtx["RootData"] = ctx.Data + tmplCtx["Consts"] = map[string]any{ + "RepoUnitTypeCode": unit.TypeCode, + "RepoUnitTypeIssues": unit.TypeIssues, + "RepoUnitTypePullRequests": unit.TypePullRequests, + "RepoUnitTypeReleases": unit.TypeReleases, + "RepoUnitTypeWiki": unit.TypeWiki, + "RepoUnitTypeExternalWiki": unit.TypeExternalWiki, + "RepoUnitTypeExternalTracker": unit.TypeExternalTracker, + "RepoUnitTypeProjects": unit.TypeProjects, + "RepoUnitTypePackages": unit.TypePackages, + "RepoUnitTypeActions": unit.TypeActions, + } return tmplCtx } diff --git a/services/context/repo.go b/services/context/repo.go index 56e9fada0e..1f4c698afc 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -383,7 +383,6 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { ctx.NotFound("no access right", nil) return } - ctx.Data["HasAccess"] = true ctx.Data["Permission"] = &ctx.Repo.Permission if repo.IsMirror { @@ -1052,19 +1051,3 @@ func GitHookService() func(ctx *Context) { } } } - -// UnitTypes returns a middleware to set unit types to context variables. -func UnitTypes() func(ctx *Context) { - return func(ctx *Context) { - ctx.Data["UnitTypeCode"] = unit_model.TypeCode - ctx.Data["UnitTypeIssues"] = unit_model.TypeIssues - ctx.Data["UnitTypePullRequests"] = unit_model.TypePullRequests - ctx.Data["UnitTypeReleases"] = unit_model.TypeReleases - ctx.Data["UnitTypeWiki"] = unit_model.TypeWiki - ctx.Data["UnitTypeExternalWiki"] = unit_model.TypeExternalWiki - ctx.Data["UnitTypeExternalTracker"] = unit_model.TypeExternalTracker - ctx.Data["UnitTypeProjects"] = unit_model.TypeProjects - ctx.Data["UnitTypePackages"] = unit_model.TypePackages - ctx.Data["UnitTypeActions"] = unit_model.TypeActions - } -} diff --git a/templates/explore/navbar.tmpl b/templates/explore/navbar.tmpl index 8e619fa66f..a157cd4b75 100644 --- a/templates/explore/navbar.tmpl +++ b/templates/explore/navbar.tmpl @@ -11,7 +11,7 @@ {{svg "octicon-organization"}} {{ctx.Locale.Tr "explore.organizations"}} - {{if and (not $.UnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}} + {{if and (not ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}} {{svg "octicon-code"}} {{ctx.Locale.Tr "explore.code"}} diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl index 30d1a3d78d..ccef8e4b38 100644 --- a/templates/repo/blame.tmpl +++ b/templates/repo/blame.tmpl @@ -80,7 +80,7 @@ {{end}}{{/* end if .IsFileTooLarge */}}
- {{if $.Permission.CanRead $.UnitTypeIssues}} + {{if $.Permission.CanRead ctx.Consts.RepoUnitTypeIssues}} {{ctx.Locale.Tr "repo.issues.context.reference_issue"}} {{end}} {{ctx.Locale.Tr "repo.file_copy_permalink"}} diff --git a/templates/repo/code_frequency.tmpl b/templates/repo/code_frequency.tmpl index 50ec1beb6b..79b45d4931 100644 --- a/templates/repo/code_frequency.tmpl +++ b/templates/repo/code_frequency.tmpl @@ -1,4 +1,4 @@ -{{if .Permission.CanRead $.UnitTypeCode}} +{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
{{ctx.Locale.Tr "repo.diff.browse_source"}} - {{if and ($.Permission.CanWrite $.UnitTypeCode) (not $.Repository.IsArchived) (not .IsDeleted)}}{{- /* */ -}} + {{if and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) (not $.Repository.IsArchived) (not .IsDeleted)}}{{- /* */ -}}