Add Notifications section in User Settings (#35008)

Related: #34982

---------

Signed-off-by: NorthRealm <155140859+NorthRealm@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
NorthRealm 2025-07-11 10:17:52 +08:00 committed by GitHub
parent b46623f6a5
commit 56eccb4995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 109 additions and 58 deletions

View File

@ -35,7 +35,7 @@ const (
// Account renders change user's password, user's email and user suicide page
func Account(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials, setting.UserFeatureDeletion) && !setting.Service.EnableNotifyMail {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials, setting.UserFeatureDeletion) {
ctx.NotFound(errors.New("account setting are not allowed to be changed"))
return
}
@ -43,7 +43,6 @@ func Account(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings.account")
ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.Doer.Email
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
loadAccountData(ctx)
@ -61,7 +60,6 @@ func AccountPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.Doer.Email
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
if ctx.HasError() {
loadAccountData(ctx)
@ -112,7 +110,6 @@ func EmailPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.Doer.Email
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
// Make email address primary.
if ctx.FormString("_method") == "PRIMARY" {
@ -172,30 +169,6 @@ func EmailPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
return
}
// Set Email Notification Preference
if ctx.FormString("_method") == "NOTIFICATION" {
preference := ctx.FormString("preference")
if !(preference == user_model.EmailNotificationsEnabled ||
preference == user_model.EmailNotificationsOnMention ||
preference == user_model.EmailNotificationsDisabled ||
preference == user_model.EmailNotificationsAndYourOwn) {
log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name)
ctx.ServerError("SetEmailPreference", errors.New("option unrecognized"))
return
}
opts := &user.UpdateOptions{
EmailNotificationsPreference: optional.Some(preference),
}
if err := user.UpdateUser(ctx, ctx.Doer, opts); err != nil {
log.Error("Set Email Notifications failed: %v", err)
ctx.ServerError("UpdateUser", err)
return
}
log.Trace("Email notifications preference made %s: %s", preference, ctx.Doer.Name)
ctx.Flash.Success(ctx.Tr("settings.email_preference_set_success"))
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
return
}
if ctx.HasError() {
loadAccountData(ctx)
@ -267,7 +240,6 @@ func DeleteAccount(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.Doer.Email
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
if _, _, err := auth.UserSignIn(ctx, ctx.Doer.Name, ctx.FormString("password")); err != nil {
switch {
@ -342,7 +314,6 @@ func loadAccountData(ctx *context.Context) {
emails[i] = &email
}
ctx.Data["Emails"] = emails
ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference
ctx.Data["ActivationsPending"] = pendingActivation
ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm
ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)

View File

@ -0,0 +1,62 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"errors"
"net/http"
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/templates"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/user"
)
const tplSettingsNotifications templates.TplName = "user/settings/notifications"
// Notifications render user's notifications settings
func Notifications(ctx *context.Context) {
if !setting.Service.EnableNotifyMail {
ctx.NotFound(nil)
return
}
ctx.Data["Title"] = ctx.Tr("notifications")
ctx.Data["PageIsSettingsNotifications"] = true
ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference
ctx.HTML(http.StatusOK, tplSettingsNotifications)
}
// NotificationsEmailPost set user's email notification preference
func NotificationsEmailPost(ctx *context.Context) {
if !setting.Service.EnableNotifyMail {
ctx.NotFound(nil)
return
}
preference := ctx.FormString("preference")
if !(preference == user_model.EmailNotificationsEnabled ||
preference == user_model.EmailNotificationsOnMention ||
preference == user_model.EmailNotificationsDisabled ||
preference == user_model.EmailNotificationsAndYourOwn) {
log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name)
ctx.ServerError("SetEmailPreference", errors.New("option unrecognized"))
return
}
opts := &user.UpdateOptions{
EmailNotificationsPreference: optional.Some(preference),
}
if err := user.UpdateUser(ctx, ctx.Doer, opts); err != nil {
log.Error("Set Email Notifications failed: %v", err)
ctx.ServerError("UpdateUser", err)
return
}
log.Trace("Email notifications preference made %s: %s", preference, ctx.Doer.Name)
ctx.Flash.Success(ctx.Tr("settings.email_preference_set_success"))
ctx.Redirect(setting.AppSubURL + "/user/settings/notifications")
}

View File

@ -595,6 +595,10 @@ func registerWebRoutes(m *web.Router) {
m.Post("/hidden_comments", user_setting.UpdateUserHiddenComments)
m.Post("/theme", web.Bind(forms.UpdateThemeForm{}), user_setting.UpdateUIThemePost)
})
m.Group("/notifications", func() {
m.Get("", user_setting.Notifications)
m.Post("/email", user_setting.NotificationsEmailPost)
})
m.Group("/security", func() {
m.Get("", security.Security)
m.Group("/two_factor", func() {
@ -682,7 +686,7 @@ func registerWebRoutes(m *web.Router) {
m.Get("", user_setting.BlockedUsers)
m.Post("", web.Bind(forms.BlockUserForm{}), user_setting.BlockedUsersPost)
})
}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "EnablePackages", setting.Packages.Enabled))
}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "EnablePackages", setting.Packages.Enabled, "EnableNotifyMail", setting.Service.EnableNotifyMail))
m.Group("/user", func() {
m.Get("/activate", auth.Activate)

View File

@ -35,37 +35,12 @@
{{end}}
</div>
{{if not (and ($.UserDisabledFeatures.Contains "manage_credentials") (not $.EnableNotifyMail))}}
{{if not ($.UserDisabledFeatures.Contains "manage_credentials")}}
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.manage_emails"}}
</h4>
<div class="ui attached segment">
<div class="ui list flex-items-block">
{{if $.EnableNotifyMail}}
<div class="item">
<form class="ui form tw-w-full" action="{{AppSubUrl}}/user/settings/account/email" method="post">
{{$.CsrfTokenHtml}}
<input name="_method" type="hidden" value="NOTIFICATION">
<div class="field">
<label>{{ctx.Locale.Tr "settings.email_desc"}}</label>
<div class="ui selection dropdown">
<input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="text"></div>
<div class="menu">
<div data-value="enabled" class="{{if eq .EmailNotificationsPreference "enabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.enable"}}</div>
<div data-value="andyourown" class="{{if eq .EmailNotificationsPreference "andyourown"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.andyourown"}}</div>
<div data-value="onmention" class="{{if eq .EmailNotificationsPreference "onmention"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.onmention"}}</div>
<div data-value="disabled" class="{{if eq .EmailNotificationsPreference "disabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.disable"}}</div>
</div>
</div>
</div>
<div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "settings.email_notifications.submit"}}</button>
</div>
</form>
</div>
{{end}}
{{if not ($.UserDisabledFeatures.Contains "manage_credentials")}}
{{range .Emails}}
<div class="item tw-flex-wrap">

View File

@ -4,11 +4,16 @@
<a class="{{if .PageIsSettingsProfile}}active {{end}}item" href="{{AppSubUrl}}/user/settings">
{{ctx.Locale.Tr "settings.profile"}}
</a>
{{if not (and ($.UserDisabledFeatures.Contains "manage_credentials" "deletion") (not $.EnableNotifyMail))}}
{{if not ($.UserDisabledFeatures.Contains "manage_credentials" "deletion")}}
<a class="{{if .PageIsSettingsAccount}}active {{end}}item" href="{{AppSubUrl}}/user/settings/account">
{{ctx.Locale.Tr "settings.account"}}
</a>
{{end}}
{{if $.EnableNotifyMail}}
<a class="{{if .PageIsSettingsNotifications}}active {{end}}item" href="{{AppSubUrl}}/user/settings/notifications">
{{ctx.Locale.Tr "notifications"}}
</a>
{{end}}
<a class="{{if .PageIsSettingsAppearance}}active {{end}}item" href="{{AppSubUrl}}/user/settings/appearance">
{{ctx.Locale.Tr "settings.appearance"}}
</a>

View File

@ -0,0 +1,34 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings")}}
<div class="user-setting-content">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "notifications"}}
</h4>
<div class="ui attached segment">
<div class="ui list flex-items-block">
<div class="item">
<form class="ui form tw-w-full" action="{{AppSubUrl}}/user/settings/notifications/email" method="post">
{{$.CsrfTokenHtml}}
<div class="field">
<label>{{ctx.Locale.Tr "settings.email_desc"}}</label>
<div class="ui selection dropdown">
<input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="text"></div>
<div class="menu">
<div data-value="enabled" class="{{if eq .EmailNotificationsPreference "enabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.enable"}}</div>
<div data-value="andyourown" class="{{if eq .EmailNotificationsPreference "andyourown"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.andyourown"}}</div>
<div data-value="onmention" class="{{if eq .EmailNotificationsPreference "onmention"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.onmention"}}</div>
<div data-value="disabled" class="{{if eq .EmailNotificationsPreference "disabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.disable"}}</div>
</div>
</div>
</div>
<div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "settings.email_notifications.submit"}}</button>
</div>
</form>
</div>
</div>
</div>
</div>
{{template "user/settings/layout_footer" .}}