0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-07-17 19:42:55 +02:00
This commit is contained in:
NorthRealm 2025-07-12 02:04:39 +08:00
parent d92ec015c4
commit fed3ade254
14 changed files with 66 additions and 303 deletions

View File

@ -1,84 +0,0 @@
- user_id: 1
actions: failureonly
- user_id: 2
actions: failureonly
- user_id: 3
actions: failureonly
- user_id: 4
actions: failureonly
- user_id: 5
actions: failureonly
- user_id: 6
actions: failureonly
- user_id: 7
actions: failureonly
- user_id: 8
actions: failureonly
- user_id: 9
actions: failureonly
- user_id: 10
actions: failureonly
- user_id: 11
actions: failureonly
- user_id: 12
actions: failureonly
- user_id: 13
actions: failureonly
- user_id: 14
actions: failureonly
- user_id: 15
actions: failureonly
- user_id: 16
actions: failureonly
- user_id: 17
actions: failureonly
- user_id: 18
actions: failureonly
- user_id: 19
actions: failureonly
- user_id: 20
actions: failureonly
- user_id: 21
actions: failureonly
- user_id: 22
actions: failureonly
- user_id: 23
actions: failureonly
- user_id: 24
actions: failureonly
- user_id: 25
actions: failureonly
- user_id: 26
actions: failureonly
- user_id: 27
actions: failureonly
- user_id: 28
actions: failureonly
- user_id: 29
actions: failureonly
- user_id: 30
actions: failureonly
- user_id: 31
actions: failureonly
- user_id: 32
actions: failureonly
- user_id: 33
actions: failureonly
- user_id: 34
actions: failureonly
- user_id: 35
actions: failureonly
- user_id: 36
actions: failureonly
- user_id: 37
actions: failureonly
- user_id: 38
actions: failureonly
- user_id: 39
actions: failureonly
- user_id: 40
actions: failureonly
- user_id: 41
actions: failureonly
- user_id: 42
actions: failureonly

View File

@ -382,7 +382,6 @@ func prepareMigrationTasks() []*migration {
newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
newMigration(320, "Migrate two_factor_policy to login_source table", v1_24.MigrateSkipTwoFactor),
newMigration(321, "Add new table for fine-grained notification settings", v1_24.AddFineGrainedActionsNotificationSettings),
}
return preparedMigrations
}

View File

@ -1,59 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_24
import (
"context"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"xorm.io/xorm"
)
type NotificationSettings struct {
UserID int64 `xorm:"pk"`
Actions string `xorm:"NOT NULL DEFAULT 'failureonly'"`
}
func (*NotificationSettings) TableName() string {
return "user_notification_settings"
}
func AddFineGrainedActionsNotificationSettings(x *xorm.Engine) error {
if err := x.Sync(&NotificationSettings{}); err != nil {
return err
}
settings := make([]NotificationSettings, 0, 100)
type User struct {
ID int64 `xorm:"pk autoincr"`
}
if err := db.Iterate(context.Background(), nil, func(ctx context.Context, user *User) error {
settings = append(settings, NotificationSettings{
UserID: user.ID,
Actions: user_model.NotificationGiteaActionsFailureOnly,
})
if len(settings) >= 100 {
_, err := x.Insert(&settings)
if err != nil {
return err
}
settings = settings[:0]
}
return nil
}); err != nil {
return err
}
if len(settings) > 0 {
if _, err := x.Insert(&settings); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,11 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
// setting values
const (
EmailNotificationGiteaActionsAll = "all"
EmailNotificationGiteaActionsFailureOnly = "failureonly" // Default for actions email preference
EmailNotificationGiteaActionsDisabled = "disabled"
)

View File

@ -0,0 +1,34 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func TestNotificationSettings(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
u := unittest.AssertExistsAndLoadBean(t, &User{ID: 1})
assert.NoError(t, SetUserSetting(db.DefaultContext, u.ID, SettingsEmailNotificationGiteaActions, EmailNotificationGiteaActionsAll))
settings, err := GetSetting(db.DefaultContext, u.ID, SettingsEmailNotificationGiteaActions)
assert.NoError(t, err)
assert.Equal(t, EmailNotificationGiteaActionsAll, settings)
assert.NoError(t, SetUserSetting(db.DefaultContext, u.ID, SettingsEmailNotificationGiteaActions, EmailNotificationGiteaActionsDisabled))
settings, err = GetSetting(db.DefaultContext, u.ID, SettingsEmailNotificationGiteaActions)
assert.NoError(t, err)
assert.Equal(t, EmailNotificationGiteaActionsDisabled, settings)
assert.NoError(t, SetUserSetting(db.DefaultContext, u.ID, SettingsEmailNotificationGiteaActions, EmailNotificationGiteaActionsFailureOnly))
settings, err = GetSetting(db.DefaultContext, u.ID, SettingsEmailNotificationGiteaActions)
assert.NoError(t, err)
assert.Equal(t, EmailNotificationGiteaActionsFailureOnly, settings)
}

View File

@ -21,4 +21,6 @@ const (
SignupUserAgent = "signup.user_agent"
SettingsKeyCodeViewShowFileTree = "code_view.show_file_tree"
SettingsEmailNotificationGiteaActions = "email_notifications.actions"
)

View File

@ -798,10 +798,7 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o
return err
}
if err := db.Insert(ctx, &NotificationSettings{
UserID: u.ID,
Actions: NotificationGiteaActionsFailureOnly,
}); err != nil {
if err := SetUserSetting(ctx, u.ID, SettingsEmailNotificationGiteaActions, EmailNotificationGiteaActionsFailureOnly); err != nil {
return err
}

View File

@ -1,55 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"context"
"code.gitea.io/gitea/models/db"
)
// Actions email preference
const (
NotificationGiteaActionsAll = "all"
NotificationGiteaActionsFailureOnly = "failureonly"
NotificationGiteaActionsDisabled = "disabled"
)
type NotificationSettings struct {
UserID int64 `xorm:"pk"`
User *User `xorm:"-"`
Actions string `xorm:"NOT NULL DEFAULT 'failureonly'"`
}
func (NotificationSettings) TableName() string {
return "user_notification_settings"
}
func init() {
db.RegisterModel(new(NotificationSettings))
}
// GetUserNotificationSettings returns a user's fine-grained notification preference
func GetUserNotificationSettings(ctx context.Context, userID int64) (*NotificationSettings, error) {
settings := &NotificationSettings{}
if has, err := db.GetEngine(ctx).Where("user_id=?", userID).Get(settings); err != nil {
return nil, err
} else if !has {
return nil, nil
}
user, err := GetUserByID(ctx, userID)
if err != nil {
return nil, err
}
settings.User = user
return settings, nil
}
func UpdateUserNotificationSettings(ctx context.Context, settings *NotificationSettings) error {
_, err := db.GetEngine(ctx).Where("user_id = ?", settings.UserID).
Update(&NotificationSettings{
Actions: settings.Actions,
})
return err
}

View File

@ -1,43 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func TestUserNotificationSettings(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
settings, err := GetUserNotificationSettings(db.DefaultContext, 1)
assert.NoError(t, err)
assert.NotNil(t, settings.User)
assert.Equal(t, settings.User.ID, settings.UserID)
assert.Equal(t, NotificationGiteaActionsFailureOnly, settings.Actions)
assert.NoError(t, UpdateUserNotificationSettings(db.DefaultContext, &NotificationSettings{
UserID: 1,
Actions: NotificationGiteaActionsAll,
}))
settings, err = GetUserNotificationSettings(db.DefaultContext, 1)
assert.NoError(t, err)
assert.NotNil(t, settings.User)
assert.Equal(t, settings.User.ID, settings.UserID)
assert.Equal(t, NotificationGiteaActionsAll, settings.Actions)
assert.NoError(t, UpdateUserNotificationSettings(db.DefaultContext, &NotificationSettings{
UserID: 1,
Actions: NotificationGiteaActionsDisabled,
}))
settings, err = GetUserNotificationSettings(db.DefaultContext, 1)
assert.NoError(t, err)
assert.NotNil(t, settings.User)
assert.Equal(t, settings.User.ID, settings.UserID)
assert.Equal(t, NotificationGiteaActionsDisabled, settings.Actions)
}

View File

@ -30,12 +30,18 @@ func Notifications(ctx *context.Context) {
ctx.Data["PageIsSettingsNotifications"] = true
ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference
fineGrainedPreference, err := user_model.GetUserNotificationSettings(ctx, ctx.Doer.ID)
fineGrainedPreference, err := user_model.GetSettings(ctx, ctx.Doer.ID, []string{
user_model.SettingsEmailNotificationGiteaActions,
})
if err != nil {
ctx.ServerError("GetUserNotificationSettings", err)
return
}
ctx.Data["ActionsEmailNotificationsPreference"] = fineGrainedPreference.Actions
actionsNotify := fineGrainedPreference[user_model.SettingsEmailNotificationGiteaActions].SettingValue
if actionsNotify == "" {
actionsNotify = user_model.EmailNotificationGiteaActionsFailureOnly
}
ctx.Data["ActionsEmailNotificationsPreference"] = actionsNotify
ctx.HTML(http.StatusOK, tplSettingsNotifications)
}
@ -77,19 +83,16 @@ func NotificationsActionsEmailPost(ctx *context.Context) {
}
preference := ctx.FormString("preference")
if !(preference == user_model.NotificationGiteaActionsAll ||
preference == user_model.NotificationGiteaActionsDisabled ||
preference == user_model.NotificationGiteaActionsFailureOnly) {
if !(preference == user_model.EmailNotificationGiteaActionsAll ||
preference == user_model.EmailNotificationGiteaActionsDisabled ||
preference == user_model.EmailNotificationGiteaActionsFailureOnly) {
log.Error("Actions Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name)
ctx.ServerError("NotificationsActionsEmailPost", errors.New("option unrecognized"))
return
}
opts := &user.UpdateNotificationSettingsOptions{
Actions: optional.Some(preference),
}
if err := user.UpdateNotificationSettings(ctx, ctx.Doer.ID, opts); err != nil {
if err := user_model.SetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsEmailNotificationGiteaActions, preference); err != nil {
log.Error("Cannot set actions email notifications preference: %v", err)
ctx.ServerError("UpdateNotificationSettings", err)
ctx.ServerError("SetUserSetting", err)
return
}
log.Trace("Actions email notifications preference made %s: %s", preference, ctx.Doer.Name)

View File

@ -149,18 +149,19 @@ func SendActionsWorkflowRunStatusEmail(ctx context.Context, sender *user_model.U
recipients := make([]*user_model.User, 0)
if !sender.IsGiteaActions() && !sender.IsGhost() && sender.IsMailable() {
notifyPref, err := user_model.GetUserNotificationSettings(ctx, sender.ID)
notifyPref, err := user_model.GetUserSetting(ctx, sender.ID,
user_model.SettingsEmailNotificationGiteaActions, user_model.EmailNotificationGiteaActionsFailureOnly)
if err != nil {
log.Error("GetUserNotificationSettings: %v", err)
log.Error("GetUserSetting: %v", err)
return
}
if run.Status.IsSuccess() {
if notifyPref.Actions == user_model.NotificationGiteaActionsAll {
if notifyPref == user_model.EmailNotificationGiteaActionsAll {
recipients = append(recipients, sender)
}
sendActionsWorkflowRunStatusEmail(ctx, repo, run, sender, recipients)
return
} else if notifyPref.Actions != user_model.NotificationGiteaActionsDisabled {
} else if notifyPref != user_model.EmailNotificationGiteaActionsDisabled {
recipients = append(recipients, sender)
}
}

View File

@ -95,7 +95,6 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
&user_model.Blocking{BlockerID: u.ID},
&user_model.Blocking{BlockeeID: u.ID},
&actions_model.ActionRunnerToken{OwnerID: u.ID},
&user_model.NotificationSettings{UserID: u.ID},
); err != nil {
return fmt.Errorf("deleteBeans: %w", err)
}

View File

@ -244,18 +244,3 @@ func UpdateAuth(ctx context.Context, u *user_model.User, opts *UpdateAuthOptions
}
return nil
}
type UpdateNotificationSettingsOptions struct {
Actions optional.Option[string]
}
func UpdateNotificationSettings(ctx context.Context, userID int64, opts *UpdateNotificationSettingsOptions) error {
settings := &user_model.NotificationSettings{
UserID: userID,
}
if opts.Actions.Has() {
settings.Actions = opts.Actions.Value()
}
return user_model.UpdateUserNotificationSettings(ctx, settings)
}

View File

@ -122,30 +122,3 @@ func TestUpdateAuth(t *testing.T) {
Password: optional.Some("aaaa"),
}), password_module.ErrMinLength)
}
func TestUpdateNotificationSettings(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
settings := &user_model.NotificationSettings{UserID: 2}
exists, err := db.GetEngine(db.DefaultContext).Get(settings)
assert.NoError(t, err)
assert.True(t, exists)
assert.NoError(t, UpdateNotificationSettings(db.DefaultContext, settings.UserID, &UpdateNotificationSettingsOptions{
Actions: optional.Some(user_model.NotificationGiteaActionsAll),
}))
settings, err = user_model.GetUserNotificationSettings(db.DefaultContext, settings.UserID)
assert.NoError(t, err)
assert.NotNil(t, settings)
assert.Equal(t, user_model.NotificationGiteaActionsAll, settings.Actions)
assert.NotEqual(t, user_model.NotificationGiteaActionsFailureOnly, settings.Actions)
assert.NoError(t, UpdateNotificationSettings(db.DefaultContext, settings.UserID, &UpdateNotificationSettingsOptions{
Actions: optional.Some(user_model.NotificationGiteaActionsDisabled),
}))
settings, err = user_model.GetUserNotificationSettings(db.DefaultContext, settings.UserID)
assert.NoError(t, err)
assert.NotNil(t, settings)
assert.Equal(t, user_model.NotificationGiteaActionsDisabled, settings.Actions)
assert.NotEqual(t, user_model.NotificationGiteaActionsFailureOnly, settings.Actions)
}