From fed3ade25446d6a0938e92f7d1fb18d70255631a Mon Sep 17 00:00:00 2001 From: NorthRealm <155140859+NorthRealm@users.noreply.github.com> Date: Sat, 12 Jul 2025 02:04:39 +0800 Subject: [PATCH] rewrite --- .../fixtures/user_notification_settings.yml | 84 ------------------- models/migrations/migrations.go | 1 - models/migrations/v1_24/v321.go | 59 ------------- models/user/email_notification.go | 11 +++ models/user/email_notification_test.go | 34 ++++++++ models/user/setting_keys.go | 2 + models/user/user.go | 5 +- models/user/user_notification.go | 55 ------------ models/user/user_notification_test.go | 43 ---------- routers/web/user/setting/notifications.go | 23 ++--- services/mailer/mail_workflow_run.go | 9 +- services/user/delete.go | 1 - services/user/update.go | 15 ---- services/user/update_test.go | 27 ------ 14 files changed, 66 insertions(+), 303 deletions(-) delete mode 100644 models/fixtures/user_notification_settings.yml delete mode 100644 models/migrations/v1_24/v321.go create mode 100644 models/user/email_notification.go create mode 100644 models/user/email_notification_test.go delete mode 100644 models/user/user_notification.go delete mode 100644 models/user/user_notification_test.go diff --git a/models/fixtures/user_notification_settings.yml b/models/fixtures/user_notification_settings.yml deleted file mode 100644 index 57df18f513..0000000000 --- a/models/fixtures/user_notification_settings.yml +++ /dev/null @@ -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 diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 46659bf247..176372486e 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -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 } diff --git a/models/migrations/v1_24/v321.go b/models/migrations/v1_24/v321.go deleted file mode 100644 index 430b6c1adc..0000000000 --- a/models/migrations/v1_24/v321.go +++ /dev/null @@ -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 -} diff --git a/models/user/email_notification.go b/models/user/email_notification.go new file mode 100644 index 0000000000..c6faa26909 --- /dev/null +++ b/models/user/email_notification.go @@ -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" +) diff --git a/models/user/email_notification_test.go b/models/user/email_notification_test.go new file mode 100644 index 0000000000..a099e10b24 --- /dev/null +++ b/models/user/email_notification_test.go @@ -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) +} diff --git a/models/user/setting_keys.go b/models/user/setting_keys.go index 2c2ed078be..1505c8aa35 100644 --- a/models/user/setting_keys.go +++ b/models/user/setting_keys.go @@ -21,4 +21,6 @@ const ( SignupUserAgent = "signup.user_agent" SettingsKeyCodeViewShowFileTree = "code_view.show_file_tree" + + SettingsEmailNotificationGiteaActions = "email_notifications.actions" ) diff --git a/models/user/user.go b/models/user/user.go index c70fe258e4..f8d74924b5 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -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 } diff --git a/models/user/user_notification.go b/models/user/user_notification.go deleted file mode 100644 index 2a13c6cc9c..0000000000 --- a/models/user/user_notification.go +++ /dev/null @@ -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 -} diff --git a/models/user/user_notification_test.go b/models/user/user_notification_test.go deleted file mode 100644 index b81119287e..0000000000 --- a/models/user/user_notification_test.go +++ /dev/null @@ -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) -} diff --git a/routers/web/user/setting/notifications.go b/routers/web/user/setting/notifications.go index 3f42b2d414..dd81a862d3 100644 --- a/routers/web/user/setting/notifications.go +++ b/routers/web/user/setting/notifications.go @@ -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) diff --git a/services/mailer/mail_workflow_run.go b/services/mailer/mail_workflow_run.go index c91292841a..169de71a52 100644 --- a/services/mailer/mail_workflow_run.go +++ b/services/mailer/mail_workflow_run.go @@ -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) } } diff --git a/services/user/delete.go b/services/user/delete.go index 49f8403c7e..39c6ef052d 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -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) } diff --git a/services/user/update.go b/services/user/update.go index 4fcebbc44c..d7354542bf 100644 --- a/services/user/update.go +++ b/services/user/update.go @@ -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) -} diff --git a/services/user/update_test.go b/services/user/update_test.go index e7fca20920..27513e8040 100644 --- a/services/user/update_test.go +++ b/services/user/update_test.go @@ -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) -}