mirror of https://github.com/gogs/gogs.git
refactor(db): migrate off `user_email.go` to `users.go` (#7452)
parent
9ac93067f6
commit
0721ef2399
|
@ -30,12 +30,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
go-version: 1.20.x
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
|
@ -52,6 +51,11 @@ jobs:
|
|||
echo "Run 'go mod tidy' or 'task generate' commit them"
|
||||
exit 1
|
||||
fi
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
|
||||
test:
|
||||
name: Test
|
||||
|
|
|
@ -317,6 +317,7 @@ delete_email = Delete
|
|||
email_deletion = Email Deletion
|
||||
email_deletion_desc = Deleting this email address will remove related information from your account. Do you want to continue?
|
||||
email_deletion_success = Email has been deleted successfully!
|
||||
email_deletion_primary = Cannot delete primary email address.
|
||||
add_new_email = Add new email address
|
||||
add_email = Add Email
|
||||
add_email_confirmation_sent = A new confirmation email has been sent to '%s', please check your inbox within the next %d hours to complete the confirmation process.
|
||||
|
|
|
@ -55,6 +55,22 @@ Indexes:
|
|||
"idx_action_user_id" (user_id)
|
||||
```
|
||||
|
||||
# Table "email_address"
|
||||
|
||||
```
|
||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||
--------------+--------------+--------------------------------+--------------------------------+---------------------------------
|
||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||
UserID | uid | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||
Email | email | VARCHAR(254) NOT NULL | VARCHAR(254) NOT NULL | TEXT NOT NULL
|
||||
IsActivated | is_activated | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
||||
|
||||
Primary keys: id
|
||||
Indexes:
|
||||
"email_address_user_email_unique" UNIQUE (uid, email)
|
||||
"idx_email_address_user_id" (uid)
|
||||
```
|
||||
|
||||
# Table "follow"
|
||||
|
||||
```
|
||||
|
|
|
@ -31,8 +31,9 @@ func TestDumpAndImport(t *testing.T) {
|
|||
}
|
||||
t.Parallel()
|
||||
|
||||
if len(Tables) != 6 {
|
||||
t.Fatalf("New table has added (want 6 got %d), please add new tests for the table and update this check", len(Tables))
|
||||
const wantTables = 7
|
||||
if len(Tables) != wantTables {
|
||||
t.Fatalf("New table has added (want %d got %d), please add new tests for the table and update this check", wantTables, len(Tables))
|
||||
}
|
||||
|
||||
db := dbtest.NewDB(t, "dumpAndImport", Tables...)
|
||||
|
@ -131,6 +132,19 @@ func setupDBToDump(t *testing.T, db *gorm.DB) {
|
|||
CreatedUnix: 1588568886,
|
||||
},
|
||||
|
||||
&EmailAddress{
|
||||
ID: 1,
|
||||
UserID: 1,
|
||||
Email: "alice@example.com",
|
||||
IsActivated: false,
|
||||
},
|
||||
&EmailAddress{
|
||||
ID: 2,
|
||||
UserID: 2,
|
||||
Email: "bob@example.com",
|
||||
IsActivated: true,
|
||||
},
|
||||
|
||||
&Follow{
|
||||
ID: 1,
|
||||
UserID: 1,
|
||||
|
|
|
@ -42,6 +42,7 @@ func newLogWriter() (logger.Writer, error) {
|
|||
// NOTE: Lines are sorted in alphabetical order, each letter in its own line.
|
||||
var Tables = []any{
|
||||
new(Access), new(AccessToken), new(Action),
|
||||
new(EmailAddress),
|
||||
new(Follow),
|
||||
new(LFSObject), new(LoginSource),
|
||||
}
|
||||
|
@ -121,7 +122,6 @@ func Init(w logger.Writer) (*gorm.DB, error) {
|
|||
// Initialize stores, sorted in alphabetical order.
|
||||
AccessTokens = &accessTokens{DB: db}
|
||||
Actions = NewActionsStore(db)
|
||||
EmailAddresses = NewEmailAddressesStore(db)
|
||||
LoginSources = &loginSources{DB: db, files: sourceFiles}
|
||||
LFS = &lfs{DB: db}
|
||||
Orgs = NewOrgsStore(db)
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright 2022 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
)
|
||||
|
||||
// EmailAddressesStore is the persistent interface for email addresses.
|
||||
type EmailAddressesStore interface {
|
||||
// GetByEmail returns the email address with given email. If `needsActivated` is
|
||||
// true, only activated email will be returned, otherwise, it may return
|
||||
// inactivated email addresses. It returns ErrEmailNotExist when no qualified
|
||||
// email is not found.
|
||||
GetByEmail(ctx context.Context, email string, needsActivated bool) (*EmailAddress, error)
|
||||
}
|
||||
|
||||
var EmailAddresses EmailAddressesStore
|
||||
|
||||
var _ EmailAddressesStore = (*emailAddresses)(nil)
|
||||
|
||||
type emailAddresses struct {
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
// NewEmailAddressesStore returns a persistent interface for email addresses
|
||||
// with given database connection.
|
||||
func NewEmailAddressesStore(db *gorm.DB) EmailAddressesStore {
|
||||
return &emailAddresses{DB: db}
|
||||
}
|
||||
|
||||
var _ errutil.NotFound = (*ErrEmailNotExist)(nil)
|
||||
|
||||
type ErrEmailNotExist struct {
|
||||
args errutil.Args
|
||||
}
|
||||
|
||||
// IsErrEmailAddressNotExist returns true if the underlying error has the type
|
||||
// ErrEmailNotExist.
|
||||
func IsErrEmailAddressNotExist(err error) bool {
|
||||
_, ok := errors.Cause(err).(ErrEmailNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrEmailNotExist) Error() string {
|
||||
return fmt.Sprintf("email address does not exist: %v", err.args)
|
||||
}
|
||||
|
||||
func (ErrEmailNotExist) NotFound() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (db *emailAddresses) GetByEmail(ctx context.Context, email string, needsActivated bool) (*EmailAddress, error) {
|
||||
tx := db.WithContext(ctx).Where("email = ?", email)
|
||||
if needsActivated {
|
||||
tx = tx.Where("is_activated = ?", true)
|
||||
}
|
||||
|
||||
emailAddress := new(EmailAddress)
|
||||
err := tx.First(emailAddress).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, ErrEmailNotExist{
|
||||
args: errutil.Args{
|
||||
"email": email,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return emailAddress, nil
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2022 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"gogs.io/gogs/internal/dbtest"
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
)
|
||||
|
||||
func TestEmailAddresses(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip()
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
tables := []any{new(EmailAddress)}
|
||||
db := &emailAddresses{
|
||||
DB: dbtest.NewDB(t, "emailAddresses", tables...),
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
test func(t *testing.T, db *emailAddresses)
|
||||
}{
|
||||
{"GetByEmail", emailAddressesGetByEmail},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
err := clearTables(t, db.DB, tables...)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
tc.test(t, db)
|
||||
})
|
||||
if t.Failed() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func emailAddressesGetByEmail(t *testing.T, db *emailAddresses) {
|
||||
ctx := context.Background()
|
||||
|
||||
const testEmail = "alice@example.com"
|
||||
_, err := db.GetByEmail(ctx, testEmail, false)
|
||||
wantErr := ErrEmailNotExist{
|
||||
args: errutil.Args{
|
||||
"email": testEmail,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, wantErr, err)
|
||||
|
||||
// TODO: Use EmailAddresses.Create to replace SQL hack when the method is available.
|
||||
err = db.Exec(`INSERT INTO email_address (uid, email, is_activated) VALUES (1, ?, FALSE)`, testEmail).Error
|
||||
require.NoError(t, err)
|
||||
got, err := db.GetByEmail(ctx, testEmail, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testEmail, got.Email)
|
||||
|
||||
// Should not return if we only want activated emails
|
||||
_, err = db.GetByEmail(ctx, testEmail, true)
|
||||
assert.Equal(t, wantErr, err)
|
||||
|
||||
// TODO: Use EmailAddresses.MarkActivated to replace SQL hack when the method is available.
|
||||
err = db.Exec(`UPDATE email_address SET is_activated = TRUE WHERE email = ?`, testEmail).Error
|
||||
require.NoError(t, err)
|
||||
got, err = db.GetByEmail(ctx, testEmail, true)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testEmail, got.Email)
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type EmailNotFound struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
func IsEmailNotFound(err error) bool {
|
||||
_, ok := err.(EmailNotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err EmailNotFound) Error() string {
|
||||
return fmt.Sprintf("email is not found [email: %s]", err.Email)
|
||||
}
|
||||
|
||||
type EmailNotVerified struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
func IsEmailNotVerified(err error) bool {
|
||||
_, ok := err.(EmailNotVerified)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err EmailNotVerified) Error() string {
|
||||
return fmt.Sprintf("email has not been verified [email: %s]", err.Email)
|
||||
}
|
|
@ -58,7 +58,7 @@ func init() {
|
|||
new(Mirror), new(Release), new(Webhook), new(HookTask),
|
||||
new(ProtectBranch), new(ProtectBranchWhitelist),
|
||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
||||
new(Notice), new(EmailAddress))
|
||||
new(Notice))
|
||||
|
||||
gonicNames := []string{"SSL"}
|
||||
for _, name := range gonicNames {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
{"ID":1,"UserID":1,"Email":"alice@example.com","IsActivated":false}
|
||||
{"ID":2,"UserID":2,"Email":"bob@example.com","IsActivated":true}
|
|
@ -1,199 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gogs.io/gogs/internal/db/errors"
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
)
|
||||
|
||||
// EmailAddresses is the list of all email addresses of a user. Can contain the
|
||||
// primary email address, but is not obligatory.
|
||||
type EmailAddress struct {
|
||||
ID int64 `gorm:"primaryKey"`
|
||||
UserID int64 `xorm:"uid INDEX NOT NULL" gorm:"column:uid;index;not null"`
|
||||
Email string `xorm:"UNIQUE NOT NULL" gorm:"unique;not null"`
|
||||
IsActivated bool `gorm:"not null;default:FALSE"`
|
||||
IsPrimary bool `xorm:"-" gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// GetEmailAddresses returns all email addresses belongs to given user.
|
||||
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
|
||||
emails := make([]*EmailAddress, 0, 5)
|
||||
if err := x.Where("uid=?", uid).Find(&emails); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := Users.GetByID(context.TODO(), uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isPrimaryFound := false
|
||||
for _, email := range emails {
|
||||
if email.Email == u.Email {
|
||||
isPrimaryFound = true
|
||||
email.IsPrimary = true
|
||||
} else {
|
||||
email.IsPrimary = false
|
||||
}
|
||||
}
|
||||
|
||||
// We always want the primary email address displayed, even if it's not in
|
||||
// the emailaddress table (yet).
|
||||
if !isPrimaryFound {
|
||||
emails = append(emails, &EmailAddress{
|
||||
Email: u.Email,
|
||||
IsActivated: true,
|
||||
IsPrimary: true,
|
||||
})
|
||||
}
|
||||
return emails, nil
|
||||
}
|
||||
|
||||
func isEmailUsed(e Engine, email string) (bool, error) {
|
||||
if email == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
has, err := e.Get(&EmailAddress{Email: email})
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if has {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// We need to check primary email of users as well.
|
||||
return e.Where("type=?", UserTypeIndividual).And("email=?", email).Get(new(User))
|
||||
}
|
||||
|
||||
// IsEmailUsed returns true if the email has been used.
|
||||
func IsEmailUsed(email string) (bool, error) {
|
||||
return isEmailUsed(x, email)
|
||||
}
|
||||
|
||||
func addEmailAddress(e Engine, email *EmailAddress) error {
|
||||
email.Email = strings.ToLower(strings.TrimSpace(email.Email))
|
||||
used, err := isEmailUsed(e, email.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{args: errutil.Args{"email": email.Email}}
|
||||
}
|
||||
|
||||
_, err = e.Insert(email)
|
||||
return err
|
||||
}
|
||||
|
||||
func AddEmailAddress(email *EmailAddress) error {
|
||||
return addEmailAddress(x, email)
|
||||
}
|
||||
|
||||
func AddEmailAddresses(emails []*EmailAddress) error {
|
||||
if len(emails) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if any of them has been used
|
||||
for i := range emails {
|
||||
emails[i].Email = strings.ToLower(strings.TrimSpace(emails[i].Email))
|
||||
used, err := IsEmailUsed(emails[i].Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{args: errutil.Args{"email": emails[i].Email}}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := x.Insert(emails); err != nil {
|
||||
return fmt.Errorf("Insert: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (email *EmailAddress) Activate() error {
|
||||
email.IsActivated = true
|
||||
if _, err := x.ID(email.ID).AllCols().Update(email); err != nil {
|
||||
return err
|
||||
}
|
||||
return Users.Update(context.TODO(), email.UserID, UpdateUserOptions{GenerateNewRands: true})
|
||||
}
|
||||
|
||||
func DeleteEmailAddress(email *EmailAddress) (err error) {
|
||||
if email.ID > 0 {
|
||||
_, err = x.Id(email.ID).Delete(new(EmailAddress))
|
||||
} else {
|
||||
_, err = x.Where("email=?", email.Email).Delete(new(EmailAddress))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteEmailAddresses(emails []*EmailAddress) (err error) {
|
||||
for i := range emails {
|
||||
if err = DeleteEmailAddress(emails[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeEmailPrimary(userID int64, email *EmailAddress) error {
|
||||
has, err := x.Get(email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return errors.EmailNotFound{Email: email.Email}
|
||||
}
|
||||
|
||||
if email.UserID != userID {
|
||||
return errors.New("not the owner of the email")
|
||||
}
|
||||
|
||||
if !email.IsActivated {
|
||||
return errors.EmailNotVerified{Email: email.Email}
|
||||
}
|
||||
|
||||
user := &User{ID: email.UserID}
|
||||
has, err = x.Get(user)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrUserNotExist{args: map[string]any{"userID": email.UserID}}
|
||||
}
|
||||
|
||||
// Make sure the former primary email doesn't disappear.
|
||||
formerPrimaryEmail := &EmailAddress{Email: user.Email}
|
||||
has, err = x.Get(formerPrimaryEmail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !has {
|
||||
formerPrimaryEmail.UserID = user.ID
|
||||
formerPrimaryEmail.IsActivated = user.IsActive
|
||||
if _, err = sess.Insert(formerPrimaryEmail); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
user.Email = email.Email
|
||||
if _, err = sess.ID(user.ID).AllCols().Update(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
|
@ -49,7 +49,7 @@ type UsersStore interface {
|
|||
// Create creates a new user and persists to database. It returns
|
||||
// ErrNameNotAllowed if the given name or pattern of the name is not allowed as
|
||||
// a username, or ErrUserAlreadyExist when a user with same name already exists,
|
||||
// or ErrEmailAlreadyUsed if the email has been used by another user.
|
||||
// or ErrEmailAlreadyUsed if the email has been verified by another user.
|
||||
Create(ctx context.Context, username, email string, opts CreateUserOptions) (*User, error)
|
||||
|
||||
// GetByEmail returns the user (not organization) with given email. It ignores
|
||||
|
@ -101,6 +101,27 @@ type UsersStore interface {
|
|||
// DeleteInactivated deletes all inactivated users.
|
||||
DeleteInactivated() error
|
||||
|
||||
// AddEmail adds a new email address to given user. It returns
|
||||
// ErrEmailAlreadyUsed if the email has been verified by another user.
|
||||
AddEmail(ctx context.Context, userID int64, email string, isActivated bool) error
|
||||
// GetEmail returns the email address of the given user. If `needsActivated` is
|
||||
// true, only activated email will be returned, otherwise, it may return
|
||||
// inactivated email addresses. It returns ErrEmailNotExist when no qualified
|
||||
// email is not found.
|
||||
GetEmail(ctx context.Context, userID int64, email string, needsActivated bool) (*EmailAddress, error)
|
||||
// ListEmails returns all email addresses of the given user. It always includes
|
||||
// a primary email address.
|
||||
ListEmails(ctx context.Context, userID int64) ([]*EmailAddress, error)
|
||||
// MarkEmailActivated marks the email address of the given user as activated,
|
||||
// and new rands are generated for the user.
|
||||
MarkEmailActivated(ctx context.Context, userID int64, email string) error
|
||||
// MarkEmailPrimary marks the email address of the given user as primary. It
|
||||
// returns ErrEmailNotExist when the email is not found for the user, and
|
||||
// ErrEmailNotActivated when the email is not activated.
|
||||
MarkEmailPrimary(ctx context.Context, userID int64, email string) error
|
||||
// DeleteEmail deletes the email address of the given user.
|
||||
DeleteEmail(ctx context.Context, userID int64, email string) error
|
||||
|
||||
// Follow marks the user to follow the other user.
|
||||
Follow(ctx context.Context, userID, followID int64) error
|
||||
// Unfollow removes the mark the user to follow the other user.
|
||||
|
@ -386,7 +407,7 @@ func (db *users) Create(ctx context.Context, username, email string, opts Create
|
|||
}
|
||||
}
|
||||
|
||||
email = strings.ToLower(email)
|
||||
email = strings.ToLower(strings.TrimSpace(email))
|
||||
_, err = db.GetByEmail(ctx, email)
|
||||
if err == nil {
|
||||
return nil, ErrEmailAlreadyUsed{
|
||||
|
@ -1061,6 +1082,183 @@ func (db *users) UseCustomAvatar(ctx context.Context, userID int64, avatar []byt
|
|||
Error
|
||||
}
|
||||
|
||||
func (db *users) AddEmail(ctx context.Context, userID int64, email string, isActivated bool) error {
|
||||
email = strings.ToLower(strings.TrimSpace(email))
|
||||
_, err := db.GetByEmail(ctx, email)
|
||||
if err == nil {
|
||||
return ErrEmailAlreadyUsed{
|
||||
args: errutil.Args{
|
||||
"email": email,
|
||||
},
|
||||
}
|
||||
} else if !IsErrUserNotExist(err) {
|
||||
return errors.Wrap(err, "check user by email")
|
||||
}
|
||||
|
||||
return db.WithContext(ctx).Create(
|
||||
&EmailAddress{
|
||||
UserID: userID,
|
||||
Email: email,
|
||||
IsActivated: isActivated,
|
||||
},
|
||||
).Error
|
||||
}
|
||||
|
||||
var _ errutil.NotFound = (*ErrEmailNotExist)(nil)
|
||||
|
||||
type ErrEmailNotExist struct {
|
||||
args errutil.Args
|
||||
}
|
||||
|
||||
// IsErrEmailAddressNotExist returns true if the underlying error has the type
|
||||
// ErrEmailNotExist.
|
||||
func IsErrEmailAddressNotExist(err error) bool {
|
||||
_, ok := errors.Cause(err).(ErrEmailNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrEmailNotExist) Error() string {
|
||||
return fmt.Sprintf("email address does not exist: %v", err.args)
|
||||
}
|
||||
|
||||
func (ErrEmailNotExist) NotFound() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (db *users) GetEmail(ctx context.Context, userID int64, email string, needsActivated bool) (*EmailAddress, error) {
|
||||
tx := db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email)
|
||||
if needsActivated {
|
||||
tx = tx.Where("is_activated = ?", true)
|
||||
}
|
||||
|
||||
emailAddress := new(EmailAddress)
|
||||
err := tx.First(emailAddress).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, ErrEmailNotExist{
|
||||
args: errutil.Args{
|
||||
"email": email,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return emailAddress, nil
|
||||
}
|
||||
|
||||
func (db *users) ListEmails(ctx context.Context, userID int64) ([]*EmailAddress, error) {
|
||||
user, err := db.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get user")
|
||||
}
|
||||
|
||||
var emails []*EmailAddress
|
||||
err = db.WithContext(ctx).Where("uid = ?", userID).Order("id ASC").Find(&emails).Error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "list emails")
|
||||
}
|
||||
|
||||
isPrimaryFound := false
|
||||
for _, email := range emails {
|
||||
if email.Email == user.Email {
|
||||
isPrimaryFound = true
|
||||
email.IsPrimary = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// We always want the primary email address displayed, even if it's not in the
|
||||
// email_address table yet.
|
||||
if !isPrimaryFound {
|
||||
emails = append(emails, &EmailAddress{
|
||||
Email: user.Email,
|
||||
IsActivated: user.IsActive,
|
||||
IsPrimary: true,
|
||||
})
|
||||
}
|
||||
return emails, nil
|
||||
}
|
||||
|
||||
func (db *users) MarkEmailActivated(ctx context.Context, userID int64, email string) error {
|
||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
err := db.WithContext(ctx).
|
||||
Model(&EmailAddress{}).
|
||||
Where("uid = ? AND email = ?", userID, email).
|
||||
Update("is_activated", true).
|
||||
Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "mark email activated")
|
||||
}
|
||||
|
||||
return NewUsersStore(tx).Update(ctx, userID, UpdateUserOptions{GenerateNewRands: true})
|
||||
})
|
||||
}
|
||||
|
||||
type ErrEmailNotVerified struct {
|
||||
args errutil.Args
|
||||
}
|
||||
|
||||
// IsErrEmailNotVerified returns true if the underlying error has the type
|
||||
// ErrEmailNotVerified.
|
||||
func IsErrEmailNotVerified(err error) bool {
|
||||
_, ok := errors.Cause(err).(ErrEmailNotVerified)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrEmailNotVerified) Error() string {
|
||||
return fmt.Sprintf("email has not been verified: %v", err.args)
|
||||
}
|
||||
|
||||
func (db *users) MarkEmailPrimary(ctx context.Context, userID int64, email string) error {
|
||||
var emailAddress EmailAddress
|
||||
err := db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email).First(&emailAddress).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return ErrEmailNotExist{args: errutil.Args{"email": email}}
|
||||
}
|
||||
return errors.Wrap(err, "get email address")
|
||||
}
|
||||
|
||||
if !emailAddress.IsActivated {
|
||||
return ErrEmailNotVerified{args: errutil.Args{"email": email}}
|
||||
}
|
||||
|
||||
user, err := db.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get user")
|
||||
}
|
||||
|
||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// Make sure the former primary email doesn't disappear.
|
||||
err = tx.FirstOrCreate(
|
||||
&EmailAddress{
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
IsActivated: user.IsActive,
|
||||
},
|
||||
&EmailAddress{
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "upsert former primary email address")
|
||||
}
|
||||
|
||||
return tx.Model(&User{}).
|
||||
Where("id = ?", user.ID).
|
||||
Updates(map[string]any{
|
||||
"email": email,
|
||||
"updated_unix": tx.NowFunc().Unix(),
|
||||
},
|
||||
).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (db *users) DeleteEmail(ctx context.Context, userID int64, email string) error {
|
||||
return db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email).Delete(&EmailAddress{}).Error
|
||||
}
|
||||
|
||||
// UserType indicates the type of the user account.
|
||||
type UserType int
|
||||
|
||||
|
@ -1422,6 +1620,15 @@ func isUsernameAllowed(name string) error {
|
|||
return isNameAllowed(reservedUsernames, reservedUsernamePatterns, name)
|
||||
}
|
||||
|
||||
// EmailAddress is an email address of a user.
|
||||
type EmailAddress struct {
|
||||
ID int64 `gorm:"primaryKey"`
|
||||
UserID int64 `xorm:"uid INDEX NOT NULL" gorm:"column:uid;index;uniqueIndex:email_address_user_email_unique;not null"`
|
||||
Email string `xorm:"UNIQUE NOT NULL" gorm:"uniqueIndex:email_address_user_email_unique;not null;size:254"`
|
||||
IsActivated bool `gorm:"not null;default:FALSE"`
|
||||
IsPrimary bool `xorm:"-" gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// Follow represents relations of users and their followers.
|
||||
type Follow struct {
|
||||
ID int64 `gorm:"primaryKey"`
|
||||
|
|
|
@ -116,6 +116,12 @@ func TestUsers(t *testing.T) {
|
|||
{"SearchByName", usersSearchByName},
|
||||
{"Update", usersUpdate},
|
||||
{"UseCustomAvatar", usersUseCustomAvatar},
|
||||
{"AddEmail", usersAddEmail},
|
||||
{"GetEmail", usersGetEmail},
|
||||
{"ListEmails", usersListEmails},
|
||||
{"MarkEmailActivated", usersMarkEmailActivated},
|
||||
{"MarkEmailPrimary", usersMarkEmailPrimary},
|
||||
{"DeleteEmail", usersDeleteEmail},
|
||||
{"Follow", usersFollow},
|
||||
{"IsFollowing", usersIsFollowing},
|
||||
{"Unfollow", usersUnfollow},
|
||||
|
@ -1100,7 +1106,19 @@ func usersUpdate(t *testing.T, db *users) {
|
|||
})
|
||||
|
||||
t.Run("update email but already used", func(t *testing.T) {
|
||||
// todo
|
||||
bob, err := db.Create(
|
||||
ctx,
|
||||
"bob",
|
||||
"bob@example.com",
|
||||
CreateUserOptions{
|
||||
Activated: true,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
got := db.Update(ctx, alice.ID, UpdateUserOptions{Email: &bob.Email})
|
||||
want := ErrEmailAlreadyUsed{args: errutil.Args{"email": bob.Email}}
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
|
||||
loginSource := int64(1)
|
||||
|
@ -1204,6 +1222,161 @@ func TestIsUsernameAllowed(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func usersAddEmail(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("multiple users can add the same unverified email", func(t *testing.T) {
|
||||
alice, err := db.Create(ctx, "alice", "unverified@example.com", CreateUserOptions{})
|
||||
require.NoError(t, err)
|
||||
err = db.AddEmail(ctx, alice.ID+1, "unverified@example.com", false)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("only one user can add the same verified email", func(t *testing.T) {
|
||||
bob, err := db.Create(ctx, "bob", "verified@example.com", CreateUserOptions{Activated: true})
|
||||
require.NoError(t, err)
|
||||
got := db.AddEmail(ctx, bob.ID+1, "verified@example.com", true)
|
||||
want := ErrEmailAlreadyUsed{args: errutil.Args{"email": "verified@example.com"}}
|
||||
require.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
|
||||
func usersGetEmail(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
|
||||
const testUserID = 1
|
||||
const testEmail = "alice@example.com"
|
||||
_, err := db.GetEmail(ctx, testUserID, testEmail, false)
|
||||
wantErr := ErrEmailNotExist{
|
||||
args: errutil.Args{
|
||||
"email": testEmail,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, wantErr, err)
|
||||
|
||||
err = db.AddEmail(ctx, testUserID, testEmail, false)
|
||||
require.NoError(t, err)
|
||||
got, err := db.GetEmail(ctx, testUserID, testEmail, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testEmail, got.Email)
|
||||
|
||||
// Should not return if we ask for a different user
|
||||
_, err = db.GetEmail(ctx, testUserID+1, testEmail, false)
|
||||
assert.Equal(t, wantErr, err)
|
||||
|
||||
// Should not return if we only want activated emails
|
||||
_, err = db.GetEmail(ctx, testUserID, testEmail, true)
|
||||
assert.Equal(t, wantErr, err)
|
||||
|
||||
err = db.MarkEmailActivated(ctx, testUserID, testEmail)
|
||||
require.NoError(t, err)
|
||||
got, err = db.GetEmail(ctx, testUserID, testEmail, true)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testEmail, got.Email)
|
||||
}
|
||||
|
||||
func usersListEmails(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("list emails with primary email", func(t *testing.T) {
|
||||
alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||
require.NoError(t, err)
|
||||
err = db.AddEmail(ctx, alice.ID, "alice2@example.com", true)
|
||||
require.NoError(t, err)
|
||||
err = db.MarkEmailPrimary(ctx, alice.ID, "alice2@example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
emails, err := db.ListEmails(ctx, alice.ID)
|
||||
require.NoError(t, err)
|
||||
got := make([]string, 0, len(emails))
|
||||
for _, email := range emails {
|
||||
got = append(got, email.Email)
|
||||
}
|
||||
want := []string{"alice2@example.com", "alice@example.com"}
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
|
||||
t.Run("list emails without primary email", func(t *testing.T) {
|
||||
bob, err := db.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
|
||||
require.NoError(t, err)
|
||||
err = db.AddEmail(ctx, bob.ID, "bob2@example.com", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
emails, err := db.ListEmails(ctx, bob.ID)
|
||||
require.NoError(t, err)
|
||||
got := make([]string, 0, len(emails))
|
||||
for _, email := range emails {
|
||||
got = append(got, email.Email)
|
||||
}
|
||||
want := []string{"bob2@example.com", "bob@example.com"}
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
|
||||
func usersMarkEmailActivated(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
|
||||
alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.AddEmail(ctx, alice.ID, "alice2@example.com", false)
|
||||
require.NoError(t, err)
|
||||
err = db.MarkEmailActivated(ctx, alice.ID, "alice2@example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
gotEmail, err := db.GetEmail(ctx, alice.ID, "alice2@example.com", true)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, gotEmail.IsActivated)
|
||||
|
||||
gotAlice, err := db.GetByID(ctx, alice.ID)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, alice.Rands, gotAlice.Rands)
|
||||
}
|
||||
|
||||
func usersMarkEmailPrimary(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||
require.NoError(t, err)
|
||||
err = db.AddEmail(ctx, alice.ID, "alice2@example.com", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should fail because email not verified
|
||||
gotError := db.MarkEmailPrimary(ctx, alice.ID, "alice2@example.com")
|
||||
wantError := ErrEmailNotVerified{args: errutil.Args{"email": "alice2@example.com"}}
|
||||
assert.Equal(t, wantError, gotError)
|
||||
|
||||
// Mark email as verified and should succeed
|
||||
err = db.MarkEmailActivated(ctx, alice.ID, "alice2@example.com")
|
||||
require.NoError(t, err)
|
||||
err = db.MarkEmailPrimary(ctx, alice.ID, "alice2@example.com")
|
||||
require.NoError(t, err)
|
||||
gotAlice, err := db.GetByID(ctx, alice.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "alice2@example.com", gotAlice.Email)
|
||||
|
||||
// Former primary email should be preserved
|
||||
gotEmail, err := db.GetEmail(ctx, alice.ID, "alice@example.com", false)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, gotEmail.IsActivated)
|
||||
}
|
||||
|
||||
func usersDeleteEmail(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.AddEmail(ctx, alice.ID, "alice2@example.com", false)
|
||||
require.NoError(t, err)
|
||||
_, err = db.GetEmail(ctx, alice.ID, "alice2@example.com", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.DeleteEmail(ctx, alice.ID, "alice2@example.com")
|
||||
require.NoError(t, err)
|
||||
_, got := db.GetEmail(ctx, alice.ID, "alice2@example.com", false)
|
||||
want := ErrEmailNotExist{args: errutil.Args{"email": "alice2@example.com"}}
|
||||
require.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func usersFollow(t *testing.T, db *users) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
)
|
||||
|
||||
func ListEmails(c *context.APIContext) {
|
||||
emails, err := db.GetEmailAddresses(c.User.ID)
|
||||
emails, err := db.Users.ListEmails(c.Req.Context(), c.User.ID)
|
||||
if err != nil {
|
||||
c.Error(err, "get email addresses")
|
||||
return
|
||||
|
@ -35,48 +35,40 @@ func AddEmail(c *context.APIContext, form api.CreateEmailOption) {
|
|||
return
|
||||
}
|
||||
|
||||
emails := make([]*db.EmailAddress, len(form.Emails))
|
||||
for i := range form.Emails {
|
||||
emails[i] = &db.EmailAddress{
|
||||
UserID: c.User.ID,
|
||||
Email: form.Emails[i],
|
||||
IsActivated: !conf.Auth.RequireEmailConfirmation,
|
||||
apiEmails := make([]*api.Email, 0, len(form.Emails))
|
||||
for _, email := range form.Emails {
|
||||
err := db.Users.AddEmail(c.Req.Context(), c.User.ID, email, !conf.Auth.RequireEmailConfirmation)
|
||||
if err != nil {
|
||||
if db.IsErrEmailAlreadyUsed(err) {
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.Errorf("email address has been used: %s", err.(db.ErrEmailAlreadyUsed).Email()))
|
||||
} else {
|
||||
c.Error(err, "add email addresses")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.AddEmailAddresses(emails); err != nil {
|
||||
if db.IsErrEmailAlreadyUsed(err) {
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("email address has been used: "+err.(db.ErrEmailAlreadyUsed).Email()))
|
||||
} else {
|
||||
c.Error(err, "add email addresses")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
apiEmails := make([]*api.Email, len(emails))
|
||||
for i := range emails {
|
||||
apiEmails[i] = convert.ToEmail(emails[i])
|
||||
apiEmails = append(apiEmails,
|
||||
&api.Email{
|
||||
Email: email,
|
||||
Verified: !conf.Auth.RequireEmailConfirmation,
|
||||
},
|
||||
)
|
||||
}
|
||||
c.JSON(http.StatusCreated, &apiEmails)
|
||||
}
|
||||
|
||||
func DeleteEmail(c *context.APIContext, form api.CreateEmailOption) {
|
||||
if len(form.Emails) == 0 {
|
||||
c.NoContent()
|
||||
return
|
||||
}
|
||||
|
||||
emails := make([]*db.EmailAddress, len(form.Emails))
|
||||
for i := range form.Emails {
|
||||
emails[i] = &db.EmailAddress{
|
||||
UserID: c.User.ID,
|
||||
Email: form.Emails[i],
|
||||
for _, email := range form.Emails {
|
||||
if email == c.User.Email {
|
||||
c.ErrorStatus(http.StatusBadRequest, errors.Errorf("cannot delete primary email %q", email))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.DeleteEmailAddresses(emails); err != nil {
|
||||
c.Error(err, "delete email addresses")
|
||||
return
|
||||
err := db.Users.DeleteEmail(c.Req.Context(), c.User.ID, email)
|
||||
if err != nil {
|
||||
c.Error(err, "delete email addresses")
|
||||
return
|
||||
}
|
||||
}
|
||||
c.NoContent()
|
||||
}
|
||||
|
|
|
@ -3171,6 +3171,9 @@ func (c TwoFactorsStoreIsEnabledFuncCall) Results() []interface{} {
|
|||
// MockUsersStore is a mock implementation of the UsersStore interface (from
|
||||
// the package gogs.io/gogs/internal/db) used for unit testing.
|
||||
type MockUsersStore struct {
|
||||
// AddEmailFunc is an instance of a mock function object controlling the
|
||||
// behavior of the method AddEmail.
|
||||
AddEmailFunc *UsersStoreAddEmailFunc
|
||||
// AuthenticateFunc is an instance of a mock function object controlling
|
||||
// the behavior of the method Authenticate.
|
||||
AuthenticateFunc *UsersStoreAuthenticateFunc
|
||||
|
@ -3189,6 +3192,9 @@ type MockUsersStore struct {
|
|||
// DeleteCustomAvatarFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method DeleteCustomAvatar.
|
||||
DeleteCustomAvatarFunc *UsersStoreDeleteCustomAvatarFunc
|
||||
// DeleteEmailFunc is an instance of a mock function object controlling
|
||||
// the behavior of the method DeleteEmail.
|
||||
DeleteEmailFunc *UsersStoreDeleteEmailFunc
|
||||
// DeleteInactivatedFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method DeleteInactivated.
|
||||
DeleteInactivatedFunc *UsersStoreDeleteInactivatedFunc
|
||||
|
@ -3207,6 +3213,9 @@ type MockUsersStore struct {
|
|||
// GetByUsernameFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method GetByUsername.
|
||||
GetByUsernameFunc *UsersStoreGetByUsernameFunc
|
||||
// GetEmailFunc is an instance of a mock function object controlling the
|
||||
// behavior of the method GetEmail.
|
||||
GetEmailFunc *UsersStoreGetEmailFunc
|
||||
// GetMailableEmailsByUsernamesFunc is an instance of a mock function
|
||||
// object controlling the behavior of the method
|
||||
// GetMailableEmailsByUsernames.
|
||||
|
@ -3220,12 +3229,21 @@ type MockUsersStore struct {
|
|||
// ListFunc is an instance of a mock function object controlling the
|
||||
// behavior of the method List.
|
||||
ListFunc *UsersStoreListFunc
|
||||
// ListEmailsFunc is an instance of a mock function object controlling
|
||||
// the behavior of the method ListEmails.
|
||||
ListEmailsFunc *UsersStoreListEmailsFunc
|
||||
// ListFollowersFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method ListFollowers.
|
||||
ListFollowersFunc *UsersStoreListFollowersFunc
|
||||
// ListFollowingsFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method ListFollowings.
|
||||
ListFollowingsFunc *UsersStoreListFollowingsFunc
|
||||
// MarkEmailActivatedFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method MarkEmailActivated.
|
||||
MarkEmailActivatedFunc *UsersStoreMarkEmailActivatedFunc
|
||||
// MarkEmailPrimaryFunc is an instance of a mock function object
|
||||
// controlling the behavior of the method MarkEmailPrimary.
|
||||
MarkEmailPrimaryFunc *UsersStoreMarkEmailPrimaryFunc
|
||||
// SearchByNameFunc is an instance of a mock function object controlling
|
||||
// the behavior of the method SearchByName.
|
||||
SearchByNameFunc *UsersStoreSearchByNameFunc
|
||||
|
@ -3244,6 +3262,11 @@ type MockUsersStore struct {
|
|||
// methods return zero values for all results, unless overwritten.
|
||||
func NewMockUsersStore() *MockUsersStore {
|
||||
return &MockUsersStore{
|
||||
AddEmailFunc: &UsersStoreAddEmailFunc{
|
||||
defaultHook: func(context.Context, int64, string, bool) (r0 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
||||
defaultHook: func(context.Context, string, string, int64) (r0 *db.User, r1 error) {
|
||||
return
|
||||
|
@ -3274,6 +3297,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||
return
|
||||
},
|
||||
},
|
||||
DeleteEmailFunc: &UsersStoreDeleteEmailFunc{
|
||||
defaultHook: func(context.Context, int64, string) (r0 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
||||
defaultHook: func() (r0 error) {
|
||||
return
|
||||
|
@ -3304,6 +3332,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||
return
|
||||
},
|
||||
},
|
||||
GetEmailFunc: &UsersStoreGetEmailFunc{
|
||||
defaultHook: func(context.Context, int64, string, bool) (r0 *db.EmailAddress, r1 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
||||
defaultHook: func(context.Context, []string) (r0 []string, r1 error) {
|
||||
return
|
||||
|
@ -3324,6 +3357,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||
return
|
||||
},
|
||||
},
|
||||
ListEmailsFunc: &UsersStoreListEmailsFunc{
|
||||
defaultHook: func(context.Context, int64) (r0 []*db.EmailAddress, r1 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
||||
defaultHook: func(context.Context, int64, int, int) (r0 []*db.User, r1 error) {
|
||||
return
|
||||
|
@ -3334,6 +3372,16 @@ func NewMockUsersStore() *MockUsersStore {
|
|||
return
|
||||
},
|
||||
},
|
||||
MarkEmailActivatedFunc: &UsersStoreMarkEmailActivatedFunc{
|
||||
defaultHook: func(context.Context, int64, string) (r0 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
MarkEmailPrimaryFunc: &UsersStoreMarkEmailPrimaryFunc{
|
||||
defaultHook: func(context.Context, int64, string) (r0 error) {
|
||||
return
|
||||
},
|
||||
},
|
||||
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
||||
defaultHook: func(context.Context, string, int, int, string) (r0 []*db.User, r1 int64, r2 error) {
|
||||
return
|
||||
|
@ -3361,6 +3409,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||
// All methods panic on invocation, unless overwritten.
|
||||
func NewStrictMockUsersStore() *MockUsersStore {
|
||||
return &MockUsersStore{
|
||||
AddEmailFunc: &UsersStoreAddEmailFunc{
|
||||
defaultHook: func(context.Context, int64, string, bool) error {
|
||||
panic("unexpected invocation of MockUsersStore.AddEmail")
|
||||
},
|
||||
},
|
||||
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
||||
defaultHook: func(context.Context, string, string, int64) (*db.User, error) {
|
||||
panic("unexpected invocation of MockUsersStore.Authenticate")
|
||||
|
@ -3391,6 +3444,11 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||
panic("unexpected invocation of MockUsersStore.DeleteCustomAvatar")
|
||||
},
|
||||
},
|
||||
DeleteEmailFunc: &UsersStoreDeleteEmailFunc{
|
||||
defaultHook: func(context.Context, int64, string) error {
|
||||
panic("unexpected invocation of MockUsersStore.DeleteEmail")
|
||||
},
|
||||
},
|
||||
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
||||
defaultHook: func() error {
|
||||
panic("unexpected invocation of MockUsersStore.DeleteInactivated")
|
||||
|
@ -3421,6 +3479,11 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||
panic("unexpected invocation of MockUsersStore.GetByUsername")
|
||||
},
|
||||
},
|
||||
GetEmailFunc: &UsersStoreGetEmailFunc{
|
||||
defaultHook: func(context.Context, int64, string, bool) (*db.EmailAddress, error) {
|
||||
panic("unexpected invocation of MockUsersStore.GetEmail")
|
||||
},
|
||||
},
|
||||
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
||||
defaultHook: func(context.Context, []string) ([]string, error) {
|
||||
panic("unexpected invocation of MockUsersStore.GetMailableEmailsByUsernames")
|
||||
|
@ -3441,6 +3504,11 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||
panic("unexpected invocation of MockUsersStore.List")
|
||||
},
|
||||
},
|
||||
ListEmailsFunc: &UsersStoreListEmailsFunc{
|
||||
defaultHook: func(context.Context, int64) ([]*db.EmailAddress, error) {
|
||||
panic("unexpected invocation of MockUsersStore.ListEmails")
|
||||
},
|
||||
},
|
||||
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
||||
defaultHook: func(context.Context, int64, int, int) ([]*db.User, error) {
|
||||
panic("unexpected invocation of MockUsersStore.ListFollowers")
|
||||
|
@ -3451,6 +3519,16 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||
panic("unexpected invocation of MockUsersStore.ListFollowings")
|
||||
},
|
||||
},
|
||||
MarkEmailActivatedFunc: &UsersStoreMarkEmailActivatedFunc{
|
||||
defaultHook: func(context.Context, int64, string) error {
|
||||
panic("unexpected invocation of MockUsersStore.MarkEmailActivated")
|
||||
},
|
||||
},
|
||||
MarkEmailPrimaryFunc: &UsersStoreMarkEmailPrimaryFunc{
|
||||
defaultHook: func(context.Context, int64, string) error {
|
||||
panic("unexpected invocation of MockUsersStore.MarkEmailPrimary")
|
||||
},
|
||||
},
|
||||
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
||||
defaultHook: func(context.Context, string, int, int, string) ([]*db.User, int64, error) {
|
||||
panic("unexpected invocation of MockUsersStore.SearchByName")
|
||||
|
@ -3478,6 +3556,9 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||
// All methods delegate to the given implementation, unless overwritten.
|
||||
func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
||||
return &MockUsersStore{
|
||||
AddEmailFunc: &UsersStoreAddEmailFunc{
|
||||
defaultHook: i.AddEmail,
|
||||
},
|
||||
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
||||
defaultHook: i.Authenticate,
|
||||
},
|
||||
|
@ -3496,6 +3577,9 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||
DeleteCustomAvatarFunc: &UsersStoreDeleteCustomAvatarFunc{
|
||||
defaultHook: i.DeleteCustomAvatar,
|
||||
},
|
||||
DeleteEmailFunc: &UsersStoreDeleteEmailFunc{
|
||||
defaultHook: i.DeleteEmail,
|
||||
},
|
||||
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
||||
defaultHook: i.DeleteInactivated,
|
||||
},
|
||||
|
@ -3514,6 +3598,9 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||
GetByUsernameFunc: &UsersStoreGetByUsernameFunc{
|
||||
defaultHook: i.GetByUsername,
|
||||
},
|
||||
GetEmailFunc: &UsersStoreGetEmailFunc{
|
||||
defaultHook: i.GetEmail,
|
||||
},
|
||||
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
||||
defaultHook: i.GetMailableEmailsByUsernames,
|
||||
},
|
||||
|
@ -3526,12 +3613,21 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||
ListFunc: &UsersStoreListFunc{
|
||||
defaultHook: i.List,
|
||||
},
|
||||
ListEmailsFunc: &UsersStoreListEmailsFunc{
|
||||
defaultHook: i.ListEmails,
|
||||
},
|
||||
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
||||
defaultHook: i.ListFollowers,
|
||||
},
|
||||
ListFollowingsFunc: &UsersStoreListFollowingsFunc{
|
||||
defaultHook: i.ListFollowings,
|
||||
},
|
||||
MarkEmailActivatedFunc: &UsersStoreMarkEmailActivatedFunc{
|
||||
defaultHook: i.MarkEmailActivated,
|
||||
},
|
||||
MarkEmailPrimaryFunc: &UsersStoreMarkEmailPrimaryFunc{
|
||||
defaultHook: i.MarkEmailPrimary,
|
||||
},
|
||||
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
||||
defaultHook: i.SearchByName,
|
||||
},
|
||||
|
@ -3547,6 +3643,117 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||
}
|
||||
}
|
||||
|
||||
// UsersStoreAddEmailFunc describes the behavior when the AddEmail method of
|
||||
// the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreAddEmailFunc struct {
|
||||
defaultHook func(context.Context, int64, string, bool) error
|
||||
hooks []func(context.Context, int64, string, bool) error
|
||||
history []UsersStoreAddEmailFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// AddEmail delegates to the next hook function in the queue and stores the
|
||||
// parameter and result values of this invocation.
|
||||
func (m *MockUsersStore) AddEmail(v0 context.Context, v1 int64, v2 string, v3 bool) error {
|
||||
r0 := m.AddEmailFunc.nextHook()(v0, v1, v2, v3)
|
||||
m.AddEmailFunc.appendCall(UsersStoreAddEmailFuncCall{v0, v1, v2, v3, r0})
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the AddEmail method of
|
||||
// the parent MockUsersStore instance is invoked and the hook queue is
|
||||
// empty.
|
||||
func (f *UsersStoreAddEmailFunc) SetDefaultHook(hook func(context.Context, int64, string, bool) error) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// AddEmail method of the parent MockUsersStore instance invokes the hook at
|
||||
// the front of the queue and discards it. After the queue is empty, the
|
||||
// default hook function is invoked for any future action.
|
||||
func (f *UsersStoreAddEmailFunc) PushHook(hook func(context.Context, int64, string, bool) error) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *UsersStoreAddEmailFunc) SetDefaultReturn(r0 error) {
|
||||
f.SetDefaultHook(func(context.Context, int64, string, bool) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *UsersStoreAddEmailFunc) PushReturn(r0 error) {
|
||||
f.PushHook(func(context.Context, int64, string, bool) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
func (f *UsersStoreAddEmailFunc) nextHook() func(context.Context, int64, string, bool) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *UsersStoreAddEmailFunc) appendCall(r0 UsersStoreAddEmailFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of UsersStoreAddEmailFuncCall objects
|
||||
// describing the invocations of this function.
|
||||
func (f *UsersStoreAddEmailFunc) History() []UsersStoreAddEmailFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]UsersStoreAddEmailFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// UsersStoreAddEmailFuncCall is an object that describes an invocation of
|
||||
// method AddEmail on an instance of MockUsersStore.
|
||||
type UsersStoreAddEmailFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 int64
|
||||
// Arg2 is the value of the 3rd argument passed to this method
|
||||
// invocation.
|
||||
Arg2 string
|
||||
// Arg3 is the value of the 4th argument passed to this method
|
||||
// invocation.
|
||||
Arg3 bool
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c UsersStoreAddEmailFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c UsersStoreAddEmailFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0}
|
||||
}
|
||||
|
||||
// UsersStoreAuthenticateFunc describes the behavior when the Authenticate
|
||||
// method of the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreAuthenticateFunc struct {
|
||||
|
@ -4197,6 +4404,114 @@ func (c UsersStoreDeleteCustomAvatarFuncCall) Results() []interface{} {
|
|||
return []interface{}{c.Result0}
|
||||
}
|
||||
|
||||
// UsersStoreDeleteEmailFunc describes the behavior when the DeleteEmail
|
||||
// method of the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreDeleteEmailFunc struct {
|
||||
defaultHook func(context.Context, int64, string) error
|
||||
hooks []func(context.Context, int64, string) error
|
||||
history []UsersStoreDeleteEmailFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// DeleteEmail delegates to the next hook function in the queue and stores
|
||||
// the parameter and result values of this invocation.
|
||||
func (m *MockUsersStore) DeleteEmail(v0 context.Context, v1 int64, v2 string) error {
|
||||
r0 := m.DeleteEmailFunc.nextHook()(v0, v1, v2)
|
||||
m.DeleteEmailFunc.appendCall(UsersStoreDeleteEmailFuncCall{v0, v1, v2, r0})
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the DeleteEmail method
|
||||
// of the parent MockUsersStore instance is invoked and the hook queue is
|
||||
// empty.
|
||||
func (f *UsersStoreDeleteEmailFunc) SetDefaultHook(hook func(context.Context, int64, string) error) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// DeleteEmail method of the parent MockUsersStore instance invokes the hook
|
||||
// at the front of the queue and discards it. After the queue is empty, the
|
||||
// default hook function is invoked for any future action.
|
||||
func (f *UsersStoreDeleteEmailFunc) PushHook(hook func(context.Context, int64, string) error) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *UsersStoreDeleteEmailFunc) SetDefaultReturn(r0 error) {
|
||||
f.SetDefaultHook(func(context.Context, int64, string) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *UsersStoreDeleteEmailFunc) PushReturn(r0 error) {
|
||||
f.PushHook(func(context.Context, int64, string) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
func (f *UsersStoreDeleteEmailFunc) nextHook() func(context.Context, int64, string) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *UsersStoreDeleteEmailFunc) appendCall(r0 UsersStoreDeleteEmailFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of UsersStoreDeleteEmailFuncCall objects
|
||||
// describing the invocations of this function.
|
||||
func (f *UsersStoreDeleteEmailFunc) History() []UsersStoreDeleteEmailFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]UsersStoreDeleteEmailFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// UsersStoreDeleteEmailFuncCall is an object that describes an invocation
|
||||
// of method DeleteEmail on an instance of MockUsersStore.
|
||||
type UsersStoreDeleteEmailFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 int64
|
||||
// Arg2 is the value of the 3rd argument passed to this method
|
||||
// invocation.
|
||||
Arg2 string
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c UsersStoreDeleteEmailFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1, c.Arg2}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c UsersStoreDeleteEmailFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0}
|
||||
}
|
||||
|
||||
// UsersStoreDeleteInactivatedFunc describes the behavior when the
|
||||
// DeleteInactivated method of the parent MockUsersStore instance is
|
||||
// invoked.
|
||||
|
@ -4836,6 +5151,120 @@ func (c UsersStoreGetByUsernameFuncCall) Results() []interface{} {
|
|||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// UsersStoreGetEmailFunc describes the behavior when the GetEmail method of
|
||||
// the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreGetEmailFunc struct {
|
||||
defaultHook func(context.Context, int64, string, bool) (*db.EmailAddress, error)
|
||||
hooks []func(context.Context, int64, string, bool) (*db.EmailAddress, error)
|
||||
history []UsersStoreGetEmailFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// GetEmail delegates to the next hook function in the queue and stores the
|
||||
// parameter and result values of this invocation.
|
||||
func (m *MockUsersStore) GetEmail(v0 context.Context, v1 int64, v2 string, v3 bool) (*db.EmailAddress, error) {
|
||||
r0, r1 := m.GetEmailFunc.nextHook()(v0, v1, v2, v3)
|
||||
m.GetEmailFunc.appendCall(UsersStoreGetEmailFuncCall{v0, v1, v2, v3, r0, r1})
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the GetEmail method of
|
||||
// the parent MockUsersStore instance is invoked and the hook queue is
|
||||
// empty.
|
||||
func (f *UsersStoreGetEmailFunc) SetDefaultHook(hook func(context.Context, int64, string, bool) (*db.EmailAddress, error)) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// GetEmail method of the parent MockUsersStore instance invokes the hook at
|
||||
// the front of the queue and discards it. After the queue is empty, the
|
||||
// default hook function is invoked for any future action.
|
||||
func (f *UsersStoreGetEmailFunc) PushHook(hook func(context.Context, int64, string, bool) (*db.EmailAddress, error)) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *UsersStoreGetEmailFunc) SetDefaultReturn(r0 *db.EmailAddress, r1 error) {
|
||||
f.SetDefaultHook(func(context.Context, int64, string, bool) (*db.EmailAddress, error) {
|
||||
return r0, r1
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *UsersStoreGetEmailFunc) PushReturn(r0 *db.EmailAddress, r1 error) {
|
||||
f.PushHook(func(context.Context, int64, string, bool) (*db.EmailAddress, error) {
|
||||
return r0, r1
|
||||
})
|
||||
}
|
||||
|
||||
func (f *UsersStoreGetEmailFunc) nextHook() func(context.Context, int64, string, bool) (*db.EmailAddress, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *UsersStoreGetEmailFunc) appendCall(r0 UsersStoreGetEmailFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of UsersStoreGetEmailFuncCall objects
|
||||
// describing the invocations of this function.
|
||||
func (f *UsersStoreGetEmailFunc) History() []UsersStoreGetEmailFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]UsersStoreGetEmailFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// UsersStoreGetEmailFuncCall is an object that describes an invocation of
|
||||
// method GetEmail on an instance of MockUsersStore.
|
||||
type UsersStoreGetEmailFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 int64
|
||||
// Arg2 is the value of the 3rd argument passed to this method
|
||||
// invocation.
|
||||
Arg2 string
|
||||
// Arg3 is the value of the 4th argument passed to this method
|
||||
// invocation.
|
||||
Arg3 bool
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 *db.EmailAddress
|
||||
// Result1 is the value of the 2nd result returned from this method
|
||||
// invocation.
|
||||
Result1 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c UsersStoreGetEmailFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c UsersStoreGetEmailFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// UsersStoreGetMailableEmailsByUsernamesFunc describes the behavior when
|
||||
// the GetMailableEmailsByUsernames method of the parent MockUsersStore
|
||||
// instance is invoked.
|
||||
|
@ -5274,6 +5703,114 @@ func (c UsersStoreListFuncCall) Results() []interface{} {
|
|||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// UsersStoreListEmailsFunc describes the behavior when the ListEmails
|
||||
// method of the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreListEmailsFunc struct {
|
||||
defaultHook func(context.Context, int64) ([]*db.EmailAddress, error)
|
||||
hooks []func(context.Context, int64) ([]*db.EmailAddress, error)
|
||||
history []UsersStoreListEmailsFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// ListEmails delegates to the next hook function in the queue and stores
|
||||
// the parameter and result values of this invocation.
|
||||
func (m *MockUsersStore) ListEmails(v0 context.Context, v1 int64) ([]*db.EmailAddress, error) {
|
||||
r0, r1 := m.ListEmailsFunc.nextHook()(v0, v1)
|
||||
m.ListEmailsFunc.appendCall(UsersStoreListEmailsFuncCall{v0, v1, r0, r1})
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the ListEmails method of
|
||||
// the parent MockUsersStore instance is invoked and the hook queue is
|
||||
// empty.
|
||||
func (f *UsersStoreListEmailsFunc) SetDefaultHook(hook func(context.Context, int64) ([]*db.EmailAddress, error)) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// ListEmails method of the parent MockUsersStore instance invokes the hook
|
||||
// at the front of the queue and discards it. After the queue is empty, the
|
||||
// default hook function is invoked for any future action.
|
||||
func (f *UsersStoreListEmailsFunc) PushHook(hook func(context.Context, int64) ([]*db.EmailAddress, error)) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *UsersStoreListEmailsFunc) SetDefaultReturn(r0 []*db.EmailAddress, r1 error) {
|
||||
f.SetDefaultHook(func(context.Context, int64) ([]*db.EmailAddress, error) {
|
||||
return r0, r1
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *UsersStoreListEmailsFunc) PushReturn(r0 []*db.EmailAddress, r1 error) {
|
||||
f.PushHook(func(context.Context, int64) ([]*db.EmailAddress, error) {
|
||||
return r0, r1
|
||||
})
|
||||
}
|
||||
|
||||
func (f *UsersStoreListEmailsFunc) nextHook() func(context.Context, int64) ([]*db.EmailAddress, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *UsersStoreListEmailsFunc) appendCall(r0 UsersStoreListEmailsFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of UsersStoreListEmailsFuncCall objects
|
||||
// describing the invocations of this function.
|
||||
func (f *UsersStoreListEmailsFunc) History() []UsersStoreListEmailsFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]UsersStoreListEmailsFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// UsersStoreListEmailsFuncCall is an object that describes an invocation of
|
||||
// method ListEmails on an instance of MockUsersStore.
|
||||
type UsersStoreListEmailsFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 int64
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 []*db.EmailAddress
|
||||
// Result1 is the value of the 2nd result returned from this method
|
||||
// invocation.
|
||||
Result1 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c UsersStoreListEmailsFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c UsersStoreListEmailsFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// UsersStoreListFollowersFunc describes the behavior when the ListFollowers
|
||||
// method of the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreListFollowersFunc struct {
|
||||
|
@ -5502,6 +6039,223 @@ func (c UsersStoreListFollowingsFuncCall) Results() []interface{} {
|
|||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// UsersStoreMarkEmailActivatedFunc describes the behavior when the
|
||||
// MarkEmailActivated method of the parent MockUsersStore instance is
|
||||
// invoked.
|
||||
type UsersStoreMarkEmailActivatedFunc struct {
|
||||
defaultHook func(context.Context, int64, string) error
|
||||
hooks []func(context.Context, int64, string) error
|
||||
history []UsersStoreMarkEmailActivatedFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// MarkEmailActivated delegates to the next hook function in the queue and
|
||||
// stores the parameter and result values of this invocation.
|
||||
func (m *MockUsersStore) MarkEmailActivated(v0 context.Context, v1 int64, v2 string) error {
|
||||
r0 := m.MarkEmailActivatedFunc.nextHook()(v0, v1, v2)
|
||||
m.MarkEmailActivatedFunc.appendCall(UsersStoreMarkEmailActivatedFuncCall{v0, v1, v2, r0})
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the MarkEmailActivated
|
||||
// method of the parent MockUsersStore instance is invoked and the hook
|
||||
// queue is empty.
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) SetDefaultHook(hook func(context.Context, int64, string) error) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// MarkEmailActivated method of the parent MockUsersStore instance invokes
|
||||
// the hook at the front of the queue and discards it. After the queue is
|
||||
// empty, the default hook function is invoked for any future action.
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) PushHook(hook func(context.Context, int64, string) error) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) SetDefaultReturn(r0 error) {
|
||||
f.SetDefaultHook(func(context.Context, int64, string) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) PushReturn(r0 error) {
|
||||
f.PushHook(func(context.Context, int64, string) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) nextHook() func(context.Context, int64, string) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) appendCall(r0 UsersStoreMarkEmailActivatedFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of UsersStoreMarkEmailActivatedFuncCall
|
||||
// objects describing the invocations of this function.
|
||||
func (f *UsersStoreMarkEmailActivatedFunc) History() []UsersStoreMarkEmailActivatedFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]UsersStoreMarkEmailActivatedFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// UsersStoreMarkEmailActivatedFuncCall is an object that describes an
|
||||
// invocation of method MarkEmailActivated on an instance of MockUsersStore.
|
||||
type UsersStoreMarkEmailActivatedFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 int64
|
||||
// Arg2 is the value of the 3rd argument passed to this method
|
||||
// invocation.
|
||||
Arg2 string
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c UsersStoreMarkEmailActivatedFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1, c.Arg2}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c UsersStoreMarkEmailActivatedFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0}
|
||||
}
|
||||
|
||||
// UsersStoreMarkEmailPrimaryFunc describes the behavior when the
|
||||
// MarkEmailPrimary method of the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreMarkEmailPrimaryFunc struct {
|
||||
defaultHook func(context.Context, int64, string) error
|
||||
hooks []func(context.Context, int64, string) error
|
||||
history []UsersStoreMarkEmailPrimaryFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// MarkEmailPrimary delegates to the next hook function in the queue and
|
||||
// stores the parameter and result values of this invocation.
|
||||
func (m *MockUsersStore) MarkEmailPrimary(v0 context.Context, v1 int64, v2 string) error {
|
||||
r0 := m.MarkEmailPrimaryFunc.nextHook()(v0, v1, v2)
|
||||
m.MarkEmailPrimaryFunc.appendCall(UsersStoreMarkEmailPrimaryFuncCall{v0, v1, v2, r0})
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the MarkEmailPrimary
|
||||
// method of the parent MockUsersStore instance is invoked and the hook
|
||||
// queue is empty.
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) SetDefaultHook(hook func(context.Context, int64, string) error) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// MarkEmailPrimary method of the parent MockUsersStore instance invokes the
|
||||
// hook at the front of the queue and discards it. After the queue is empty,
|
||||
// the default hook function is invoked for any future action.
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) PushHook(hook func(context.Context, int64, string) error) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultHook with a function that returns the
|
||||
// given values.
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) SetDefaultReturn(r0 error) {
|
||||
f.SetDefaultHook(func(context.Context, int64, string) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushHook with a function that returns the given values.
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) PushReturn(r0 error) {
|
||||
f.PushHook(func(context.Context, int64, string) error {
|
||||
return r0
|
||||
})
|
||||
}
|
||||
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) nextHook() func(context.Context, int64, string) error {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) appendCall(r0 UsersStoreMarkEmailPrimaryFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of UsersStoreMarkEmailPrimaryFuncCall objects
|
||||
// describing the invocations of this function.
|
||||
func (f *UsersStoreMarkEmailPrimaryFunc) History() []UsersStoreMarkEmailPrimaryFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]UsersStoreMarkEmailPrimaryFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// UsersStoreMarkEmailPrimaryFuncCall is an object that describes an
|
||||
// invocation of method MarkEmailPrimary on an instance of MockUsersStore.
|
||||
type UsersStoreMarkEmailPrimaryFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 int64
|
||||
// Arg2 is the value of the 3rd argument passed to this method
|
||||
// invocation.
|
||||
Arg2 string
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c UsersStoreMarkEmailPrimaryFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1, c.Arg2}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c UsersStoreMarkEmailPrimaryFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0}
|
||||
}
|
||||
|
||||
// UsersStoreSearchByNameFunc describes the behavior when the SearchByName
|
||||
// method of the parent MockUsersStore instance is invoked.
|
||||
type UsersStoreSearchByNameFunc struct {
|
||||
|
|
|
@ -445,7 +445,7 @@ func verifyActiveEmailCode(code, email string) *db.EmailAddress {
|
|||
data := com.ToStr(user.ID) + email + user.LowerName + user.Password + user.Rands
|
||||
|
||||
if tool.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
emailAddress, err := db.EmailAddresses.GetByEmail(gocontext.TODO(), email, false)
|
||||
emailAddress, err := db.Users.GetEmail(gocontext.TODO(), user.ID, email, false)
|
||||
if err == nil {
|
||||
return emailAddress
|
||||
}
|
||||
|
@ -515,8 +515,10 @@ func ActivateEmail(c *context.Context) {
|
|||
|
||||
// Verify code.
|
||||
if email := verifyActiveEmailCode(code, emailAddr); email != nil {
|
||||
if err := email.Activate(); err != nil {
|
||||
err := db.Users.MarkEmailActivated(c.Req.Context(), email.UserID, email.Email)
|
||||
if err != nil {
|
||||
c.Error(err, "activate email")
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Email activated: %s", email.Email)
|
||||
|
|
|
@ -223,7 +223,7 @@ func SettingsEmails(c *context.Context) {
|
|||
c.Title("settings.emails")
|
||||
c.PageIs("SettingsEmails")
|
||||
|
||||
emails, err := db.GetEmailAddresses(c.User.ID)
|
||||
emails, err := db.Users.ListEmails(c.Req.Context(), c.User.ID)
|
||||
if err != nil {
|
||||
c.Errorf(err, "get email addresses")
|
||||
return
|
||||
|
@ -237,9 +237,9 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||
c.Title("settings.emails")
|
||||
c.PageIs("SettingsEmails")
|
||||
|
||||
// Make emailaddress primary.
|
||||
if c.Query("_method") == "PRIMARY" {
|
||||
if err := db.MakeEmailPrimary(c.UserID(), &db.EmailAddress{ID: c.QueryInt64("id")}); err != nil {
|
||||
err := db.Users.MarkEmailPrimary(c.Req.Context(), c.User.ID, c.Query("email"))
|
||||
if err != nil {
|
||||
c.Errorf(err, "make email primary")
|
||||
return
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||
}
|
||||
|
||||
// Add Email address.
|
||||
emails, err := db.GetEmailAddresses(c.User.ID)
|
||||
emails, err := db.Users.ListEmails(c.Req.Context(), c.User.ID)
|
||||
if err != nil {
|
||||
c.Errorf(err, "get email addresses")
|
||||
return
|
||||
|
@ -261,12 +261,8 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||
return
|
||||
}
|
||||
|
||||
emailAddr := &db.EmailAddress{
|
||||
UserID: c.User.ID,
|
||||
Email: f.Email,
|
||||
IsActivated: !conf.Auth.RequireEmailConfirmation,
|
||||
}
|
||||
if err := db.AddEmailAddress(emailAddr); err != nil {
|
||||
err = db.Users.AddEmail(c.Req.Context(), c.User.ID, f.Email, !conf.Auth.RequireEmailConfirmation)
|
||||
if err != nil {
|
||||
if db.IsErrEmailAlreadyUsed(err) {
|
||||
c.RenderWithErr(c.Tr("form.email_been_used"), SETTINGS_EMAILS, &f)
|
||||
} else {
|
||||
|
@ -277,12 +273,12 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||
|
||||
// Send confirmation email
|
||||
if conf.Auth.RequireEmailConfirmation {
|
||||
email.SendActivateEmailMail(c.Context, db.NewMailerUser(c.User), emailAddr.Email)
|
||||
email.SendActivateEmailMail(c.Context, db.NewMailerUser(c.User), f.Email)
|
||||
|
||||
if err := c.Cache.Put("MailResendLimit_"+c.User.LowerName, c.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache 'MailResendLimit' failed: %v", err)
|
||||
}
|
||||
c.Flash.Info(c.Tr("settings.add_email_confirmation_sent", emailAddr.Email, conf.Auth.ActivateCodeLives/60))
|
||||
c.Flash.Info(c.Tr("settings.add_email_confirmation_sent", f.Email, conf.Auth.ActivateCodeLives/60))
|
||||
} else {
|
||||
c.Flash.Success(c.Tr("settings.add_email_success"))
|
||||
}
|
||||
|
@ -291,11 +287,18 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||
}
|
||||
|
||||
func DeleteEmail(c *context.Context) {
|
||||
if err := db.DeleteEmailAddress(&db.EmailAddress{
|
||||
ID: c.QueryInt64("id"),
|
||||
UserID: c.User.ID,
|
||||
}); err != nil {
|
||||
c.Errorf(err, "delete email address")
|
||||
email := c.Query("id") // The "id" here is the actual email address
|
||||
if c.User.Email == email {
|
||||
c.Flash.Error(c.Tr("settings.email_deletion_primary"))
|
||||
c.JSONSuccess(map[string]any{
|
||||
"redirect": conf.Server.Subpath + "/user/settings/email",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err := db.Users.DeleteEmail(c.Req.Context(), c.User.ID, email)
|
||||
if err != nil {
|
||||
c.Error(err, "delete email address")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
{{if .IsPrimary}}<span class="ui green tiny primary label">{{$.i18n.Tr "settings.primary"}}</span>{{end}}
|
||||
{{if not .IsPrimary}}
|
||||
<div class="ui right">
|
||||
<button class="ui red tiny basic button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
|
||||
<button class="ui red tiny basic button delete-button" data-url="{{$.Link}}/delete" data-id="{{.Email}}">
|
||||
{{$.i18n.Tr "settings.delete_email"}}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<form action="{{$.Link}}" method="post">
|
||||
{{$.CSRFTokenHTML}}
|
||||
<input name="_method" type="hidden" value="PRIMARY">
|
||||
<input name="id" type="hidden" value="{{.ID}}">
|
||||
<input name="email" type="hidden" value="{{.Email}}">
|
||||
<button class="ui green tiny basic button">{{$.i18n.Tr "settings.primary_email"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue