From 27deea7330f83ddb37c918afbb4159053d8847cb Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 2 Mar 2024 23:05:07 +0800 Subject: [PATCH 01/12] Make PR form use toast to show error message (#29545) ![image](https://github.com/go-gitea/gitea/assets/2114189/b7a14ed6-db89-4f21-a590-66cd33307233) --- routers/web/repo/branch.go | 2 +- routers/web/repo/editor.go | 8 ++++---- routers/web/repo/issue.go | 15 ++++++++------- routers/web/repo/pull.go | 15 +++++++-------- services/context/context_response.go | 9 +++++---- web_src/js/features/common-global.js | 11 ++++++++--- web_src/js/modules/toast.js | 5 ++--- 7 files changed, 35 insertions(+), 30 deletions(-) diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 05f06a3ceb7a9..53bd599b0dde9 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -231,7 +231,7 @@ func CreateBranch(ctx *context.Context) { if len(e.Message) == 0 { ctx.Flash.Error(ctx.Tr("repo.editor.push_rejected_no_message")) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(e.Message), diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 8f3d9612eca0b..6146ce4ce4a38 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -341,7 +341,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b if len(errPushRej.Message) == 0 { ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplEditFile, &form) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(errPushRej.Message), @@ -353,7 +353,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b ctx.RenderWithErr(flashError, tplEditFile, &form) } } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.fail_to_update_file", form.TreePath), "Summary": ctx.Tr("repo.editor.fail_to_update_file_summary"), "Details": utils.SanitizeFlashErrorString(err.Error()), @@ -542,7 +542,7 @@ func DeleteFilePost(ctx *context.Context) { if len(errPushRej.Message) == 0 { ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplDeleteFile, &form) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(errPushRej.Message), @@ -742,7 +742,7 @@ func UploadFilePost(ctx *context.Context) { if len(errPushRej.Message) == 0 { ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplUploadFile, &form) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(errPushRej.Message), diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index ebaa955ac8057..cec0f87471b1e 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -9,6 +9,7 @@ import ( stdCtx "context" "errors" "fmt" + "html/template" "math/big" "net/http" "net/url" @@ -1016,7 +1017,7 @@ func NewIssue(ctx *context.Context) { ctx.HTML(http.StatusOK, tplIssueNew) } -func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) string { +func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) template.HTML { var files []string for k := range errs { files = append(files, k) @@ -1028,14 +1029,14 @@ func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) string lines = append(lines, fmt.Sprintf("%s: %v", file, errs[file])) } - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.issues.choose.ignore_invalid_templates"), "Summary": ctx.Tr("repo.issues.choose.invalid_templates", len(errs)), "Details": utils.SanitizeFlashErrorString(strings.Join(lines, "\n")), }) if err != nil { log.Debug("render flash error: %v", err) - flashError = ctx.Locale.TrString("repo.issues.choose.ignore_invalid_templates") + flashError = ctx.Locale.Tr("repo.issues.choose.ignore_invalid_templates") } return flashError } @@ -3296,7 +3297,7 @@ func ChangeIssueReaction(ctx *context.Context) { return } - html, err := ctx.RenderToString(tplReactions, map[string]any{ + html, err := ctx.RenderToHTML(tplReactions, map[string]any{ "ctxData": ctx.Data, "ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index), "Reactions": issue.Reactions.GroupByType(), @@ -3403,7 +3404,7 @@ func ChangeCommentReaction(ctx *context.Context) { return } - html, err := ctx.RenderToString(tplReactions, map[string]any{ + html, err := ctx.RenderToHTML(tplReactions, map[string]any{ "ctxData": ctx.Data, "ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID), "Reactions": comment.Reactions.GroupByType(), @@ -3546,8 +3547,8 @@ func updateAttachments(ctx *context.Context, item any, files []string) error { return err } -func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) string { - attachHTML, err := ctx.RenderToString(tplAttachment, map[string]any{ +func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) template.HTML { + attachHTML, err := ctx.RenderToHTML(tplAttachment, map[string]any{ "ctxData": ctx.Data, "Attachments": attachments, "Content": content, diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 428fe23156fc1..bf52d76e95616 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1129,7 +1129,7 @@ func UpdatePullRequest(ctx *context.Context) { if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil { if models.IsErrMergeConflicts(err) { conflictError := err.(models.ErrMergeConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.merge_conflict"), "Summary": ctx.Tr("repo.pulls.merge_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1143,7 +1143,7 @@ func UpdatePullRequest(ctx *context.Context) { return } else if models.IsErrRebaseConflicts(err) { conflictError := err.(models.ErrRebaseConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1275,7 +1275,7 @@ func MergePullRequest(ctx *context.Context) { ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option")) } else if models.IsErrMergeConflicts(err) { conflictError := err.(models.ErrMergeConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.merge_conflict"), "Summary": ctx.Tr("repo.editor.merge_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1288,7 +1288,7 @@ func MergePullRequest(ctx *context.Context) { ctx.JSONRedirect(issue.Link()) } else if models.IsErrRebaseConflicts(err) { conflictError := err.(models.ErrRebaseConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1318,7 +1318,7 @@ func MergePullRequest(ctx *context.Context) { if len(message) == 0 { ctx.Flash.Error(ctx.Tr("repo.pulls.push_rejected_no_message")) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.push_rejected"), "Summary": ctx.Tr("repo.pulls.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(pushrejErr.Message), @@ -1491,7 +1491,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.JSONError(ctx.Tr("repo.pulls.push_rejected_no_message")) return } - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ + flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.push_rejected"), "Summary": ctx.Tr("repo.pulls.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(pushrejErr.Message), @@ -1500,8 +1500,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.ServerError("CompareAndPullRequest.HTMLString", err) return } - ctx.Flash.Error(flashError) - ctx.JSONRedirect(ctx.Link + "?" + ctx.Req.URL.RawQuery) // FIXME: it's unfriendly, and will make the content lost + ctx.JSONError(flashError) return } ctx.ServerError("NewPullRequest", err) diff --git a/services/context/context_response.go b/services/context/context_response.go index 829bca1f59209..372b4cb38b6f6 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -6,6 +6,7 @@ package context import ( "errors" "fmt" + "html/template" "net" "net/http" "net/url" @@ -104,11 +105,11 @@ func (ctx *Context) JSONTemplate(tmpl base.TplName) { } } -// RenderToString renders the template content to a string -func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) { +// RenderToHTML renders the template content to a HTML string +func (ctx *Context) RenderToHTML(name base.TplName, data map[string]any) (template.HTML, error) { var buf strings.Builder - err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data, ctx.TemplateContext) - return buf.String(), err + err := ctx.Render.HTML(&buf, 0, string(name), data, ctx.TemplateContext) + return template.HTML(buf.String()), err } // RenderWithErr used for page has form validation but need to prompt error to users. diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index f90591aff3131..c53d43cbb2dec 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -91,19 +91,24 @@ async function fetchActionDoRequest(actionElem, url, opt) { } else { window.location.reload(); } + return; } else if (resp.status >= 400 && resp.status < 500) { const data = await resp.json(); // the code was quite messy, sometimes the backend uses "err", sometimes it uses "error", and even "user_error" // but at the moment, as a new approach, we only use "errorMessage" here, backend can use JSONError() to respond. - showErrorToast(data.errorMessage || `server error: ${resp.status}`); + if (data.errorMessage) { + showErrorToast(data.errorMessage, {useHtmlBody: data.renderFormat === 'html'}); + } else { + showErrorToast(`server error: ${resp.status}`); + } } else { showErrorToast(`server error: ${resp.status}`); } } catch (e) { console.error('error when doRequest', e); - actionElem.classList.remove('is-loading', 'small-loading-icon'); - showErrorToast(i18n.network_error); + showErrorToast(`${i18n.network_error} ${e}`); } + actionElem.classList.remove('is-loading', 'small-loading-icon'); } async function formFetchAction(e) { diff --git a/web_src/js/modules/toast.js b/web_src/js/modules/toast.js index fa075aed4843e..d64359799c9db 100644 --- a/web_src/js/modules/toast.js +++ b/web_src/js/modules/toast.js @@ -21,13 +21,12 @@ const levels = { }; // See https://github.com/apvarun/toastify-js#api for options -function showToast(message, level, {gravity, position, duration, ...other} = {}) { +function showToast(message, level, {gravity, position, duration, useHtmlBody, ...other} = {}) { const {icon, background, duration: levelDuration} = levels[level ?? 'info']; - const toast = Toastify({ text: `
${svg(icon)}
-
${htmlEscape(message)}
+
${useHtmlBody ? message : htmlEscape(message)}
`, escapeMarkup: false, From 3f081d4b54261c1b4ee4f1df40c610fdd9581ef2 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 2 Mar 2024 23:30:18 +0800 Subject: [PATCH 02/12] Rename Action.GetDisplayName to GetActDisplayName (#29540) To avoid conflicting with User.GetDisplayName, because there is no data type in template. And it matches other methods like GetActFullName / GetActUserName --- models/activities/action.go | 8 ++++---- templates/user/dashboard/feeds.tmpl | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/models/activities/action.go b/models/activities/action.go index 15bd9a52acc5c..fcc97e387264d 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -225,8 +225,8 @@ func (a *Action) ShortActUserName(ctx context.Context) string { return base.EllipsisString(a.GetActUserName(ctx), 20) } -// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank. -func (a *Action) GetDisplayName(ctx context.Context) string { +// GetActDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank. +func (a *Action) GetActDisplayName(ctx context.Context) string { if setting.UI.DefaultShowFullName { trimmedFullName := strings.TrimSpace(a.GetActFullName(ctx)) if len(trimmedFullName) > 0 { @@ -236,8 +236,8 @@ func (a *Action) GetDisplayName(ctx context.Context) string { return a.ShortActUserName(ctx) } -// GetDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME -func (a *Action) GetDisplayNameTitle(ctx context.Context) string { +// GetActDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME +func (a *Action) GetActDisplayNameTitle(ctx context.Context) string { if setting.UI.DefaultShowFullName { return a.ShortActUserName(ctx) } diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index 6dec610e930ce..0e7371ad83a21 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -7,7 +7,7 @@
{{if gt .ActUser.ID 0}} - {{.GetDisplayName ctx}} + {{.GetActDisplayName ctx}} {{else}} {{.ShortActUserName ctx}} {{end}} From a3f05d0d98408bb47333b19f505b21afcefa9e7c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 2 Mar 2024 16:42:31 +0100 Subject: [PATCH 03/12] remove util.OptionalBool and related functions (#29513) and migrate affected code _last refactoring bits to replace **util.OptionalBool** with **optional.Option[bool]**_ --- models/actions/runner.go | 13 ++-- models/auth/source.go | 9 +-- models/issues/comment.go | 15 ++-- models/issues/issue_search.go | 25 +++---- models/issues/issue_stats.go | 8 +-- models/issues/label.go | 3 +- models/issues/milestone.go | 3 +- models/issues/milestone_list.go | 8 +-- models/issues/milestone_test.go | 24 +++---- models/issues/review_list.go | 8 +-- models/issues/tracked_time.go | 9 +-- models/issues/tracked_time_test.go | 8 +-- models/packages/nuget/search.go | 2 +- models/packages/package_version.go | 25 +++---- models/project/project.go | 10 ++- models/repo/repo.go | 7 +- models/repo/repo_test.go | 6 +- models/user/user.go | 8 +-- models/webhook/webhook.go | 7 +- models/webhook/webhook_system.go | 8 +-- models/webhook/webhook_test.go | 6 +- modules/indexer/issues/bleve/bleve.go | 8 +-- modules/indexer/issues/db/options.go | 3 +- .../issues/elasticsearch/elasticsearch.go | 8 +-- modules/indexer/issues/indexer_test.go | 10 +-- modules/indexer/issues/internal/model.go | 6 +- .../indexer/issues/internal/tests/tests.go | 10 +-- .../indexer/issues/meilisearch/meilisearch.go | 8 +-- modules/util/util.go | 51 ------------- routers/api/packages/cargo/cargo.go | 3 +- routers/api/packages/chef/chef.go | 5 +- routers/api/packages/composer/composer.go | 3 +- routers/api/packages/goproxy/goproxy.go | 3 +- routers/api/packages/helm/helm.go | 5 +- routers/api/packages/npm/npm.go | 7 +- routers/api/packages/nuget/nuget.go | 11 +-- routers/api/packages/rubygems/rubygems.go | 5 +- routers/api/packages/swift/swift.go | 3 +- routers/api/v1/admin/hooks.go | 3 +- routers/api/v1/packages/package.go | 4 +- routers/api/v1/repo/issue.go | 41 +++++------ routers/api/v1/repo/issue_comment.go | 10 +-- routers/api/v1/repo/milestone.go | 6 +- routers/web/admin/hooks.go | 4 +- routers/web/admin/packages.go | 4 +- routers/web/admin/users.go | 4 +- routers/web/auth/auth.go | 8 +-- routers/web/org/projects.go | 8 +-- routers/web/repo/actions/actions.go | 4 +- routers/web/repo/branch.go | 3 +- routers/web/repo/issue.go | 72 +++++++++---------- routers/web/repo/milestone.go | 6 +- routers/web/repo/packages.go | 4 +- routers/web/repo/projects.go | 6 +- routers/web/shared/packages/packages.go | 4 +- routers/web/shared/user/header.go | 3 +- routers/web/user/home.go | 15 ++-- routers/web/user/notification.go | 19 +++-- routers/web/user/package.go | 11 +-- routers/web/user/setting/security/security.go | 4 +- services/auth/signin.go | 4 +- services/auth/source/oauth2/init.go | 4 +- services/auth/source/oauth2/providers.go | 4 +- services/auth/sspi.go | 3 +- services/migrations/gitea_uploader_test.go | 8 +-- services/packages/cleanup/cleanup.go | 4 +- services/packages/container/cleanup.go | 6 +- services/packages/packages.go | 6 +- services/pull/review.go | 8 +-- services/repository/branch.go | 5 +- services/webhook/webhook.go | 7 +- 71 files changed, 308 insertions(+), 355 deletions(-) diff --git a/models/actions/runner.go b/models/actions/runner.go index b646146ee6728..67f003387b112 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -13,6 +13,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/shared/types" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" @@ -159,7 +160,7 @@ type FindRunnerOptions struct { OwnerID int64 Sort string Filter string - IsOnline util.OptionalBool + IsOnline optional.Option[bool] WithAvailable bool // not only runners belong to, but also runners can be used } @@ -186,10 +187,12 @@ func (opts FindRunnerOptions) ToConds() builder.Cond { cond = cond.And(builder.Like{"name", opts.Filter}) } - if opts.IsOnline.IsTrue() { - cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()}) - } else if opts.IsOnline.IsFalse() { - cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()}) + if opts.IsOnline.Has() { + if opts.IsOnline.Value() { + cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()}) + } else { + cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()}) + } } return cond } diff --git a/models/auth/source.go b/models/auth/source.go index 1bdde8235cdb4..f360ca98017d0 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -243,14 +244,14 @@ func CreateSource(ctx context.Context, source *Source) error { type FindSourcesOptions struct { db.ListOptions - IsActive util.OptionalBool + IsActive optional.Option[bool] LoginType Type } func (opts FindSourcesOptions) ToConds() builder.Cond { conds := builder.NewCond() - if !opts.IsActive.IsNone() { - conds = conds.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) + if opts.IsActive.Has() { + conds = conds.And(builder.Eq{"is_active": opts.IsActive.Value()}) } if opts.LoginType != NoType { conds = conds.And(builder.Eq{"`type`": opts.LoginType}) @@ -262,7 +263,7 @@ func (opts FindSourcesOptions) ToConds() builder.Cond { // source of type LoginSSPI func IsSSPIEnabled(ctx context.Context) bool { exist, err := db.Exist[Source](ctx, FindSourcesOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), LoginType: SSPI, }.ToConds()) if err != nil { diff --git a/models/issues/comment.go b/models/issues/comment.go index da91a83384ec2..e37f844b5c7d2 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -1036,8 +1037,8 @@ type FindCommentsOptions struct { TreePath string Type CommentType IssueIDs []int64 - Invalidated util.OptionalBool - IsPull util.OptionalBool + Invalidated optional.Option[bool] + IsPull optional.Option[bool] } // ToConds implements FindOptions interface @@ -1069,11 +1070,11 @@ func (opts FindCommentsOptions) ToConds() builder.Cond { if len(opts.TreePath) > 0 { cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath}) } - if !opts.Invalidated.IsNone() { - cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.IsTrue()}) + if opts.Invalidated.Has() { + cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.Value()}) } - if opts.IsPull != util.OptionalBoolNone { - cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.IsTrue()}) + if opts.IsPull.Has() { + cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.Value()}) } return cond } @@ -1082,7 +1083,7 @@ func (opts FindCommentsOptions) ToConds() builder.Cond { func FindComments(ctx context.Context, opts *FindCommentsOptions) (CommentList, error) { comments := make([]*Comment, 0, 10) sess := db.GetEngine(ctx).Where(opts.ToConds()) - if opts.RepoID > 0 || opts.IsPull != util.OptionalBoolNone { + if opts.RepoID > 0 || opts.IsPull.Has() { sess.Join("INNER", "issue", "issue.id = comment.issue_id") } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 7dc277327a89c..c5c9cecdb92d6 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -13,7 +13,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "xorm.io/builder" "xorm.io/xorm" @@ -34,8 +34,8 @@ type IssuesOptions struct { //nolint MilestoneIDs []int64 ProjectID int64 ProjectBoardID int64 - IsClosed util.OptionalBool - IsPull util.OptionalBool + IsClosed optional.Option[bool] + IsPull optional.Option[bool] LabelIDs []int64 IncludedLabelNames []string ExcludedLabelNames []string @@ -46,7 +46,7 @@ type IssuesOptions struct { //nolint UpdatedBeforeUnix int64 // prioritize issues from this repo PriorityRepoID int64 - IsArchived util.OptionalBool + IsArchived optional.Option[bool] Org *organization.Organization // issues permission scope Team *organization.Team // issues permission scope User *user_model.User // issues permission scope @@ -217,8 +217,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { applyRepoConditions(sess, opts) - if !opts.IsClosed.IsNone() { - sess.And("issue.is_closed=?", opts.IsClosed.IsTrue()) + if opts.IsClosed.Has() { + sess.And("issue.is_closed=?", opts.IsClosed.Value()) } if opts.AssigneeID > 0 { @@ -260,21 +260,18 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { applyProjectBoardCondition(sess, opts) - switch opts.IsPull { - case util.OptionalBoolTrue: - sess.And("issue.is_pull=?", true) - case util.OptionalBoolFalse: - sess.And("issue.is_pull=?", false) + if opts.IsPull.Has() { + sess.And("issue.is_pull=?", opts.IsPull.Value()) } - if opts.IsArchived != util.OptionalBoolNone { - sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) + if opts.IsArchived.Has() { + sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.Value()}) } applyLabelsCondition(sess, opts) if opts.User != nil { - sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue())) + sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value())) } return sess diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 99ca19f8044b8..32c5674fc925a 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -8,7 +8,6 @@ import ( "fmt" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/util" "xorm.io/builder" "xorm.io/xorm" @@ -170,11 +169,8 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6 applyReviewedCondition(sess, opts.ReviewedID) } - switch opts.IsPull { - case util.OptionalBoolTrue: - sess.And("issue.is_pull=?", true) - case util.OptionalBoolFalse: - sess.And("issue.is_pull=?", false) + if opts.IsPull.Has() { + sess.And("issue.is_pull=?", opts.IsPull.Value()) } return sess diff --git a/models/issues/label.go b/models/issues/label.go index 527d8d7853289..f6ecc68cd1804 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/label" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -126,7 +127,7 @@ func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) { counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{ RepoIDs: []int64{repoID}, LabelIDs: []int64{labelID}, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }) for _, count := range counts { diff --git a/models/issues/milestone.go b/models/issues/milestone.go index ea52a64c81be8..db0312adf0057 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -302,7 +303,7 @@ func DeleteMilestoneByRepoID(ctx context.Context, repoID, id int64) error { } numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{ RepoID: repo.ID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }) if err != nil { return err diff --git a/models/issues/milestone_list.go b/models/issues/milestone_list.go index a73bf73c17c9f..d1b3f0301b580 100644 --- a/models/issues/milestone_list.go +++ b/models/issues/milestone_list.go @@ -8,7 +8,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "xorm.io/builder" ) @@ -28,7 +28,7 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 { type FindMilestoneOptions struct { db.ListOptions RepoID int64 - IsClosed util.OptionalBool + IsClosed optional.Option[bool] Name string SortType string RepoCond builder.Cond @@ -40,8 +40,8 @@ func (opts FindMilestoneOptions) ToConds() builder.Cond { if opts.RepoID != 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if opts.IsClosed != util.OptionalBoolNone { - cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.IsTrue()}) + if opts.IsClosed.Has() { + cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()}) } if opts.RepoCond != nil && opts.RepoCond.IsValid() { cond = cond.And(builder.In("repo_id", builder.Select("id").From("repository").Where(opts.RepoCond))) diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go index 7477af92c8c49..e5f6f15ca2a29 100644 --- a/models/issues/milestone_test.go +++ b/models/issues/milestone_test.go @@ -11,10 +11,10 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -39,10 +39,10 @@ func TestGetMilestoneByRepoID(t *testing.T) { func TestGetMilestonesByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) test := func(repoID int64, state api.StateType) { - var isClosed util.OptionalBool + var isClosed optional.Option[bool] switch state { case api.StateClosed, api.StateOpen: - isClosed = util.OptionalBoolOf(state == api.StateClosed) + isClosed = optional.Some(state == api.StateClosed) } repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ @@ -84,7 +84,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ RepoID: unittest.NonexistentID, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }) assert.NoError(t, err) assert.Len(t, milestones, 0) @@ -101,7 +101,7 @@ func TestGetMilestones(t *testing.T) { PageSize: setting.UI.IssuePagingNum, }, RepoID: repo.ID, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), SortType: sortType, }) assert.NoError(t, err) @@ -118,7 +118,7 @@ func TestGetMilestones(t *testing.T) { PageSize: setting.UI.IssuePagingNum, }, RepoID: repo.ID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), Name: "", SortType: sortType, }) @@ -178,7 +178,7 @@ func TestCountRepoClosedMilestones(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ RepoID: repoID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }) assert.NoError(t, err) assert.EqualValues(t, repo.NumClosedMilestones, count) @@ -189,7 +189,7 @@ func TestCountRepoClosedMilestones(t *testing.T) { count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ RepoID: unittest.NonexistentID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }) assert.NoError(t, err) assert.EqualValues(t, 0, count) @@ -206,7 +206,7 @@ func TestCountMilestonesByRepoIDs(t *testing.T) { openCounts, err := issues_model.CountMilestonesMap(db.DefaultContext, issues_model.FindMilestoneOptions{ RepoIDs: []int64{1, 2}, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }) assert.NoError(t, err) assert.EqualValues(t, repo1OpenCount, openCounts[1]) @@ -215,7 +215,7 @@ func TestCountMilestonesByRepoIDs(t *testing.T) { closedCounts, err := issues_model.CountMilestonesMap(db.DefaultContext, issues_model.FindMilestoneOptions{ RepoIDs: []int64{1, 2}, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }) assert.NoError(t, err) assert.EqualValues(t, repo1ClosedCount, closedCounts[1]) @@ -234,7 +234,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { PageSize: setting.UI.IssuePagingNum, }, RepoIDs: []int64{repo1.ID, repo2.ID}, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), SortType: sortType, }) assert.NoError(t, err) @@ -252,7 +252,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { PageSize: setting.UI.IssuePagingNum, }, RepoIDs: []int64{repo1.ID, repo2.ID}, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), SortType: sortType, }) assert.NoError(t, err) diff --git a/models/issues/review_list.go b/models/issues/review_list.go index 282f18b4f7f65..ec6cb07988658 100644 --- a/models/issues/review_list.go +++ b/models/issues/review_list.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "xorm.io/builder" ) @@ -68,7 +68,7 @@ type FindReviewOptions struct { IssueID int64 ReviewerID int64 OfficialOnly bool - Dismissed util.OptionalBool + Dismissed optional.Option[bool] } func (opts *FindReviewOptions) toCond() builder.Cond { @@ -85,8 +85,8 @@ func (opts *FindReviewOptions) toCond() builder.Cond { if opts.OfficialOnly { cond = cond.And(builder.Eq{"official": true}) } - if !opts.Dismissed.IsNone() { - cond = cond.And(builder.Eq{"dismissed": opts.Dismissed.IsTrue()}) + if opts.Dismissed.Has() { + cond = cond.And(builder.Eq{"dismissed": opts.Dismissed.Value()}) } return cond } diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index 91c4832e497f9..4063ca043bd3d 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -340,7 +341,7 @@ func GetTrackedTimeByID(ctx context.Context, id int64) (*TrackedTime, error) { } // GetIssueTotalTrackedTime returns the total tracked time for issues by given conditions. -func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool) (int64, error) { +func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed optional.Option[bool]) (int64, error) { if len(opts.IssueIDs) <= MaxQueryParameters { return getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs) } @@ -363,7 +364,7 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed return accum, nil } -func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool, issueIDs []int64) (int64, error) { +func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed optional.Option[bool], issueIDs []int64) (int64, error) { sumSession := func(opts *IssuesOptions, issueIDs []int64) *xorm.Session { sess := db.GetEngine(ctx). Table("tracked_time"). @@ -378,8 +379,8 @@ func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isC } session := sumSession(opts, issueIDs) - if !isClosed.IsNone() { - session = session.And("issue.is_closed = ?", isClosed.IsTrue()) + if isClosed.Has() { + session = session.And("issue.is_closed = ?", isClosed.Value()) } return session.SumInt(new(trackedTime), "tracked_time.time") } diff --git a/models/issues/tracked_time_test.go b/models/issues/tracked_time_test.go index 9beb862ffbca2..d82bff967a980 100644 --- a/models/issues/tracked_time_test.go +++ b/models/issues/tracked_time_test.go @@ -11,7 +11,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" ) @@ -120,15 +120,15 @@ func TestTotalTimesForEachUser(t *testing.T) { func TestGetIssueTotalTrackedTime(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolFalse) + ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, optional.Some(false)) assert.NoError(t, err) assert.EqualValues(t, 3682, ttt) - ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolTrue) + ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, optional.Some(true)) assert.NoError(t, err) assert.EqualValues(t, 0, ttt) - ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolNone) + ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, optional.None[bool]()) assert.NoError(t, err) assert.EqualValues(t, 3682, ttt) } diff --git a/models/packages/nuget/search.go b/models/packages/nuget/search.go index 53cdf2d4ad89e..7a505ff08f3e5 100644 --- a/models/packages/nuget/search.go +++ b/models/packages/nuget/search.go @@ -55,7 +55,7 @@ func CountPackages(ctx context.Context, opts *packages_model.PackageSearchOption func toConds(opts *packages_model.PackageSearchOptions) builder.Cond { var cond builder.Cond = builder.Eq{ - "package.is_internal": opts.IsInternal.IsTrue(), + "package.is_internal": opts.IsInternal.Value(), "package.owner_id": opts.OwnerID, "package.type": packages_model.TypeNuGet, } diff --git a/models/packages/package_version.go b/models/packages/package_version.go index 8fc475691bd0c..505dbaa0a576a 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -105,7 +106,7 @@ func getVersionByNameAndVersion(ctx context.Context, ownerID int64, packageType ExactMatch: true, Value: version, }, - IsInternal: util.OptionalBoolOf(isInternal), + IsInternal: optional.Some(isInternal), Paginator: db.NewAbsoluteListOptions(0, 1), }) if err != nil { @@ -122,7 +123,7 @@ func GetVersionsByPackageType(ctx context.Context, ownerID int64, packageType Ty pvs, _, err := SearchVersions(ctx, &PackageSearchOptions{ OwnerID: ownerID, Type: packageType, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) return pvs, err } @@ -136,7 +137,7 @@ func GetVersionsByPackageName(ctx context.Context, ownerID int64, packageType Ty ExactMatch: true, Value: name, }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) return pvs, err } @@ -182,18 +183,18 @@ type PackageSearchOptions struct { Name SearchValue // only results with the specific name are found Version SearchValue // only results with the specific version are found Properties map[string]string // only results are found which contain all listed version properties with the specific value - IsInternal util.OptionalBool - HasFileWithName string // only results are found which are associated with a file with the specific name - HasFiles util.OptionalBool // only results are found which have associated files + IsInternal optional.Option[bool] + HasFileWithName string // only results are found which are associated with a file with the specific name + HasFiles optional.Option[bool] // only results are found which have associated files Sort VersionSort db.Paginator } func (opts *PackageSearchOptions) ToConds() builder.Cond { cond := builder.NewCond() - if !opts.IsInternal.IsNone() { + if opts.IsInternal.Has() { cond = builder.Eq{ - "package_version.is_internal": opts.IsInternal.IsTrue(), + "package_version.is_internal": opts.IsInternal.Value(), } } @@ -250,10 +251,10 @@ func (opts *PackageSearchOptions) ToConds() builder.Cond { cond = cond.And(builder.Exists(builder.Select("package_file.id").From("package_file").Where(fileCond))) } - if !opts.HasFiles.IsNone() { + if opts.HasFiles.Has() { filesCond := builder.Exists(builder.Select("package_file.id").From("package_file").Where(builder.Expr("package_file.version_id = package_version.id"))) - if opts.HasFiles.IsFalse() { + if !opts.HasFiles.Value() { filesCond = builder.Not{filesCond} } @@ -307,8 +308,8 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P And(builder.Expr("pv2.id IS NULL")) joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))") - if !opts.IsInternal.IsNone() { - joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.IsTrue()}) + if opts.IsInternal.Has() { + joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()}) } sess := db.GetEngine(ctx). diff --git a/models/project/project.go b/models/project/project.go index 42b06e58c93f1..8f9ee2a99e9c7 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -12,6 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -196,7 +197,7 @@ type SearchOptions struct { db.ListOptions OwnerID int64 RepoID int64 - IsClosed util.OptionalBool + IsClosed optional.Option[bool] OrderBy db.SearchOrderBy Type Type Title string @@ -207,11 +208,8 @@ func (opts SearchOptions) ToConds() builder.Cond { if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - switch opts.IsClosed { - case util.OptionalBoolTrue: - cond = cond.And(builder.Eq{"is_closed": true}) - case util.OptionalBoolFalse: - cond = cond.And(builder.Eq{"is_closed": false}) + if opts.IsClosed.Has() { + cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()}) } if opts.Type > 0 { diff --git a/models/repo/repo.go b/models/repo/repo.go index 13493ba6e80e6..5ce3ecb58aebc 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -840,7 +841,7 @@ func (repo *Repository) TemplateRepo(ctx context.Context) *Repository { type CountRepositoryOptions struct { OwnerID int64 - Private util.OptionalBool + Private optional.Option[bool] } // CountRepositories returns number of repositories. @@ -852,8 +853,8 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64, if opts.OwnerID > 0 { sess.And("owner_id = ?", opts.OwnerID) } - if !opts.Private.IsNone() { - sess.And("is_private=?", opts.Private.IsTrue()) + if opts.Private.Has() { + sess.And("is_private=?", opts.Private.Value()) } count, err := sess.Count(new(Repository)) diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go index ca9209d75153d..1a870224bf5d1 100644 --- a/models/repo/repo_test.go +++ b/models/repo/repo_test.go @@ -12,17 +12,17 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) var ( countRepospts = repo_model.CountRepositoryOptions{OwnerID: 10} - countReposptsPublic = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolFalse} - countReposptsPrivate = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolTrue} + countReposptsPublic = repo_model.CountRepositoryOptions{OwnerID: 10, Private: optional.Some(false)} + countReposptsPrivate = repo_model.CountRepositoryOptions{OwnerID: 10, Private: optional.Some(true)} ) func TestGetRepositoryCount(t *testing.T) { diff --git a/models/user/user.go b/models/user/user.go index e92bbd4d0b09e..a898e71a2d710 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -715,7 +715,7 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve // IsLastAdminUser check whether user is the last admin func IsLastAdminUser(ctx context.Context, user *User) bool { - if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 { + if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 { return true } return false @@ -724,7 +724,7 @@ func IsLastAdminUser(ctx context.Context, user *User) bool { // CountUserFilter represent optional filters for CountUsers type CountUserFilter struct { LastLoginSince *int64 - IsAdmin util.OptionalBool + IsAdmin optional.Option[bool] } // CountUsers returns number of users. @@ -742,8 +742,8 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 { cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince}) } - if !opts.IsAdmin.IsNone() { - cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()}) + if opts.IsAdmin.Has() { + cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()}) } } diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 4a84a3d411169..894357e36a12d 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -433,7 +434,7 @@ type ListWebhookOptions struct { db.ListOptions RepoID int64 OwnerID int64 - IsActive util.OptionalBool + IsActive optional.Option[bool] } func (opts ListWebhookOptions) ToConds() builder.Cond { @@ -444,8 +445,8 @@ func (opts ListWebhookOptions) ToConds() builder.Cond { if opts.OwnerID != 0 { cond = cond.And(builder.Eq{"webhook.owner_id": opts.OwnerID}) } - if !opts.IsActive.IsNone() { - cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()}) + if opts.IsActive.Has() { + cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.Value()}) } return cond } diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go index 2e89f9547bba2..a2a9ee321ae92 100644 --- a/models/webhook/webhook_system.go +++ b/models/webhook/webhook_system.go @@ -8,7 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" ) // GetDefaultWebhooks returns all admin-default webhooks. @@ -34,15 +34,15 @@ func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) } // GetSystemWebhooks returns all admin system webhooks. -func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webhook, error) { +func GetSystemWebhooks(ctx context.Context, isActive optional.Option[bool]) ([]*Webhook, error) { webhooks := make([]*Webhook, 0, 5) - if isActive.IsNone() { + if !isActive.Has() { return webhooks, db.GetEngine(ctx). Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, true). Find(&webhooks) } return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()). + Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.Value()). Find(&webhooks) } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 694fd7a873f37..c70c8e99fcc7d 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -11,9 +11,9 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" "github.com/stretchr/testify/assert" @@ -123,7 +123,7 @@ func TestGetWebhookByOwnerID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(1), hooks[0].ID) @@ -143,7 +143,7 @@ func TestGetWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 7c82cfbb792ef..6a5d65cb665d0 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -175,11 +175,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( queries = append(queries, bleve.NewDisjunctionQuery(repoQueries...)) } - if !options.IsPull.IsNone() { - queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.IsTrue(), "is_pull")) + if options.IsPull.Has() { + queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull")) } - if !options.IsClosed.IsNone() { - queries = append(queries, inner_bleve.BoolFieldQuery(options.IsClosed.IsTrue(), "is_closed")) + if options.IsClosed.Has() { + queries = append(queries, inner_bleve.BoolFieldQuery(options.IsClosed.Value(), "is_closed")) } if options.NoLabelOnly { diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 5406715bbcfb2..69146573a8682 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -11,6 +11,7 @@ import ( issue_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/indexer/issues/internal" + "code.gitea.io/gitea/modules/optional" ) func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) { @@ -75,7 +76,7 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m UpdatedAfterUnix: convertInt64(options.UpdatedAfterUnix), UpdatedBeforeUnix: convertInt64(options.UpdatedBeforeUnix), PriorityRepoID: 0, - IsArchived: 0, + IsArchived: optional.None[bool](), Org: nil, Team: nil, User: nil, diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index d059f76b3288a..3acd3ade71528 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -153,11 +153,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.Must(q) } - if !options.IsPull.IsNone() { - query.Must(elastic.NewTermQuery("is_pull", options.IsPull.IsTrue())) + if options.IsPull.Has() { + query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value())) } - if !options.IsClosed.IsNone() { - query.Must(elastic.NewTermQuery("is_closed", options.IsClosed.IsTrue())) + if options.IsClosed.Has() { + query.Must(elastic.NewTermQuery("is_closed", options.IsClosed.Value())) } if options.NoLabelOnly { diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 3b96686d98ef0..10ffa7cbe6b63 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/indexer/issues/internal" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" _ "code.gitea.io/gitea/models" _ "code.gitea.io/gitea/models/actions" @@ -210,13 +210,13 @@ func searchIssueIsPull(t *testing.T) { }{ { SearchOptions{ - IsPull: util.OptionalBoolFalse, + IsPull: optional.Some(false), }, []int64{17, 16, 15, 14, 13, 6, 5, 18, 10, 7, 4, 1}, }, { SearchOptions{ - IsPull: util.OptionalBoolTrue, + IsPull: optional.Some(true), }, []int64{22, 21, 12, 11, 20, 19, 9, 8, 3, 2}, }, @@ -237,13 +237,13 @@ func searchIssueIsClosed(t *testing.T) { }{ { SearchOptions{ - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }, []int64{22, 21, 17, 16, 15, 14, 13, 12, 11, 20, 6, 19, 18, 10, 7, 9, 8, 3, 2, 1}, }, { SearchOptions{ - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }, []int64{5, 4}, }, diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 031745dd2fcc1..947335d8ce9cb 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -5,8 +5,8 @@ package internal import ( "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" ) // IndexerData data stored in the issue indexer @@ -77,8 +77,8 @@ type SearchOptions struct { RepoIDs []int64 // repository IDs which the issues belong to AllPublic bool // if include all public repositories - IsPull util.OptionalBool // if the issues is a pull request - IsClosed util.OptionalBool // if the issues is closed + IsPull optional.Option[bool] // if the issues is a pull request + IsClosed optional.Option[bool] // if the issues is closed IncludedLabelIDs []int64 // labels the issues have ExcludedLabelIDs []int64 // labels the issues don't have diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 06fddeb65b627..67244715394dd 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -16,8 +16,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/indexer/issues/internal" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -166,7 +166,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - IsPull: util.OptionalBoolFalse, + IsPull: optional.Some(false), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -182,7 +182,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - IsPull: util.OptionalBoolTrue, + IsPull: optional.Some(true), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -198,7 +198,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -214,7 +214,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index ab8dcd0af45a8..325883196bb27 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -131,11 +131,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.And(q) } - if !options.IsPull.IsNone() { - query.And(inner_meilisearch.NewFilterEq("is_pull", options.IsPull.IsTrue())) + if options.IsPull.Has() { + query.And(inner_meilisearch.NewFilterEq("is_pull", options.IsPull.Value())) } - if !options.IsClosed.IsNone() { - query.And(inner_meilisearch.NewFilterEq("is_closed", options.IsClosed.IsTrue())) + if options.IsClosed.Has() { + query.And(inner_meilisearch.NewFilterEq("is_closed", options.IsClosed.Value())) } if options.NoLabelOnly { diff --git a/modules/util/util.go b/modules/util/util.go index 615f654e47523..5c751581965cb 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -17,57 +17,6 @@ import ( "golang.org/x/text/language" ) -// OptionalBool a boolean that can be "null" -type OptionalBool byte - -const ( - // OptionalBoolNone a "null" boolean value - OptionalBoolNone OptionalBool = iota - // OptionalBoolTrue a "true" boolean value - OptionalBoolTrue - // OptionalBoolFalse a "false" boolean value - OptionalBoolFalse -) - -// IsTrue return true if equal to OptionalBoolTrue -func (o OptionalBool) IsTrue() bool { - return o == OptionalBoolTrue -} - -// IsFalse return true if equal to OptionalBoolFalse -func (o OptionalBool) IsFalse() bool { - return o == OptionalBoolFalse -} - -// IsNone return true if equal to OptionalBoolNone -func (o OptionalBool) IsNone() bool { - return o == OptionalBoolNone -} - -// ToGeneric converts OptionalBool to optional.Option[bool] -func (o OptionalBool) ToGeneric() optional.Option[bool] { - if o.IsNone() { - return optional.None[bool]() - } - return optional.Some[bool](o.IsTrue()) -} - -// OptionalBoolFromGeneric converts optional.Option[bool] to OptionalBool -func OptionalBoolFromGeneric(o optional.Option[bool]) OptionalBool { - if o.Has() { - return OptionalBoolOf(o.Value()) - } - return OptionalBoolNone -} - -// OptionalBoolOf get the corresponding OptionalBool of a bool -func OptionalBoolOf(b bool) OptionalBool { - if b { - return OptionalBoolTrue - } - return OptionalBoolFalse -} - // OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool func OptionalBoolParse(s string) optional.Option[bool] { v, e := strconv.ParseBool(s) diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go index d01a13d78f67c..140e532efd822 100644 --- a/routers/api/packages/cargo/cargo.go +++ b/routers/api/packages/cargo/cargo.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" cargo_module "code.gitea.io/gitea/modules/packages/cargo" "code.gitea.io/gitea/modules/setting" @@ -110,7 +111,7 @@ func SearchPackages(ctx *context.Context) { OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeCargo, Name: packages_model.SearchValue{Value: ctx.FormTrim("q")}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: &paginator, }, ) diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go index 720fce0a2a5d7..b49f4e9d0aa8e 100644 --- a/routers/api/packages/chef/chef.go +++ b/routers/api/packages/chef/chef.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" chef_module "code.gitea.io/gitea/modules/packages/chef" "code.gitea.io/gitea/modules/setting" @@ -40,7 +41,7 @@ func PackagesUniverse(ctx *context.Context) { pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeChef, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) @@ -85,7 +86,7 @@ func EnumeratePackages(ctx *context.Context) { OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeChef, Name: packages_model.SearchValue{Value: ctx.FormTrim("q")}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: db.NewAbsoluteListOptions( ctx.FormInt("start"), ctx.FormInt("items"), diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go index 346408d2611ba..a045da40def92 100644 --- a/routers/api/packages/composer/composer.go +++ b/routers/api/packages/composer/composer.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" composer_module "code.gitea.io/gitea/modules/packages/composer" "code.gitea.io/gitea/modules/setting" @@ -66,7 +67,7 @@ func SearchPackages(ctx *context.Context) { OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeComposer, Name: packages_model.SearchValue{Value: ctx.FormTrim("q")}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: &paginator, } if ctx.FormTrim("type") != "" { diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go index 9eb515d9a1bef..d658066bb4371 100644 --- a/routers/api/packages/goproxy/goproxy.go +++ b/routers/api/packages/goproxy/goproxy.go @@ -12,6 +12,7 @@ import ( "time" packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" goproxy_module "code.gitea.io/gitea/modules/packages/goproxy" "code.gitea.io/gitea/modules/util" @@ -129,7 +130,7 @@ func resolvePackage(ctx *context.Context, ownerID int64, name, version string) ( Value: name, ExactMatch: true, }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Sort: packages_model.SortCreatedDesc, }) if err != nil { diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go index e7a346d9ca7be..efdb83ec0e776 100644 --- a/routers/api/packages/helm/helm.go +++ b/routers/api/packages/helm/helm.go @@ -15,6 +15,7 @@ import ( packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" helm_module "code.gitea.io/gitea/modules/packages/helm" "code.gitea.io/gitea/modules/setting" @@ -42,7 +43,7 @@ func Index(ctx *context.Context) { pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeHelm, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) @@ -110,7 +111,7 @@ func DownloadPackageFile(ctx *context.Context) { Value: ctx.Params("package"), }, HasFileWithName: filename, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 72b4305928111..84acfffae25e2 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -17,6 +17,7 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" npm_module "code.gitea.io/gitea/modules/packages/npm" "code.gitea.io/gitea/modules/setting" @@ -120,7 +121,7 @@ func DownloadPackageFileByName(ctx *context.Context) { Value: packageNameFromParams(ctx), }, HasFileWithName: filename, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) @@ -395,7 +396,7 @@ func setPackageTag(ctx std_ctx.Context, tag string, pv *packages_model.PackageVe Properties: map[string]string{ npm_module.TagProperty: tag, }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { return err @@ -431,7 +432,7 @@ func PackageSearch(ctx *context.Context) { pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeNpm, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Name: packages_model.SearchValue{ ExactMatch: false, Value: ctx.FormTrim("text"), diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index a0273aad5ad33..c28bc6c9d9233 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -18,6 +18,7 @@ import ( packages_model "code.gitea.io/gitea/models/packages" nuget_model "code.gitea.io/gitea/models/packages/nuget" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" nuget_module "code.gitea.io/gitea/modules/packages/nuget" "code.gitea.io/gitea/modules/setting" @@ -122,7 +123,7 @@ func SearchServiceV2(ctx *context.Context) { Name: packages_model.SearchValue{ Value: getSearchTerm(ctx), }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: paginator, }) if err != nil { @@ -172,7 +173,7 @@ func SearchServiceV2Count(ctx *context.Context) { Name: packages_model.SearchValue{ Value: getSearchTerm(ctx), }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) @@ -187,7 +188,7 @@ func SearchServiceV3(ctx *context.Context) { pvs, count, err := nuget_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Name: packages_model.SearchValue{Value: ctx.FormTrim("q")}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: db.NewAbsoluteListOptions( ctx.FormInt("skip"), ctx.FormInt("take"), @@ -313,7 +314,7 @@ func EnumeratePackageVersionsV2(ctx *context.Context) { ExactMatch: true, Value: packageName, }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: paginator, }) if err != nil { @@ -358,7 +359,7 @@ func EnumeratePackageVersionsV2Count(ctx *context.Context) { ExactMatch: true, Value: strings.Trim(ctx.FormTrim("id"), "'"), }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index 5d05b6d524d51..d2fbcd01f06bf 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -13,6 +13,7 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" rubygems_module "code.gitea.io/gitea/modules/packages/rubygems" "code.gitea.io/gitea/modules/util" @@ -43,7 +44,7 @@ func EnumeratePackagesLatest(ctx *context.Context) { pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeRubyGems, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) @@ -304,7 +305,7 @@ func getVersionsByFilename(ctx *context.Context, filename string) ([]*packages_m OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeRubyGems, HasFileWithName: filename, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) return pvs, err } diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 1fc8baeaacd6a..a9da3ea9c2d86 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -15,6 +15,7 @@ import ( packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" swift_module "code.gitea.io/gitea/modules/packages/swift" "code.gitea.io/gitea/modules/setting" @@ -433,7 +434,7 @@ func LookupPackageIdentifiers(ctx *context.Context) { Properties: map[string]string{ swift_module.PropertyRepositoryURL: url, }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index 2217d002a0206..4c168b55bf183 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -8,6 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -37,7 +38,7 @@ func ListHooks(ctx *context.APIContext) { // "200": // "$ref": "#/responses/HookList" - sysHooks, err := webhook.GetSystemWebhooks(ctx, util.OptionalBoolNone) + sysHooks, err := webhook.GetSystemWebhooks(ctx, optional.None[bool]()) if err != nil { ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) return diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go index 3be31b13ae100..b38aa131676e1 100644 --- a/routers/api/v1/packages/package.go +++ b/routers/api/v1/packages/package.go @@ -7,8 +7,8 @@ import ( "net/http" "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" @@ -60,7 +60,7 @@ func ListPackages(ctx *context.APIContext) { OwnerID: ctx.Package.Owner.ID, Type: packages.Type(packageType), Name: packages.SearchValue{Value: query}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: &listOptions, }) if err != nil { diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 227e0e725cfb9..1b2ecd474be1c 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -23,7 +23,6 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -123,14 +122,14 @@ func SearchIssues(ctx *context.APIContext) { return } - var isClosed util.OptionalBool + var isClosed optional.Option[bool] switch ctx.FormString("state") { case "closed": - isClosed = util.OptionalBoolTrue + isClosed = optional.Some(true) case "all": - isClosed = util.OptionalBoolNone + isClosed = optional.None[bool]() default: - isClosed = util.OptionalBoolFalse + isClosed = optional.Some(false) } var ( @@ -205,14 +204,14 @@ func SearchIssues(ctx *context.APIContext) { keyword = "" } - var isPull util.OptionalBool + var isPull optional.Option[bool] switch ctx.FormString("type") { case "pulls": - isPull = util.OptionalBoolTrue + isPull = optional.Some(true) case "issues": - isPull = util.OptionalBoolFalse + isPull = optional.Some(false) default: - isPull = util.OptionalBoolNone + isPull = optional.None[bool]() } var includedAnyLabels []int64 @@ -397,14 +396,14 @@ func ListIssues(ctx *context.APIContext) { return } - var isClosed util.OptionalBool + var isClosed optional.Option[bool] switch ctx.FormString("state") { case "closed": - isClosed = util.OptionalBoolTrue + isClosed = optional.Some(true) case "all": - isClosed = util.OptionalBoolNone + isClosed = optional.None[bool]() default: - isClosed = util.OptionalBoolFalse + isClosed = optional.Some(false) } keyword := ctx.FormTrim("q") @@ -453,31 +452,29 @@ func ListIssues(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - var isPull util.OptionalBool + isPull := optional.None[bool]() switch ctx.FormString("type") { case "pulls": - isPull = util.OptionalBoolTrue + isPull = optional.Some(true) case "issues": - isPull = util.OptionalBoolFalse - default: - isPull = util.OptionalBoolNone + isPull = optional.Some(false) } - if isPull != util.OptionalBoolNone && !ctx.Repo.CanReadIssuesOrPulls(isPull.IsTrue()) { + if isPull.Has() && !ctx.Repo.CanReadIssuesOrPulls(isPull.Value()) { ctx.NotFound() return } - if isPull == util.OptionalBoolNone { + if !isPull.Has() { canReadIssues := ctx.Repo.CanRead(unit.TypeIssues) canReadPulls := ctx.Repo.CanRead(unit.TypePullRequests) if !canReadIssues && !canReadPulls { ctx.NotFound() return } else if !canReadIssues { - isPull = util.OptionalBoolTrue + isPull = optional.Some(true) } else if !canReadPulls { - isPull = util.OptionalBoolFalse + isPull = optional.Some(false) } } diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 763419b7a2520..6209e960af450 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -14,8 +14,8 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -278,15 +278,15 @@ func ListRepoIssueComments(ctx *context.APIContext) { return } - var isPull util.OptionalBool + var isPull optional.Option[bool] canReadIssue := ctx.Repo.CanRead(unit.TypeIssues) canReadPull := ctx.Repo.CanRead(unit.TypePullRequests) if canReadIssue && canReadPull { - isPull = util.OptionalBoolNone + isPull = optional.None[bool]() } else if canReadIssue { - isPull = util.OptionalBoolFalse + isPull = optional.Some(false) } else if canReadPull { - isPull = util.OptionalBoolTrue + isPull = optional.Some(true) } else { ctx.NotFound() return diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index d4c828fe8bf1d..b9534016e4b87 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -11,9 +11,9 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -61,10 +61,10 @@ func ListMilestones(ctx *context.APIContext) { // "$ref": "#/responses/notFound" state := api.StateType(ctx.FormString("state")) - var isClosed util.OptionalBool + var isClosed optional.Option[bool] switch state { case api.StateClosed, api.StateOpen: - isClosed = util.OptionalBoolOf(state == api.StateClosed) + isClosed = optional.Some(state == api.StateClosed) } milestones, total, err := db.FindAndCount[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go index 8d4c66fdb2bef..8d59fbb858e96 100644 --- a/routers/web/admin/hooks.go +++ b/routers/web/admin/hooks.go @@ -8,8 +8,8 @@ import ( "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) @@ -35,7 +35,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { sys["Title"] = ctx.Tr("admin.systemhooks") sys["Description"] = ctx.Tr("admin.systemhooks.desc") - sys["Webhooks"], err = webhook.GetSystemWebhooks(ctx, util.OptionalBoolNone) + sys["Webhooks"], err = webhook.GetSystemWebhooks(ctx, optional.None[bool]()) sys["BaseLink"] = setting.AppSubURL + "/admin/hooks" sys["BaseLinkNew"] = setting.AppSubURL + "/admin/system-hooks" if err != nil { diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index 7c16b69a85cf7..39f064a1be637 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -11,8 +11,8 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup" @@ -36,7 +36,7 @@ func Packages(ctx *context.Context) { Type: packages_model.Type(packageType), Name: packages_model.SearchValue{Value: query}, Sort: sort, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Paginator: &db.ListOptions{ PageSize: setting.UI.PackagesPagingNum, Page: page, diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index bbdbc820d7829..ca47175401447 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -96,7 +96,7 @@ func NewUser(ctx *context.Context) { ctx.Data["login_type"] = "0-0" sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), }) if err != nil { ctx.ServerError("auth.Sources", err) @@ -117,7 +117,7 @@ func NewUserPost(ctx *context.Context) { ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), }) if err != nil { ctx.ServerError("auth.Sources", err) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 7704a110a67b5..04e410543d2ce 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -163,7 +163,7 @@ func SignIn(ctx *context.Context) { return } - oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { ctx.ServerError("UserSignIn", err) return @@ -186,7 +186,7 @@ func SignIn(ctx *context.Context) { func SignInPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("sign_in") - oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { ctx.ServerError("UserSignIn", err) return @@ -410,7 +410,7 @@ func SignUp(ctx *context.Context) { ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" - oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { ctx.ServerError("UserSignUp", err) return @@ -439,7 +439,7 @@ func SignUpPost(ctx *context.Context) { ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" - oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { ctx.ServerError("UserSignUp", err) return diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 82cd91997a5a5..ad8bb90d9ea82 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -18,9 +18,9 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" shared_user "code.gitea.io/gitea/routers/web/shared/user" "code.gitea.io/gitea/services/context" @@ -67,7 +67,7 @@ func Projects(ctx *context.Context) { PageSize: setting.UI.IssuePagingNum, }, OwnerID: ctx.ContextUser.ID, - IsClosed: util.OptionalBoolOf(isShowClosed), + IsClosed: optional.Some(isShowClosed), OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: projectType, Title: keyword, @@ -79,7 +79,7 @@ func Projects(ctx *context.Context) { opTotal, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, - IsClosed: util.OptionalBoolOf(!isShowClosed), + IsClosed: optional.Some(!isShowClosed), Type: projectType, }) if err != nil { @@ -388,7 +388,7 @@ func ViewProject(ctx *context.Context) { if len(referencedIDs) > 0 { if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ IssueIDs: referencedIDs, - IsPull: util.OptionalBoolTrue, + IsPull: optional.Some(true), }); err == nil { linkedPrsMap[issue.ID] = linkedPrs } diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index e784912377936..f27329aa0f02c 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -16,8 +16,8 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/repo" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" @@ -78,7 +78,7 @@ func List(ctx *context.Context) { // Get all runner labels runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{ RepoID: ctx.Repo.Repository.ID, - IsOnline: util.OptionalBoolTrue, + IsOnline: optional.Some(true), WithAvailable: true, }) if err != nil { diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 53bd599b0dde9..ae51f0596b2de 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -53,7 +54,7 @@ func Branches(ctx *context.Context) { kw := ctx.FormString("q") - defaultBranch, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, util.OptionalBoolNone, kw, page, pageSize) + defaultBranch, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, optional.None[bool](), kw, page, pageSize) if err != nil { ctx.ServerError("LoadBranches", err) return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index cec0f87471b1e..1abd5e2ba5a3d 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -140,7 +140,7 @@ func MustAllowPulls(ctx *context.Context) { } } -func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption util.OptionalBool) { +func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption optional.Option[bool]) { var err error viewType := ctx.FormString("type") sortType := ctx.FormString("sort") @@ -241,18 +241,18 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } } - var isShowClosed util.OptionalBool + var isShowClosed optional.Option[bool] switch ctx.FormString("state") { case "closed": - isShowClosed = util.OptionalBoolTrue + isShowClosed = optional.Some(true) case "all": - isShowClosed = util.OptionalBoolNone + isShowClosed = optional.None[bool]() default: - isShowClosed = util.OptionalBoolFalse + isShowClosed = optional.Some(false) } // if there are closed issues and no open issues, default to showing all issues if len(ctx.FormString("state")) == 0 && issueStats.OpenCount == 0 && issueStats.ClosedCount != 0 { - isShowClosed = util.OptionalBoolNone + isShowClosed = optional.None[bool]() } if repo.IsTimetrackerEnabled(ctx) { @@ -272,10 +272,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } var total int - switch isShowClosed { - case util.OptionalBoolTrue: + switch { + case isShowClosed.Value(): total = int(issueStats.ClosedCount) - case util.OptionalBoolNone: + case !isShowClosed.Has(): total = int(issueStats.OpenCount + issueStats.ClosedCount) default: total = int(issueStats.OpenCount) @@ -431,7 +431,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti return } - pinned, err := issues_model.GetPinnedIssues(ctx, repo.ID, isPullOption.IsTrue()) + pinned, err := issues_model.GetPinnedIssues(ctx, repo.ID, isPullOption.Value()) if err != nil { ctx.ServerError("GetPinnedIssues", err) return @@ -461,10 +461,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ctx.Data["AssigneeID"] = assigneeID ctx.Data["PosterID"] = posterID ctx.Data["Keyword"] = keyword - switch isShowClosed { - case util.OptionalBoolTrue: + switch { + case isShowClosed.Value(): ctx.Data["State"] = "closed" - case util.OptionalBoolNone: + case !isShowClosed.Has(): ctx.Data["State"] = "all" default: ctx.Data["State"] = "open" @@ -513,7 +513,7 @@ func Issues(ctx *context.Context) { ctx.Data["NewIssueChooseTemplate"] = issue_service.HasTemplatesOrContactLinks(ctx.Repo.Repository, ctx.Repo.GitRepo) } - issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), util.OptionalBoolOf(isPullList)) + issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), optional.Some(isPullList)) if ctx.Written() { return } @@ -555,7 +555,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R var err error ctx.Data["OpenMilestones"], err = db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ RepoID: repo.ID, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }) if err != nil { ctx.ServerError("GetMilestones", err) @@ -563,7 +563,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R } ctx.Data["ClosedMilestones"], err = db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ RepoID: repo.ID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }) if err != nil { ctx.ServerError("GetMilestones", err) @@ -591,7 +591,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, RepoID: repo.ID, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), Type: project_model.TypeRepository, }) if err != nil { @@ -601,7 +601,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, OwnerID: repo.OwnerID, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), Type: repoOwnerType, }) if err != nil { @@ -614,7 +614,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, RepoID: repo.ID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), Type: project_model.TypeRepository, }) if err != nil { @@ -624,7 +624,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, OwnerID: repo.OwnerID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), Type: repoOwnerType, }) if err != nil { @@ -2502,14 +2502,14 @@ func SearchIssues(ctx *context.Context) { return } - var isClosed util.OptionalBool + var isClosed optional.Option[bool] switch ctx.FormString("state") { case "closed": - isClosed = util.OptionalBoolTrue + isClosed = optional.Some(true) case "all": - isClosed = util.OptionalBoolNone + isClosed = optional.None[bool]() default: - isClosed = util.OptionalBoolFalse + isClosed = optional.Some(false) } var ( @@ -2584,14 +2584,12 @@ func SearchIssues(ctx *context.Context) { keyword = "" } - var isPull util.OptionalBool + isPull := optional.None[bool]() switch ctx.FormString("type") { case "pulls": - isPull = util.OptionalBoolTrue + isPull = optional.Some(true) case "issues": - isPull = util.OptionalBoolFalse - default: - isPull = util.OptionalBoolNone + isPull = optional.Some(false) } var includedAnyLabels []int64 @@ -2726,14 +2724,14 @@ func ListIssues(ctx *context.Context) { return } - var isClosed util.OptionalBool + var isClosed optional.Option[bool] switch ctx.FormString("state") { case "closed": - isClosed = util.OptionalBoolTrue + isClosed = optional.Some(true) case "all": - isClosed = util.OptionalBoolNone + isClosed = optional.None[bool]() default: - isClosed = util.OptionalBoolFalse + isClosed = optional.Some(false) } keyword := ctx.FormTrim("q") @@ -2785,14 +2783,12 @@ func ListIssues(ctx *context.Context) { projectID = &v } - var isPull util.OptionalBool + isPull := optional.None[bool]() switch ctx.FormString("type") { case "pulls": - isPull = util.OptionalBoolTrue + isPull = optional.Some(true) case "issues": - isPull = util.OptionalBoolFalse - default: - isPull = util.OptionalBoolNone + isPull = optional.Some(false) } // FIXME: we should be more efficient here diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index 49ac94aaf1540..c41b844ce4de8 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -14,9 +14,9 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -51,7 +51,7 @@ func Milestones(ctx *context.Context) { PageSize: setting.UI.IssuePagingNum, }, RepoID: ctx.Repo.Repository.ID, - IsClosed: util.OptionalBoolOf(isShowClosed), + IsClosed: optional.Some(isShowClosed), SortType: sortType, Name: keyword, }) @@ -292,7 +292,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) { ctx.Data["Title"] = milestone.Name ctx.Data["Milestone"] = milestone - issues(ctx, milestoneID, projectID, util.OptionalBoolNone) + issues(ctx, milestoneID, projectID, optional.None[bool]()) ret := issue.ParseTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo) ctx.Data["NewIssueChooseTemplate"] = len(ret.IssueTemplates) > 0 diff --git a/routers/web/repo/packages.go b/routers/web/repo/packages.go index 6ed5909dcf6bf..11874ab0d04a6 100644 --- a/routers/web/repo/packages.go +++ b/routers/web/repo/packages.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) @@ -37,7 +37,7 @@ func Packages(ctx *context.Context) { RepoID: ctx.Repo.Repository.ID, Type: packages.Type(packageType), Name: packages.SearchValue{Value: query}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { ctx.ServerError("SearchLatestVersions", err) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 1f9ee727c362e..4c171defbd4ff 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -20,8 +20,8 @@ import ( "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -78,7 +78,7 @@ func Projects(ctx *context.Context) { Page: page, }, RepoID: repo.ID, - IsClosed: util.OptionalBoolOf(isShowClosed), + IsClosed: optional.Some(isShowClosed), OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: project_model.TypeRepository, Title: keyword, @@ -349,7 +349,7 @@ func ViewProject(ctx *context.Context) { if len(referencedIDs) > 0 { if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ IssueIDs: referencedIDs, - IsPull: util.OptionalBoolTrue, + IsPull: optional.Some(true), }); err == nil { linkedPrsMap[issue.ID] = linkedPrs } diff --git a/routers/web/shared/packages/packages.go b/routers/web/shared/packages/packages.go index 1454396f041da..57671ad8f14bc 100644 --- a/routers/web/shared/packages/packages.go +++ b/routers/web/shared/packages/packages.go @@ -13,7 +13,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -157,7 +157,7 @@ func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) { for _, p := range packages { pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ PackageID: p.ID, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Sort: packages_model.SortCreatedDesc, Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200), }) diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 51b04e0613a58..3bc1adae99ec6 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) @@ -131,7 +130,7 @@ func LoadHeaderCount(ctx *context.Context) error { } projectCount, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, - IsClosed: util.OptionalBoolOf(false), + IsClosed: optional.Some(false), Type: projectType, }) if err != nil { diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 6f36806ff7554..caa71152593ef 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -30,7 +30,6 @@ import ( "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "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" @@ -215,7 +214,7 @@ func Milestones(ctx *context.Context) { counts, err := issues_model.CountMilestonesMap(ctx, issues_model.FindMilestoneOptions{ RepoCond: userRepoCond, Name: keyword, - IsClosed: util.OptionalBoolOf(isShowClosed), + IsClosed: optional.Some(isShowClosed), }) if err != nil { ctx.ServerError("CountMilestonesByRepoIDs", err) @@ -228,7 +227,7 @@ func Milestones(ctx *context.Context) { PageSize: setting.UI.IssuePagingNum, }, RepoCond: repoCond, - IsClosed: util.OptionalBoolOf(isShowClosed), + IsClosed: optional.Some(isShowClosed), SortType: sortType, Name: keyword, }) @@ -440,9 +439,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { isPullList := unitType == unit.TypePullRequests opts := &issues_model.IssuesOptions{ - IsPull: util.OptionalBoolOf(isPullList), + IsPull: optional.Some(isPullList), SortType: sortType, - IsArchived: util.OptionalBoolFalse, + IsArchived: optional.Some(false), Org: org, Team: team, User: ctx.Doer, @@ -516,7 +515,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // Educated guess: Do or don't show closed issues. isShowClosed := ctx.FormString("state") == "closed" - opts.IsClosed = util.OptionalBoolOf(isShowClosed) + opts.IsClosed = optional.Some(isShowClosed) // Make sure page number is at least 1. Will be posted to ctx.Data. page := ctx.FormInt("page") @@ -800,12 +799,12 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod case issues_model.FilterModeReviewed: openClosedOpts.ReviewedID = &doerID } - openClosedOpts.IsClosed = util.OptionalBoolFalse + openClosedOpts.IsClosed = optional.Some(false) ret.OpenCount, err = issue_indexer.CountIssues(ctx, openClosedOpts) if err != nil { return nil, err } - openClosedOpts.IsClosed = util.OptionalBoolTrue + openClosedOpts.IsClosed = optional.Some(true) ret.ClosedCount, err = issue_indexer.CountIssues(ctx, openClosedOpts) if err != nil { return nil, err diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 801e1cf95eaf4..09e592d63a941 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -233,26 +233,25 @@ func NotificationSubscriptions(ctx *context.Context) { if !util.SliceContainsString([]string{"all", "open", "closed"}, state, true) { state = "all" } + ctx.Data["State"] = state - var showClosed util.OptionalBool + // default state filter is "all" + showClosed := optional.None[bool]() switch state { - case "all": - showClosed = util.OptionalBoolNone case "closed": - showClosed = util.OptionalBoolTrue + showClosed = optional.Some(true) case "open": - showClosed = util.OptionalBoolFalse + showClosed = optional.Some(false) } - var issueTypeBool util.OptionalBool issueType := ctx.FormString("issueType") + // default issue type is no filter + issueTypeBool := optional.None[bool]() switch issueType { case "issues": - issueTypeBool = util.OptionalBoolFalse + issueTypeBool = optional.Some(false) case "pulls": - issueTypeBool = util.OptionalBoolTrue - default: - issueTypeBool = util.OptionalBoolNone + issueTypeBool = optional.Some(true) } ctx.Data["IssueType"] = issueType diff --git a/routers/web/user/package.go b/routers/web/user/package.go index d03b28309fd8f..4f3de13dfbced 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" alpine_module "code.gitea.io/gitea/modules/packages/alpine" debian_module "code.gitea.io/gitea/modules/packages/debian" rpm_module "code.gitea.io/gitea/modules/packages/rpm" @@ -54,7 +55,7 @@ func ListPackages(ctx *context.Context) { OwnerID: ctx.ContextUser.ID, Type: packages_model.Type(packageType), Name: packages_model.SearchValue{Value: query}, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { ctx.ServerError("SearchLatestVersions", err) @@ -145,7 +146,7 @@ func RedirectToLastVersion(ctx *context.Context) { pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{ PackageID: p.ID, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { ctx.ServerError("GetPackageByName", err) @@ -255,7 +256,7 @@ func ViewPackageVersion(ctx *context.Context) { pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ Paginator: db.NewAbsoluteListOptions(0, 5), PackageID: pd.Package.ID, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) } if err != nil { @@ -359,7 +360,7 @@ func ListPackageVersions(ctx *context.Context) { ExactMatch: false, Value: query, }, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Sort: sort, }) if err != nil { @@ -467,7 +468,7 @@ func PackageSettingsPost(ctx *context.Context) { redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages" // redirect to the package if there are still versions available - if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: util.OptionalBoolFalse}); has { + if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has { redirectURL = ctx.Package.Descriptor.PackageWebLink() } diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index 30611dd9f19c8..8d6859ab87da9 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/context" ) @@ -112,7 +112,7 @@ func loadSecurityData(ctx *context.Context) { ctx.Data["AccountLinks"] = sources authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{ - IsActive: util.OptionalBoolNone, + IsActive: optional.None[bool](), LoginType: auth_model.OAuth2, }) if err != nil { diff --git a/services/auth/signin.go b/services/auth/signin.go index fafe3ef3c67b1..e116a088e09b0 100644 --- a/services/auth/signin.go +++ b/services/auth/signin.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/smtp" @@ -87,7 +87,7 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use } sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), }) if err != nil { return nil, nil, err diff --git a/services/auth/source/oauth2/init.go b/services/auth/source/oauth2/init.go index 3ad6e307f106c..5c2568154863c 100644 --- a/services/auth/source/oauth2/init.go +++ b/services/auth/source/oauth2/init.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/google/uuid" "github.com/gorilla/sessions" @@ -66,7 +66,7 @@ func ResetOAuth2(ctx context.Context) error { // initOAuth2Sources is used to load and register all active OAuth2 providers func initOAuth2Sources(ctx context.Context) error { authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), LoginType: auth.OAuth2, }) if err != nil { diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index f4edb507f2774..ac32647839b36 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -15,8 +15,8 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/markbates/goth" ) @@ -107,7 +107,7 @@ func CreateProviderFromSource(source *auth.Source) (Provider, error) { } // GetOAuth2Providers returns the list of configured OAuth2 providers -func GetOAuth2Providers(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) { +func GetOAuth2Providers(ctx context.Context, isActive optional.Option[bool]) ([]Provider, error) { authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: isActive, LoginType: auth.OAuth2, diff --git a/services/auth/sspi.go b/services/auth/sspi.go index 9108a0a668a42..64a127e97a6c3 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/auth/source/sspi" gitea_context "code.gitea.io/gitea/services/context" @@ -132,7 +131,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, // getConfig retrieves the SSPI configuration from login sources func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) { sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), LoginType: auth.SSPI, }) if err != nil { diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index c8102c6b8b71b..c9b924809819b 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -23,9 +23,9 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -68,14 +68,14 @@ func TestGiteaUploadRepo(t *testing.T) { milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ RepoID: repo.ID, - IsClosed: util.OptionalBoolFalse, + IsClosed: optional.Some(false), }) assert.NoError(t, err) assert.Len(t, milestones, 1) milestones, err = db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ RepoID: repo.ID, - IsClosed: util.OptionalBoolTrue, + IsClosed: optional.Some(true), }) assert.NoError(t, err) assert.Empty(t, milestones) @@ -108,7 +108,7 @@ func TestGiteaUploadRepo(t *testing.T) { issues, err := issues_model.Issues(db.DefaultContext, &issues_model.IssuesOptions{ RepoIDs: []int64{repo.ID}, - IsPull: util.OptionalBoolFalse, + IsPull: optional.Some(false), SortType: "oldest", }) assert.NoError(t, err) diff --git a/services/packages/cleanup/cleanup.go b/services/packages/cleanup/cleanup.go index 0ff8077bc9074..5d5120c6a0a87 100644 --- a/services/packages/cleanup/cleanup.go +++ b/services/packages/cleanup/cleanup.go @@ -12,8 +12,8 @@ import ( packages_model "code.gitea.io/gitea/models/packages" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" - "code.gitea.io/gitea/modules/util" packages_service "code.gitea.io/gitea/services/packages" alpine_service "code.gitea.io/gitea/services/packages/alpine" cargo_service "code.gitea.io/gitea/services/packages/cargo" @@ -60,7 +60,7 @@ func ExecuteCleanupRules(outerCtx context.Context) error { for _, p := range packages { pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ PackageID: p.ID, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), Sort: packages_model.SortCreatedDesc, Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200), }) diff --git a/services/packages/container/cleanup.go b/services/packages/container/cleanup.go index dd3f158dbf9ff..3f5f43bbc0bb1 100644 --- a/services/packages/container/cleanup.go +++ b/services/packages/container/cleanup.go @@ -9,8 +9,8 @@ import ( packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" + "code.gitea.io/gitea/modules/optional" container_module "code.gitea.io/gitea/modules/packages/container" - "code.gitea.io/gitea/modules/util" packages_service "code.gitea.io/gitea/services/packages" digest "github.com/opencontainers/go-digest" @@ -59,8 +59,8 @@ func cleanupExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) e ExactMatch: true, Value: container_model.UploadVersion, }, - IsInternal: util.OptionalBoolTrue, - HasFiles: util.OptionalBoolFalse, + IsInternal: optional.Some(true), + HasFiles: optional.Some(false), }) if err != nil { return err diff --git a/services/packages/packages.go b/services/packages/packages.go index 56d5cc04de2df..64b1ddd869632 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -18,10 +18,10 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" packages_module "code.gitea.io/gitea/modules/packages" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/util" notify_service "code.gitea.io/gitea/services/notify" ) @@ -330,7 +330,7 @@ func CheckCountQuotaExceeded(ctx context.Context, doer, owner *user_model.User) if setting.Packages.LimitTotalOwnerCount > -1 { totalCount, err := packages_model.CountVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: owner.ID, - IsInternal: util.OptionalBoolFalse, + IsInternal: optional.Some(false), }) if err != nil { log.Error("CountVersions failed: %v", err) @@ -640,7 +640,7 @@ func RemoveAllPackages(ctx context.Context, userID int64) (int, error) { Page: 1, }, OwnerID: userID, - IsInternal: util.OptionalBoolNone, + IsInternal: optional.None[bool](), }) if err != nil { return count, fmt.Errorf("GetOwnedPackages[%d]: %w", userID, err) diff --git a/services/pull/review.go b/services/pull/review.go index 3ffc276778c96..90d07c8358067 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -18,8 +18,8 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" notify_service "code.gitea.io/gitea/services/notify" ) @@ -56,7 +56,7 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis ListAll: true, }, Type: issues_model.CommentTypeCode, - Invalidated: util.OptionalBoolFalse, + Invalidated: optional.Some(false), IssueIDs: issueIDs, }) if err != nil { @@ -327,7 +327,7 @@ func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *is }, IssueID: pull.IssueID, Type: issues_model.ReviewTypeApprove, - Dismissed: util.OptionalBoolFalse, + Dismissed: optional.Some(false), }) if err != nil { return err @@ -394,7 +394,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{ IssueID: review.IssueID, ReviewerID: review.ReviewerID, - Dismissed: util.OptionalBoolFalse, + Dismissed: optional.Some(false), }) if err != nil { return nil, err diff --git a/services/repository/branch.go b/services/repository/branch.go index ec41173da8c4f..55cedf5d84bfb 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -24,7 +24,6 @@ import ( "code.gitea.io/gitea/modules/queue" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" notify_service "code.gitea.io/gitea/services/notify" files_service "code.gitea.io/gitea/services/repository/files" @@ -54,7 +53,7 @@ type Branch struct { } // LoadBranches loads branches from the repository limited by page & pageSize. -func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, isDeletedBranch util.OptionalBool, keyword string, page, pageSize int) (*Branch, []*Branch, int64, error) { +func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, isDeletedBranch optional.Option[bool], keyword string, page, pageSize int) (*Branch, []*Branch, int64, error) { defaultDBBranch, err := git_model.GetBranch(ctx, repo.ID, repo.DefaultBranch) if err != nil { return nil, nil, 0, err @@ -62,7 +61,7 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git branchOpts := git_model.FindBranchOptions{ RepoID: repo.ID, - IsDeletedBranch: isDeletedBranch.ToGeneric(), + IsDeletedBranch: isDeletedBranch, ListOptions: db.ListOptions{ Page: page, PageSize: pageSize, diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index ac18da3525288..35c760dc62409 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -225,7 +226,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu if source.Repository != nil { repoHooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{ RepoID: source.Repository.ID, - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), }) if err != nil { return fmt.Errorf("ListWebhooksByOpts: %w", err) @@ -239,7 +240,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu if owner != nil { ownerHooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{ OwnerID: owner.ID, - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), }) if err != nil { return fmt.Errorf("ListWebhooksByOpts: %w", err) @@ -248,7 +249,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu } // Add any admin-defined system webhooks - systemHooks, err := webhook_model.GetSystemWebhooks(ctx, util.OptionalBoolTrue) + systemHooks, err := webhook_model.GetSystemWebhooks(ctx, optional.Some(true)) if err != nil { return fmt.Errorf("GetSystemWebhooks: %w", err) } From 6465f94a2d26cdacc232fddc20f98d98df61ddac Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 3 Mar 2024 00:07:54 +0800 Subject: [PATCH 04/12] Fix queue worker incorrectly stopped when there are still more items in the queue (#29532) Without `case <-t.C`, the workers would stop incorrectly, the test won't pass. For the worse case, there might be only one running worker processing the queue items for long time because other workers are stopped. The root cause is related to the logic of doDispatchBatchToWorker. It isn't a serious problem at the moment, so keep it as-is. --- modules/queue/workergroup.go | 20 ++++++++++++++++---- modules/queue/workerqueue.go | 2 ++ modules/queue/workerqueue_test.go | 29 ++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/modules/queue/workergroup.go b/modules/queue/workergroup.go index 147a4f335e101..e3801ef2b2dd0 100644 --- a/modules/queue/workergroup.go +++ b/modules/queue/workergroup.go @@ -60,6 +60,9 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh full = true } + // TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum" + // The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later + // So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary. q.workerNumMu.Lock() noWorker := q.workerNum == 0 if full || noWorker { @@ -143,7 +146,11 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { log.Debug("Queue %q starts new worker", q.GetName()) defer log.Debug("Queue %q stops idle worker", q.GetName()) + atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging + t := time.NewTicker(workerIdleDuration) + defer t.Stop() + keepWorking := true stopWorking := func() { q.workerNumMu.Lock() @@ -158,13 +165,18 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { case batch, ok := <-q.batchChan: if !ok { stopWorking() - } else { - q.doWorkerHandle(batch) - t.Reset(workerIdleDuration) + continue + } + q.doWorkerHandle(batch) + // reset the idle ticker, and drain the tick after reset in case a tick is already triggered + t.Reset(workerIdleDuration) + select { + case <-t.C: + default: } case <-t.C: q.workerNumMu.Lock() - keepWorking = q.workerNum <= 1 + keepWorking = q.workerNum <= 1 // keep the last worker running if !keepWorking { q.workerNum-- } diff --git a/modules/queue/workerqueue.go b/modules/queue/workerqueue.go index b28fd880270ab..4160622d81388 100644 --- a/modules/queue/workerqueue.go +++ b/modules/queue/workerqueue.go @@ -40,6 +40,8 @@ type WorkerPoolQueue[T any] struct { workerMaxNum int workerActiveNum int workerNumMu sync.Mutex + + workerStartedCounter int32 } type flushType chan struct{} diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go index e60120162a706..e09669c54255e 100644 --- a/modules/queue/workerqueue_test.go +++ b/modules/queue/workerqueue_test.go @@ -11,6 +11,7 @@ import ( "time" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) @@ -175,11 +176,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett } func TestWorkerPoolQueueActiveWorkers(t *testing.T) { - oldWorkerIdleDuration := workerIdleDuration - workerIdleDuration = 300 * time.Millisecond - defer func() { - workerIdleDuration = oldWorkerIdleDuration - }() + defer test.MockVariableValue(&workerIdleDuration, 300*time.Millisecond)() handler := func(items ...int) (unhandled []int) { time.Sleep(100 * time.Millisecond) @@ -250,3 +247,25 @@ func TestWorkerPoolQueueShutdown(t *testing.T) { q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", qs, handler, false) assert.EqualValues(t, 20, q.GetQueueItemNumber()) } + +func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) { + defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)() + + handler := func(items ...int) (unhandled []int) { + time.Sleep(50 * time.Millisecond) + return nil + } + + q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false) + stop := runWorkerPoolQueue(q) + for i := 0; i < 20; i++ { + assert.NoError(t, q.Push(i)) + } + + time.Sleep(500 * time.Millisecond) + assert.EqualValues(t, 2, q.GetWorkerNumber()) + assert.EqualValues(t, 2, q.GetWorkerActiveNumber()) + // when the queue never becomes empty, the existing workers should keep working + assert.EqualValues(t, 2, q.workerStartedCounter) + stop() +} From 70c126e6184872a6ac63cae2f327fc745b25d1d7 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sat, 2 Mar 2024 18:02:01 +0100 Subject: [PATCH 05/12] Add support for API blob upload of release attachments (#29507) Fixes #29502 Our endpoint is not Github compatible. https://docs.github.com/en/rest/releases/assets?apiVersion=2022-11-28#upload-a-release-asset --------- Co-authored-by: Giteabot --- routers/api/v1/repo/release_attachment.go | 39 +++++++++---- services/attachment/attachment.go | 6 +- templates/swagger/v1_json.tmpl | 6 +- tests/integration/api_releases_test.go | 68 +++++++++++++++++------ 4 files changed, 87 insertions(+), 32 deletions(-) diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index a29bce66a4e89..59fd83e3a2793 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -4,7 +4,9 @@ package repo import ( + "io" "net/http" + "strings" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/log" @@ -154,6 +156,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { // - application/json // consumes: // - multipart/form-data + // - application/octet-stream // parameters: // - name: owner // in: path @@ -180,7 +183,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { // in: formData // description: attachment to upload // type: file - // required: true + // required: false // responses: // "201": // "$ref": "#/responses/Attachment" @@ -202,20 +205,36 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // Get uploaded file from request - file, header, err := ctx.Req.FormFile("attachment") - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetFile", err) - return + var content io.ReadCloser + var filename string + var size int64 = -1 + + if strings.HasPrefix(strings.ToLower(ctx.Req.Header.Get("Content-Type")), "multipart/form-data") { + file, header, err := ctx.Req.FormFile("attachment") + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetFile", err) + return + } + defer file.Close() + + content = file + size = header.Size + filename = header.Filename + if name := ctx.FormString("name"); name != "" { + filename = name + } + } else { + content = ctx.Req.Body + filename = ctx.FormString("name") } - defer file.Close() - filename := header.Filename - if query := ctx.FormString("name"); query != "" { - filename = query + if filename == "" { + ctx.Error(http.StatusBadRequest, "CreateReleaseAttachment", "Could not determine name of attachment.") + return } // Create a new attachment and save the file - attach, err := attachment.UploadAttachment(ctx, file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{ + attach, err := attachment.UploadAttachment(ctx, content, setting.Repository.Release.AllowedTypes, size, &repo_model.Attachment{ Name: filename, UploaderID: ctx.Doer.ID, RepoID: ctx.Repo.Repository.ID, diff --git a/services/attachment/attachment.go b/services/attachment/attachment.go index eab3d0b142a1b..0fd51e4fa5eeb 100644 --- a/services/attachment/attachment.go +++ b/services/attachment/attachment.go @@ -39,14 +39,14 @@ func NewAttachment(ctx context.Context, attach *repo_model.Attachment, file io.R } // UploadAttachment upload new attachment into storage and update database -func UploadAttachment(ctx context.Context, file io.Reader, allowedTypes string, fileSize int64, opts *repo_model.Attachment) (*repo_model.Attachment, error) { +func UploadAttachment(ctx context.Context, file io.Reader, allowedTypes string, fileSize int64, attach *repo_model.Attachment) (*repo_model.Attachment, error) { buf := make([]byte, 1024) n, _ := util.ReadAtMost(file, buf) buf = buf[:n] - if err := upload.Verify(buf, opts.Name, allowedTypes); err != nil { + if err := upload.Verify(buf, attach.Name, allowedTypes); err != nil { return nil, err } - return NewAttachment(ctx, opts, io.MultiReader(bytes.NewReader(buf), file), fileSize) + return NewAttachment(ctx, attach, io.MultiReader(bytes.NewReader(buf), file), fileSize) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d4c5d9a7ee672..b739bea60d1b1 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -12343,7 +12343,8 @@ }, "post": { "consumes": [ - "multipart/form-data" + "multipart/form-data", + "application/octet-stream" ], "produces": [ "application/json" @@ -12386,8 +12387,7 @@ "type": "file", "description": "attachment to upload", "name": "attachment", - "in": "formData", - "required": true + "in": "formData" } ], "responses": { diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 5b1ab76ce97b3..49aa4c4e1bf63 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -262,24 +262,60 @@ func TestAPIUploadAssetRelease(t *testing.T) { filename := "image.png" buff := generateImg() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - assert.NoError(t, err) - _, err = io.Copy(part, &buff) - assert.NoError(t, err) - err = writer.Close() - assert.NoError(t, err) + assetURL := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets", owner.Name, repo.Name, r.ID) - req := NewRequestWithBody(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets?name=test-asset", owner.Name, repo.Name, r.ID), body). - AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) - resp := MakeRequest(t, req, http.StatusCreated) + t.Run("multipart/form-data", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + body := &bytes.Buffer{} + + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("attachment", filename) + assert.NoError(t, err) + _, err = io.Copy(part, bytes.NewReader(buff.Bytes())) + assert.NoError(t, err) + err = writer.Close() + assert.NoError(t, err) + + req := NewRequestWithBody(t, http.MethodPost, assetURL, bytes.NewReader(body.Bytes())). + AddTokenAuth(token). + SetHeader("Content-Type", writer.FormDataContentType()) + resp := MakeRequest(t, req, http.StatusCreated) + + var attachment *api.Attachment + DecodeJSON(t, resp, &attachment) + + assert.EqualValues(t, filename, attachment.Name) + assert.EqualValues(t, 104, attachment.Size) + + req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=test-asset", bytes.NewReader(body.Bytes())). + AddTokenAuth(token). + SetHeader("Content-Type", writer.FormDataContentType()) + resp = MakeRequest(t, req, http.StatusCreated) + + var attachment2 *api.Attachment + DecodeJSON(t, resp, &attachment2) + + assert.EqualValues(t, "test-asset", attachment2.Name) + assert.EqualValues(t, 104, attachment2.Size) + }) + + t.Run("application/octet-stream", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithBody(t, http.MethodPost, assetURL, bytes.NewReader(buff.Bytes())). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusBadRequest) + + req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=stream.bin", bytes.NewReader(buff.Bytes())). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusCreated) - var attachment *api.Attachment - DecodeJSON(t, resp, &attachment) + var attachment *api.Attachment + DecodeJSON(t, resp, &attachment) - assert.EqualValues(t, "test-asset", attachment.Name) - assert.EqualValues(t, 104, attachment.Size) + assert.EqualValues(t, "stream.bin", attachment.Name) + assert.EqualValues(t, 104, attachment.Size) + }) } From bf6502a8f7a2e9a2b64b43b7733316d863c9a768 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 3 Mar 2024 01:38:38 +0800 Subject: [PATCH 06/12] Fix incorrect relative/absolute URL usages (#29531) Add two "HTMLURL" methods for PackageDescriptor. And rename "FullWebLink" to "VersionWebLink" --- models/packages/descriptor.go | 16 +++++++++++++--- routers/api/packages/npm/api.go | 3 ++- routers/web/user/package.go | 2 +- services/convert/package.go | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index f849ab5c0417c..b8ef698d3822a 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -70,16 +70,26 @@ type PackageFileDescriptor struct { Properties PackagePropertyList } -// PackageWebLink returns the package web link +// PackageWebLink returns the relative package web link func (pd *PackageDescriptor) PackageWebLink() string { return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName)) } -// FullWebLink returns the package version web link -func (pd *PackageDescriptor) FullWebLink() string { +// VersionWebLink returns the relative package version web link +func (pd *PackageDescriptor) VersionWebLink() string { return fmt.Sprintf("%s/%s", pd.PackageWebLink(), url.PathEscape(pd.Version.LowerVersion)) } +// PackageHTMLURL returns the absolute package HTML URL +func (pd *PackageDescriptor) PackageHTMLURL() string { + return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HTMLURL(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName)) +} + +// VersionHTMLURL returns the absolute package version HTML URL +func (pd *PackageDescriptor) VersionHTMLURL() string { + return fmt.Sprintf("%s/%s", pd.PackageHTMLURL(), url.PathEscape(pd.Version.LowerVersion)) +} + // CalculateBlobSize returns the total blobs size in bytes func (pd *PackageDescriptor) CalculateBlobSize() int64 { size := int64(0) diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go index 8470874884238..f8e839c42412e 100644 --- a/routers/api/packages/npm/api.go +++ b/routers/api/packages/npm/api.go @@ -12,6 +12,7 @@ import ( packages_model "code.gitea.io/gitea/models/packages" npm_module "code.gitea.io/gitea/modules/packages/npm" + "code.gitea.io/gitea/modules/setting" ) func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *npm_module.PackageMetadata { @@ -98,7 +99,7 @@ func createPackageSearchResponse(pds []*packages_model.PackageDescriptor, total Maintainers: []npm_module.User{}, // npm cli needs this field Keywords: metadata.Keywords, Links: &npm_module.PackageSearchPackageLinks{ - Registry: pd.FullWebLink(), + Registry: setting.AppURL + "api/packages/" + pd.Owner.Name + "/npm", Homepage: metadata.ProjectURL, }, }, diff --git a/routers/web/user/package.go b/routers/web/user/package.go index 4f3de13dfbced..3ecc59a2abad3 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -163,7 +163,7 @@ func RedirectToLastVersion(ctx *context.Context) { return } - ctx.Redirect(pd.FullWebLink()) + ctx.Redirect(pd.VersionWebLink()) } // ViewPackageVersion displays a single package version diff --git a/services/convert/package.go b/services/convert/package.go index e90ce8a00f848..b5fca21a3c3a5 100644 --- a/services/convert/package.go +++ b/services/convert/package.go @@ -35,7 +35,7 @@ func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_m Name: pd.Package.Name, Version: pd.Version.Version, CreatedAt: pd.Version.CreatedUnix.AsTime(), - HTMLURL: pd.FullWebLink(), + HTMLURL: pd.VersionHTMLURL(), }, nil } From 937e8b55149388840bbf6c4d7216495bc3dd2fe9 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 2 Mar 2024 21:31:59 +0200 Subject: [PATCH 07/12] Fix elipsis button not working if the last commit loading is deferred (#29544) Before this change, if we had more than 200 entries being deferred in loading, the entire table would get replaced thus losing any event listeners attached to the elements within the table, such as the elipsis button and commit list with tippy. With this change we remove the previous javascript code that replaced the table and use htmx to replace the table. htmx attributes added: - `hx-indicator="tr.notready td.message span"`: attach the loading spinner to the files whose last commit is still being loaded - `hx-trigger="load"` trigger the request-replace behavior as soon as possible - `hx-swap="morph"`: use the idiomorph morphing algorithm, this is the thing that makes it so the elipsis button event listener is kept during the replacement, fixing the bug because we don't actually replace the table, only modifying it - `hx-post="{{.LastCommitLoaderURL}}"`: make a post request to this url to get the table with all of the commit information As part of this change I removed the handling of partial replacement in the case we have less than 200 "not ready" files. The first reason is that I couldn't make htmx replace only a subset of returned elements, the second reason is that we have a cache implemented in the backend already so the only cost added is that we query the cache a few times (which is sure to be populated due to the initial request), and the last reason is that since the last refactor of this functionality that removed jQuery we don't properly send the "not ready" entries as the backend expects `FormData` with `f[]` and we send a JSON with `f` so we always query for all rows anyway. # Before ![before](https://github.com/go-gitea/gitea/assets/20454870/482ebfec-66c5-40cc-9c1e-e3b3bfe1bbc1) # After ![after](https://github.com/go-gitea/gitea/assets/20454870/454c517e-3a4e-4006-a49f-99cc56e0fd60) --------- Signed-off-by: Yarden Shoham Co-authored-by: wxiaoguang --- routers/web/repo/view.go | 24 ++++++----------- templates/repo/view_list.tmpl | 6 ++--- web_src/js/features/repo-commit.js | 43 ------------------------------ web_src/js/index.js | 7 +---- 4 files changed, 12 insertions(+), 68 deletions(-) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index e89739e2fb6d1..4df10fbea1adb 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -35,7 +35,6 @@ import ( "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/lfs" @@ -859,25 +858,18 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri defer cancel() } - selected := make(container.Set[string]) - selected.AddMultiple(ctx.FormStrings("f[]")...) - - entries := allEntries - if len(selected) > 0 { - entries = make(git.Entries, 0, len(selected)) - for _, entry := range allEntries { - if selected.Contains(entry.Name()) { - entries = append(entries, entry) - } - } - } - - var latestCommit *git.Commit - ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(commitInfoCtx, ctx.Repo.Commit, ctx.Repo.TreePath) + files, latestCommit, err := allEntries.GetCommitsInfo(commitInfoCtx, ctx.Repo.Commit, ctx.Repo.TreePath) if err != nil { ctx.ServerError("GetCommitsInfo", err) return nil } + ctx.Data["Files"] = files + for _, f := range files { + if f.Commit == nil { + ctx.Data["HasFilesWithoutLatestCommit"] = true + break + } + } if !loadLatestCommitData(ctx, latestCommit) { return nil diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index c1ef4ff4cb0e5..988a5ddd50e8e 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -1,7 +1,7 @@ - +
- @@ -55,7 +55,7 @@ {{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}} {{RenderCommitMessageLinkSubject $.Context $commit.Message $commitLink ($.Repository.ComposeMetas ctx)}} {{else}} -
+
{{end}} diff --git a/web_src/js/features/repo-commit.js b/web_src/js/features/repo-commit.js index 7e2f6fa58ead2..f61ea08a42b69 100644 --- a/web_src/js/features/repo-commit.js +++ b/web_src/js/features/repo-commit.js @@ -1,7 +1,5 @@ import {createTippy} from '../modules/tippy.js'; import {toggleElem} from '../utils/dom.js'; -import {parseDom} from '../utils.js'; -import {POST} from '../modules/fetch.js'; export function initRepoEllipsisButton() { for (const button of document.querySelectorAll('.js-toggle-commit-body')) { @@ -14,47 +12,6 @@ export function initRepoEllipsisButton() { } } -export async function initRepoCommitLastCommitLoader() { - const entryMap = {}; - - const entries = Array.from(document.querySelectorAll('table#repo-files-table tr.notready'), (el) => { - const entryName = el.getAttribute('data-entryname'); - entryMap[entryName] = el; - return entryName; - }); - - if (entries.length === 0) { - return; - } - - const lastCommitLoaderURL = document.querySelector('table#repo-files-table').getAttribute('data-last-commit-loader-url'); - - if (entries.length > 200) { - // For more than 200 entries, replace the entire table - const response = await POST(lastCommitLoaderURL); - const data = await response.text(); - document.querySelector('table#repo-files-table').outerHTML = data; - return; - } - - // For fewer entries, update individual rows - const response = await POST(lastCommitLoaderURL, {data: {'f': entries}}); - const data = await response.text(); - const doc = parseDom(data, 'text/html'); - for (const row of doc.querySelectorAll('tr')) { - if (row.className === 'commit-list') { - document.querySelector('table#repo-files-table .commit-list')?.replaceWith(row); - continue; - } - // there are other rows in response (eg: ) - // at the moment only the "data-entryname" rows should be processed - const entryName = row.getAttribute('data-entryname'); - if (entryName) { - entryMap[entryName]?.replaceWith(row); - } - } -} - export function initCommitStatuses() { for (const element of document.querySelectorAll('[data-tippy="commit-statuses"]')) { const top = document.querySelector('.repository.file.list') || document.querySelector('.repository.diff'); diff --git a/web_src/js/index.js b/web_src/js/index.js index b7f3ba99a0e95..c7eac9d24273e 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -33,11 +33,7 @@ import { initRepoPullRequestAllowMaintainerEdit, initRepoPullRequestReview, initRepoIssueSidebarList, initArchivedLabelHandler, } from './features/repo-issue.js'; -import { - initRepoEllipsisButton, - initRepoCommitLastCommitLoader, - initCommitStatuses, -} from './features/repo-commit.js'; +import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.js'; import { initFootLanguageMenu, initGlobalButtonClickOnEnter, @@ -148,7 +144,6 @@ onDomReady(() => { initRepoCommentForm(); initRepoEllipsisButton(); initRepoDiffCommitBranchesAndTags(); - initRepoCommitLastCommitLoader(); initRepoEditor(); initRepoGraphGit(); initRepoIssueContentHistory(); From e3e6569c5fd8c69aa65384e6d1636cc14b23a32b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 2 Mar 2024 22:55:02 +0100 Subject: [PATCH 08/12] Add option to set language in admin user view (#28449) ![image](https://github.com/go-gitea/gitea/assets/24977596/be7e3f92-af3f-4628-b4ed-abf6439687f3) `/admin/users//edit` ![image](https://github.com/go-gitea/gitea/assets/24977596/906af0dd-cceb-4ed9-9cd9-32c71ae1bf71) `/admin/users/` --- *Sponsored by Kithara Software GmbH* --- routers/web/admin/users.go | 1 + services/forms/admin.go | 1 + templates/admin/user/edit.tmpl | 15 +++++++++++++++ templates/admin/user/view_details.tmpl | 8 ++++++++ 4 files changed, 25 insertions(+) diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index ca47175401447..a34e0d0f0dc5b 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -439,6 +439,7 @@ func EditUserPost(ctx *context.Context) { AllowCreateOrganization: optional.Some(form.AllowCreateOrganization), IsRestricted: optional.Some(form.Restricted), Visibility: optional.Some(form.Visibility), + Language: optional.Some(form.Language), } if err := user_service.UpdateUser(ctx, u, opts); err != nil { diff --git a/services/forms/admin.go b/services/forms/admin.go index f112013060708..81276f8f46f9c 100644 --- a/services/forms/admin.go +++ b/services/forms/admin.go @@ -41,6 +41,7 @@ type AdminEditUserForm struct { Password string `binding:"MaxSize(255)"` Website string `binding:"ValidUrl;MaxSize(255)"` Location string `binding:"MaxSize(50)"` + Language string `binding:"MaxSize(5)"` MaxRepoCreation int Active bool Admin bool diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index fcb8ce08277fb..159c8210993fc 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -70,6 +70,21 @@

{{ctx.Locale.Tr "admin.users.password_helper"}}

+ +
+ + +
+
diff --git a/templates/admin/user/view_details.tmpl b/templates/admin/user/view_details.tmpl index 21425eecb4c3e..be2f32b5ec7de 100644 --- a/templates/admin/user/view_details.tmpl +++ b/templates/admin/user/view_details.tmpl @@ -48,6 +48,14 @@ {{svg "octicon-x"}} {{end}}
+ {{if .User.Language}} +
+ + {{ctx.Locale.Tr "settings.language"}}: + {{range .AllLangs}}{{if eq $.User.Language .Lang}}{{.Name}}{{end}}{{end}} + +
+ {{end}} {{if .User.Location}}
{{svg "octicon-location"}}{{.User.Location}} From cc896258b9ea9f60c33077c937ce9b3951b58b35 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 3 Mar 2024 00:44:43 +0100 Subject: [PATCH 09/12] gitea.service: Remove syslog.target (#29550) Remove syslog.target from service file, this target hasn't existed for over a decade. https://github.com/systemd/systemd/blob/6aa8d43ade72e24c9426e604f7fc4b7582b9db7c/NEWS#L72-L73 --- contrib/systemd/gitea.service | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/systemd/gitea.service b/contrib/systemd/gitea.service index d205c6ee8ba6e..c091722a743be 100644 --- a/contrib/systemd/gitea.service +++ b/contrib/systemd/gitea.service @@ -1,6 +1,5 @@ [Unit] Description=Gitea (Git with a cup of tea) -After=syslog.target After=network.target ### # Don't forget to add the database service dependencies From 44398e405ffe297997c6b9c8dbb97f966926b65a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 3 Mar 2024 08:14:12 +0800 Subject: [PATCH 10/12] Fix incorrect cookie path for AppSubURL (#29534) Regression of #24107 --- modules/setting/session.go | 7 +++++-- routers/common/middleware.go | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/setting/session.go b/modules/setting/session.go index 8b9b754b38702..70497e5eaa8b7 100644 --- a/modules/setting/session.go +++ b/modules/setting/session.go @@ -20,7 +20,7 @@ var SessionConfig = struct { ProviderConfig string // Cookie name to save session ID. Default is "MacaronSession". CookieName string - // Cookie path to store. Default is "/". HINT: there was a bug, the old value doesn't have trailing slash, and could be empty "". + // Cookie path to store. Default is "/". CookiePath string // GC interval time in seconds. Default is 3600. Gclifetime int64 @@ -49,7 +49,10 @@ func loadSessionFrom(rootCfg ConfigProvider) { fatalDuplicatedPath("session", SessionConfig.ProviderConfig) } SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea") - SessionConfig.CookiePath = AppSubURL + "/" // there was a bug, old code only set CookePath=AppSubURL, no trailing slash + SessionConfig.CookiePath = AppSubURL + if SessionConfig.CookiePath == "" { + SessionConfig.CookiePath = "/" + } SessionConfig.Secure = sec.Key("COOKIE_SECURE").MustBool(strings.HasPrefix(strings.ToLower(AppURL), "https://")) SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400) SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400) diff --git a/routers/common/middleware.go b/routers/common/middleware.go index 1ee4c629adbc2..c7c75fb099b46 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -38,6 +38,7 @@ func ProtocolMiddlewares() (handlers []any) { }) }) + // wrap the request and response, use the process context and add it to the process manager handlers = append(handlers, func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true) From 22b4f0c09f1de5e581929bd10f39833d30d2c482 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sun, 3 Mar 2024 00:25:22 +0000 Subject: [PATCH 11/12] [skip ci] Updated translations via Crowdin --- options/locale/locale_ja-JP.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 5d9e21703ed19..4d1ecef61c102 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -123,6 +123,7 @@ pin=ピン留め unpin=ピン留め解除 artifacts=成果物 +confirm_delete_artifact=アーティファクト %s を削除してよろしいですか? archived=アーカイブ @@ -423,6 +424,7 @@ authorization_failed_desc=無効なリクエストを検出したため認可が sspi_auth_failed=SSPI認証に失敗しました password_pwned=あなたが選択したパスワードは、過去の情報漏洩事件で流出した盗まれたパスワードのリストに含まれています。 別のパスワードでもう一度試してください。 また他の登録でもこのパスワードからの変更を検討してください。 password_pwned_err=HaveIBeenPwnedへのリクエストを完了できませんでした +last_admin=最後の管理者は削除できません。少なくとも一人の管理者が必要です。 [mail] view_it_on=%s で見る @@ -588,6 +590,7 @@ org_still_own_packages=組織はまだ1つ以上のパッケージを所有し target_branch_not_exist=ターゲットのブランチが存在していません。 +admin_cannot_delete_self=あなたが管理者である場合、自分自身を削除することはできません。最初に管理者権限を削除してください。 [user] change_avatar=アバターを変更… @@ -968,6 +971,8 @@ issue_labels_helper=イシューのラベルセットを選択 license=ライセンス license_helper=ライセンス ファイルを選択してください。 license_helper_desc=ライセンスにより、他人があなたのコードに対して何ができて何ができないのかを規定します。 どれがプロジェクトにふさわしいか迷っていますか? ライセンス選択サイト も確認してみてください。 +object_format=オブジェクトのフォーマット +object_format_helper=リポジトリのオブジェクトフォーマット。後で変更することはできません。SHA1 は最も互換性があります。 readme=README readme_helper=READMEファイル テンプレートを選択してください。 readme_helper_desc=プロジェクトについての説明をひととおり書く場所です。 @@ -1035,6 +1040,7 @@ desc.public=公開 desc.template=テンプレート desc.internal=組織内 desc.archived=アーカイブ +desc.sha256=SHA256 template.items=テンプレート項目 template.git_content=Gitコンテンツ (デフォルトブランチ) From e71b69257c38178eed9ccd0b62a5ae47d67858d4 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 3 Mar 2024 12:57:22 +0800 Subject: [PATCH 12/12] Breaking summary for template refactoring (#29395) https://github.com/go-gitea/gitea/pull/29395 --- .../administration/mail-templates.en-us.md | 16 +++++------ .../administration/mail-templates.zh-cn.md | 16 +++++------ modules/templates/mailer.go | 28 +++++++++---------- templates/mail/issue/default.tmpl | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/content/administration/mail-templates.en-us.md b/docs/content/administration/mail-templates.en-us.md index 9077f97aeac32..0154fe55d0f7b 100644 --- a/docs/content/administration/mail-templates.en-us.md +++ b/docs/content/administration/mail-templates.en-us.md @@ -259,14 +259,14 @@ This template produces something along these lines: The template system contains several functions that can be used to further process and format the messages. Here's a list of some of them: -| Name | Parameters | Available | Usage | -| ---------------- | ----------- | --------- |-----------------------------------------------------------------------------| -| `AppUrl` | - | Any | Gitea's URL | -| `AppName` | - | Any | Set from `app.ini`, usually "Gitea" | -| `AppDomain` | - | Any | Gitea's host name | -| `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed | -| `SanitizeHTML` | string | Body only | Sanitizes text by removing any dangerous HTML tags from it. | -| `SafeHTML` | string | Body only | Takes the input as HTML; can be used for `.ReviewComments.RenderedContent`. | +| Name | Parameters | Available | Usage | +| ---------------- | ----------- | --------- | ------------------------------------------------------------------- | +| `AppUrl` | - | Any | Gitea's URL | +| `AppName` | - | Any | Set from `app.ini`, usually "Gitea" | +| `AppDomain` | - | Any | Gitea's host name | +| `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed | +| `SanitizeHTML` | string | Body only | Sanitizes text by removing any dangerous HTML tags from it | +| `SafeHTML` | string | Body only | Takes the input as HTML, can be used for outputing raw HTML content | These are _functions_, not metadata, so they have to be used: diff --git a/docs/content/administration/mail-templates.zh-cn.md b/docs/content/administration/mail-templates.zh-cn.md index d58f9dc176368..e8c2817336c80 100644 --- a/docs/content/administration/mail-templates.zh-cn.md +++ b/docs/content/administration/mail-templates.zh-cn.md @@ -242,14 +242,14 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/ 模板系统包含一些函数,可用于进一步处理和格式化消息。以下是其中一些函数的列表: -| 函数名 | 参数 | 可用于 | 用法 | -|------------------| ----------- | ------------ |---------------------------------------------------------| -| `AppUrl` | - | 任何地方 | Gitea 的 URL | -| `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" | -| `AppDomain` | - | 任何地方 | Gitea 的主机名 | -| `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 | -| `SanitizeHTML` | string | 仅正文部分 | 通过删除其中的危险 HTML 标签对文本进行清理 | -| `SafeHTML` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于 `.ReviewComments.RenderedContent` 等字段 | +| 函数名 | 参数 | 可用于 | 用法 | +|------------------| ----------- | ------------ | ------------------------------ | +| `AppUrl` | - | 任何地方 | Gitea 的 URL | +| `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" | +| `AppDomain` | - | 任何地方 | Gitea 的主机名 | +| `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 | +| `SanitizeHTML` | string | 仅正文部分 | 通过删除其中的危险 HTML 标签对文本进行清理 | +| `SafeHTML` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于输出原始的 HTML 内容 | 这些都是 _函数_,而不是元数据,因此必须按以下方式使用: diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go index 04032e3982788..f1832cba0e957 100644 --- a/modules/templates/mailer.go +++ b/modules/templates/mailer.go @@ -5,6 +5,7 @@ package templates import ( "context" + "fmt" "html/template" "regexp" "strings" @@ -33,7 +34,7 @@ func mailSubjectTextFuncMap() texttmpl.FuncMap { } } -func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, name string, content []byte) { +func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, name string, content []byte) error { // Split template into subject and body var subjectContent []byte bodyContent := content @@ -42,20 +43,13 @@ func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, subjectContent = content[0:loc[0]] bodyContent = content[loc[1]:] } - if _, err := stpl.New(name). - Parse(string(subjectContent)); err != nil { - log.Error("Failed to parse template [%s/subject]: %v", name, err) - if !setting.IsProd { - log.Fatal("Please fix the mail template error") - } + if _, err := stpl.New(name).Parse(string(subjectContent)); err != nil { + return fmt.Errorf("failed to parse template [%s/subject]: %w", name, err) } - if _, err := btpl.New(name). - Parse(string(bodyContent)); err != nil { - log.Error("Failed to parse template [%s/body]: %v", name, err) - if !setting.IsProd { - log.Fatal("Please fix the mail template error") - } + if _, err := btpl.New(name).Parse(string(bodyContent)); err != nil { + return fmt.Errorf("failed to parse template [%s/body]: %w", name, err) } + return nil } // Mailer provides the templates required for sending notification mails. @@ -87,7 +81,13 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) { if firstRun { log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName) } - buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content) + if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil { + if firstRun { + log.Fatal("Failed to parse mail template, err: %v", err) + } else { + log.Error("Failed to parse mail template, err: %v", err) + } + } } } diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index 10fa0f1ffceb9..021ca3989dbb9 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -65,7 +65,7 @@ {{$.locale.Tr "mail.issue.in_tree_path" .TreePath}}
{{.Patch}}
-
{{.RenderedContent | SafeHTML}}
+
{{.RenderedContent}}
{{end -}} {{if eq .ActionName "push"}}
+ {{template "repo/latest_commit" .}} {{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When ctx.Locale}}{{end}}{{end}}