mirror of
https://github.com/gogs/gogs.git
synced 2025-07-07 19:19:08 +00:00
refactor(db): migrate off user_email.go
to users.go
(#7452)
This commit is contained in:
parent
9ac93067f6
commit
0721ef2399
14
.github/workflows/go.yml
vendored
14
.github/workflows/go.yml
vendored
@ -30,12 +30,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Run golangci-lint
|
- name: Install Go
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
version: latest
|
go-version: 1.20.x
|
||||||
args: --timeout=30m
|
|
||||||
- name: Install Task
|
- name: Install Task
|
||||||
uses: arduino/setup-task@v1
|
uses: arduino/setup-task@v1
|
||||||
with:
|
with:
|
||||||
@ -52,6 +51,11 @@ jobs:
|
|||||||
echo "Run 'go mod tidy' or 'task generate' commit them"
|
echo "Run 'go mod tidy' or 'task generate' commit them"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
- name: Run golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: --timeout=30m
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
|
@ -317,6 +317,7 @@ delete_email = Delete
|
|||||||
email_deletion = Email Deletion
|
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_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_success = Email has been deleted successfully!
|
||||||
|
email_deletion_primary = Cannot delete primary email address.
|
||||||
add_new_email = Add new email address
|
add_new_email = Add new email address
|
||||||
add_email = Add Email
|
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.
|
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)
|
"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"
|
# Table "follow"
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -31,8 +31,9 @@ func TestDumpAndImport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
if len(Tables) != 6 {
|
const wantTables = 7
|
||||||
t.Fatalf("New table has added (want 6 got %d), please add new tests for the table and update this check", len(Tables))
|
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...)
|
db := dbtest.NewDB(t, "dumpAndImport", Tables...)
|
||||||
@ -131,6 +132,19 @@ func setupDBToDump(t *testing.T, db *gorm.DB) {
|
|||||||
CreatedUnix: 1588568886,
|
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{
|
&Follow{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
UserID: 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.
|
// NOTE: Lines are sorted in alphabetical order, each letter in its own line.
|
||||||
var Tables = []any{
|
var Tables = []any{
|
||||||
new(Access), new(AccessToken), new(Action),
|
new(Access), new(AccessToken), new(Action),
|
||||||
|
new(EmailAddress),
|
||||||
new(Follow),
|
new(Follow),
|
||||||
new(LFSObject), new(LoginSource),
|
new(LFSObject), new(LoginSource),
|
||||||
}
|
}
|
||||||
@ -121,7 +122,6 @@ func Init(w logger.Writer) (*gorm.DB, error) {
|
|||||||
// Initialize stores, sorted in alphabetical order.
|
// Initialize stores, sorted in alphabetical order.
|
||||||
AccessTokens = &accessTokens{DB: db}
|
AccessTokens = &accessTokens{DB: db}
|
||||||
Actions = NewActionsStore(db)
|
Actions = NewActionsStore(db)
|
||||||
EmailAddresses = NewEmailAddressesStore(db)
|
|
||||||
LoginSources = &loginSources{DB: db, files: sourceFiles}
|
LoginSources = &loginSources{DB: db, files: sourceFiles}
|
||||||
LFS = &lfs{DB: db}
|
LFS = &lfs{DB: db}
|
||||||
Orgs = NewOrgsStore(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(Mirror), new(Release), new(Webhook), new(HookTask),
|
||||||
new(ProtectBranch), new(ProtectBranchWhitelist),
|
new(ProtectBranch), new(ProtectBranchWhitelist),
|
||||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
||||||
new(Notice), new(EmailAddress))
|
new(Notice))
|
||||||
|
|
||||||
gonicNames := []string{"SSL"}
|
gonicNames := []string{"SSL"}
|
||||||
for _, name := range gonicNames {
|
for _, name := range gonicNames {
|
||||||
|
2
internal/db/testdata/backup/EmailAddress.golden.json
vendored
Normal file
2
internal/db/testdata/backup/EmailAddress.golden.json
vendored
Normal file
@ -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
|
// 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
|
// 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,
|
// 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)
|
Create(ctx context.Context, username, email string, opts CreateUserOptions) (*User, error)
|
||||||
|
|
||||||
// GetByEmail returns the user (not organization) with given email. It ignores
|
// GetByEmail returns the user (not organization) with given email. It ignores
|
||||||
@ -101,6 +101,27 @@ type UsersStore interface {
|
|||||||
// DeleteInactivated deletes all inactivated users.
|
// DeleteInactivated deletes all inactivated users.
|
||||||
DeleteInactivated() error
|
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 marks the user to follow the other user.
|
||||||
Follow(ctx context.Context, userID, followID int64) error
|
Follow(ctx context.Context, userID, followID int64) error
|
||||||
// Unfollow removes the mark the user to follow the other user.
|
// 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)
|
_, err = db.GetByEmail(ctx, email)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil, ErrEmailAlreadyUsed{
|
return nil, ErrEmailAlreadyUsed{
|
||||||
@ -1061,6 +1082,183 @@ func (db *users) UseCustomAvatar(ctx context.Context, userID int64, avatar []byt
|
|||||||
Error
|
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.
|
// UserType indicates the type of the user account.
|
||||||
type UserType int
|
type UserType int
|
||||||
|
|
||||||
@ -1422,6 +1620,15 @@ func isUsernameAllowed(name string) error {
|
|||||||
return isNameAllowed(reservedUsernames, reservedUsernamePatterns, name)
|
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.
|
// Follow represents relations of users and their followers.
|
||||||
type Follow struct {
|
type Follow struct {
|
||||||
ID int64 `gorm:"primaryKey"`
|
ID int64 `gorm:"primaryKey"`
|
||||||
|
@ -116,6 +116,12 @@ func TestUsers(t *testing.T) {
|
|||||||
{"SearchByName", usersSearchByName},
|
{"SearchByName", usersSearchByName},
|
||||||
{"Update", usersUpdate},
|
{"Update", usersUpdate},
|
||||||
{"UseCustomAvatar", usersUseCustomAvatar},
|
{"UseCustomAvatar", usersUseCustomAvatar},
|
||||||
|
{"AddEmail", usersAddEmail},
|
||||||
|
{"GetEmail", usersGetEmail},
|
||||||
|
{"ListEmails", usersListEmails},
|
||||||
|
{"MarkEmailActivated", usersMarkEmailActivated},
|
||||||
|
{"MarkEmailPrimary", usersMarkEmailPrimary},
|
||||||
|
{"DeleteEmail", usersDeleteEmail},
|
||||||
{"Follow", usersFollow},
|
{"Follow", usersFollow},
|
||||||
{"IsFollowing", usersIsFollowing},
|
{"IsFollowing", usersIsFollowing},
|
||||||
{"Unfollow", usersUnfollow},
|
{"Unfollow", usersUnfollow},
|
||||||
@ -1100,7 +1106,19 @@ func usersUpdate(t *testing.T, db *users) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update email but already used", func(t *testing.T) {
|
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)
|
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) {
|
func usersFollow(t *testing.T, db *users) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ListEmails(c *context.APIContext) {
|
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 {
|
if err != nil {
|
||||||
c.Error(err, "get email addresses")
|
c.Error(err, "get email addresses")
|
||||||
return
|
return
|
||||||
@ -35,48 +35,40 @@ func AddEmail(c *context.APIContext, form api.CreateEmailOption) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emails := make([]*db.EmailAddress, len(form.Emails))
|
apiEmails := make([]*api.Email, 0, len(form.Emails))
|
||||||
for i := range form.Emails {
|
for _, email := range form.Emails {
|
||||||
emails[i] = &db.EmailAddress{
|
err := db.Users.AddEmail(c.Req.Context(), c.User.ID, email, !conf.Auth.RequireEmailConfirmation)
|
||||||
UserID: c.User.ID,
|
if err != nil {
|
||||||
Email: form.Emails[i],
|
if db.IsErrEmailAlreadyUsed(err) {
|
||||||
IsActivated: !conf.Auth.RequireEmailConfirmation,
|
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 {
|
apiEmails = append(apiEmails,
|
||||||
if db.IsErrEmailAlreadyUsed(err) {
|
&api.Email{
|
||||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("email address has been used: "+err.(db.ErrEmailAlreadyUsed).Email()))
|
Email: email,
|
||||||
} else {
|
Verified: !conf.Auth.RequireEmailConfirmation,
|
||||||
c.Error(err, "add email addresses")
|
},
|
||||||
}
|
)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
apiEmails := make([]*api.Email, len(emails))
|
|
||||||
for i := range emails {
|
|
||||||
apiEmails[i] = convert.ToEmail(emails[i])
|
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusCreated, &apiEmails)
|
c.JSON(http.StatusCreated, &apiEmails)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteEmail(c *context.APIContext, form api.CreateEmailOption) {
|
func DeleteEmail(c *context.APIContext, form api.CreateEmailOption) {
|
||||||
if len(form.Emails) == 0 {
|
for _, email := range form.Emails {
|
||||||
c.NoContent()
|
if email == c.User.Email {
|
||||||
return
|
c.ErrorStatus(http.StatusBadRequest, errors.Errorf("cannot delete primary email %q", email))
|
||||||
}
|
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],
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.DeleteEmailAddresses(emails); err != nil {
|
err := db.Users.DeleteEmail(c.Req.Context(), c.User.ID, email)
|
||||||
c.Error(err, "delete email addresses")
|
if err != nil {
|
||||||
return
|
c.Error(err, "delete email addresses")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.NoContent()
|
c.NoContent()
|
||||||
}
|
}
|
||||||
|
@ -3171,6 +3171,9 @@ func (c TwoFactorsStoreIsEnabledFuncCall) Results() []interface{} {
|
|||||||
// MockUsersStore is a mock implementation of the UsersStore interface (from
|
// MockUsersStore is a mock implementation of the UsersStore interface (from
|
||||||
// the package gogs.io/gogs/internal/db) used for unit testing.
|
// the package gogs.io/gogs/internal/db) used for unit testing.
|
||||||
type MockUsersStore struct {
|
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
|
// AuthenticateFunc is an instance of a mock function object controlling
|
||||||
// the behavior of the method Authenticate.
|
// the behavior of the method Authenticate.
|
||||||
AuthenticateFunc *UsersStoreAuthenticateFunc
|
AuthenticateFunc *UsersStoreAuthenticateFunc
|
||||||
@ -3189,6 +3192,9 @@ type MockUsersStore struct {
|
|||||||
// DeleteCustomAvatarFunc is an instance of a mock function object
|
// DeleteCustomAvatarFunc is an instance of a mock function object
|
||||||
// controlling the behavior of the method DeleteCustomAvatar.
|
// controlling the behavior of the method DeleteCustomAvatar.
|
||||||
DeleteCustomAvatarFunc *UsersStoreDeleteCustomAvatarFunc
|
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
|
// DeleteInactivatedFunc is an instance of a mock function object
|
||||||
// controlling the behavior of the method DeleteInactivated.
|
// controlling the behavior of the method DeleteInactivated.
|
||||||
DeleteInactivatedFunc *UsersStoreDeleteInactivatedFunc
|
DeleteInactivatedFunc *UsersStoreDeleteInactivatedFunc
|
||||||
@ -3207,6 +3213,9 @@ type MockUsersStore struct {
|
|||||||
// GetByUsernameFunc is an instance of a mock function object
|
// GetByUsernameFunc is an instance of a mock function object
|
||||||
// controlling the behavior of the method GetByUsername.
|
// controlling the behavior of the method GetByUsername.
|
||||||
GetByUsernameFunc *UsersStoreGetByUsernameFunc
|
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
|
// GetMailableEmailsByUsernamesFunc is an instance of a mock function
|
||||||
// object controlling the behavior of the method
|
// object controlling the behavior of the method
|
||||||
// GetMailableEmailsByUsernames.
|
// GetMailableEmailsByUsernames.
|
||||||
@ -3220,12 +3229,21 @@ type MockUsersStore struct {
|
|||||||
// ListFunc is an instance of a mock function object controlling the
|
// ListFunc is an instance of a mock function object controlling the
|
||||||
// behavior of the method List.
|
// behavior of the method List.
|
||||||
ListFunc *UsersStoreListFunc
|
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
|
// ListFollowersFunc is an instance of a mock function object
|
||||||
// controlling the behavior of the method ListFollowers.
|
// controlling the behavior of the method ListFollowers.
|
||||||
ListFollowersFunc *UsersStoreListFollowersFunc
|
ListFollowersFunc *UsersStoreListFollowersFunc
|
||||||
// ListFollowingsFunc is an instance of a mock function object
|
// ListFollowingsFunc is an instance of a mock function object
|
||||||
// controlling the behavior of the method ListFollowings.
|
// controlling the behavior of the method ListFollowings.
|
||||||
ListFollowingsFunc *UsersStoreListFollowingsFunc
|
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
|
// SearchByNameFunc is an instance of a mock function object controlling
|
||||||
// the behavior of the method SearchByName.
|
// the behavior of the method SearchByName.
|
||||||
SearchByNameFunc *UsersStoreSearchByNameFunc
|
SearchByNameFunc *UsersStoreSearchByNameFunc
|
||||||
@ -3244,6 +3262,11 @@ type MockUsersStore struct {
|
|||||||
// methods return zero values for all results, unless overwritten.
|
// methods return zero values for all results, unless overwritten.
|
||||||
func NewMockUsersStore() *MockUsersStore {
|
func NewMockUsersStore() *MockUsersStore {
|
||||||
return &MockUsersStore{
|
return &MockUsersStore{
|
||||||
|
AddEmailFunc: &UsersStoreAddEmailFunc{
|
||||||
|
defaultHook: func(context.Context, int64, string, bool) (r0 error) {
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
||||||
defaultHook: func(context.Context, string, string, int64) (r0 *db.User, r1 error) {
|
defaultHook: func(context.Context, string, string, int64) (r0 *db.User, r1 error) {
|
||||||
return
|
return
|
||||||
@ -3274,6 +3297,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DeleteEmailFunc: &UsersStoreDeleteEmailFunc{
|
||||||
|
defaultHook: func(context.Context, int64, string) (r0 error) {
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
||||||
defaultHook: func() (r0 error) {
|
defaultHook: func() (r0 error) {
|
||||||
return
|
return
|
||||||
@ -3304,6 +3332,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GetEmailFunc: &UsersStoreGetEmailFunc{
|
||||||
|
defaultHook: func(context.Context, int64, string, bool) (r0 *db.EmailAddress, r1 error) {
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
||||||
defaultHook: func(context.Context, []string) (r0 []string, r1 error) {
|
defaultHook: func(context.Context, []string) (r0 []string, r1 error) {
|
||||||
return
|
return
|
||||||
@ -3324,6 +3357,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ListEmailsFunc: &UsersStoreListEmailsFunc{
|
||||||
|
defaultHook: func(context.Context, int64) (r0 []*db.EmailAddress, r1 error) {
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
||||||
defaultHook: func(context.Context, int64, int, int) (r0 []*db.User, r1 error) {
|
defaultHook: func(context.Context, int64, int, int) (r0 []*db.User, r1 error) {
|
||||||
return
|
return
|
||||||
@ -3334,6 +3372,16 @@ func NewMockUsersStore() *MockUsersStore {
|
|||||||
return
|
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{
|
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
||||||
defaultHook: func(context.Context, string, int, int, string) (r0 []*db.User, r1 int64, r2 error) {
|
defaultHook: func(context.Context, string, int, int, string) (r0 []*db.User, r1 int64, r2 error) {
|
||||||
return
|
return
|
||||||
@ -3361,6 +3409,11 @@ func NewMockUsersStore() *MockUsersStore {
|
|||||||
// All methods panic on invocation, unless overwritten.
|
// All methods panic on invocation, unless overwritten.
|
||||||
func NewStrictMockUsersStore() *MockUsersStore {
|
func NewStrictMockUsersStore() *MockUsersStore {
|
||||||
return &MockUsersStore{
|
return &MockUsersStore{
|
||||||
|
AddEmailFunc: &UsersStoreAddEmailFunc{
|
||||||
|
defaultHook: func(context.Context, int64, string, bool) error {
|
||||||
|
panic("unexpected invocation of MockUsersStore.AddEmail")
|
||||||
|
},
|
||||||
|
},
|
||||||
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
||||||
defaultHook: func(context.Context, string, string, int64) (*db.User, error) {
|
defaultHook: func(context.Context, string, string, int64) (*db.User, error) {
|
||||||
panic("unexpected invocation of MockUsersStore.Authenticate")
|
panic("unexpected invocation of MockUsersStore.Authenticate")
|
||||||
@ -3391,6 +3444,11 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||||||
panic("unexpected invocation of MockUsersStore.DeleteCustomAvatar")
|
panic("unexpected invocation of MockUsersStore.DeleteCustomAvatar")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DeleteEmailFunc: &UsersStoreDeleteEmailFunc{
|
||||||
|
defaultHook: func(context.Context, int64, string) error {
|
||||||
|
panic("unexpected invocation of MockUsersStore.DeleteEmail")
|
||||||
|
},
|
||||||
|
},
|
||||||
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
||||||
defaultHook: func() error {
|
defaultHook: func() error {
|
||||||
panic("unexpected invocation of MockUsersStore.DeleteInactivated")
|
panic("unexpected invocation of MockUsersStore.DeleteInactivated")
|
||||||
@ -3421,6 +3479,11 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||||||
panic("unexpected invocation of MockUsersStore.GetByUsername")
|
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{
|
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
||||||
defaultHook: func(context.Context, []string) ([]string, error) {
|
defaultHook: func(context.Context, []string) ([]string, error) {
|
||||||
panic("unexpected invocation of MockUsersStore.GetMailableEmailsByUsernames")
|
panic("unexpected invocation of MockUsersStore.GetMailableEmailsByUsernames")
|
||||||
@ -3441,6 +3504,11 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||||||
panic("unexpected invocation of MockUsersStore.List")
|
panic("unexpected invocation of MockUsersStore.List")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ListEmailsFunc: &UsersStoreListEmailsFunc{
|
||||||
|
defaultHook: func(context.Context, int64) ([]*db.EmailAddress, error) {
|
||||||
|
panic("unexpected invocation of MockUsersStore.ListEmails")
|
||||||
|
},
|
||||||
|
},
|
||||||
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
||||||
defaultHook: func(context.Context, int64, int, int) ([]*db.User, error) {
|
defaultHook: func(context.Context, int64, int, int) ([]*db.User, error) {
|
||||||
panic("unexpected invocation of MockUsersStore.ListFollowers")
|
panic("unexpected invocation of MockUsersStore.ListFollowers")
|
||||||
@ -3451,6 +3519,16 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||||||
panic("unexpected invocation of MockUsersStore.ListFollowings")
|
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{
|
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
||||||
defaultHook: func(context.Context, string, int, int, string) ([]*db.User, int64, error) {
|
defaultHook: func(context.Context, string, int, int, string) ([]*db.User, int64, error) {
|
||||||
panic("unexpected invocation of MockUsersStore.SearchByName")
|
panic("unexpected invocation of MockUsersStore.SearchByName")
|
||||||
@ -3478,6 +3556,9 @@ func NewStrictMockUsersStore() *MockUsersStore {
|
|||||||
// All methods delegate to the given implementation, unless overwritten.
|
// All methods delegate to the given implementation, unless overwritten.
|
||||||
func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
||||||
return &MockUsersStore{
|
return &MockUsersStore{
|
||||||
|
AddEmailFunc: &UsersStoreAddEmailFunc{
|
||||||
|
defaultHook: i.AddEmail,
|
||||||
|
},
|
||||||
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
AuthenticateFunc: &UsersStoreAuthenticateFunc{
|
||||||
defaultHook: i.Authenticate,
|
defaultHook: i.Authenticate,
|
||||||
},
|
},
|
||||||
@ -3496,6 +3577,9 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||||||
DeleteCustomAvatarFunc: &UsersStoreDeleteCustomAvatarFunc{
|
DeleteCustomAvatarFunc: &UsersStoreDeleteCustomAvatarFunc{
|
||||||
defaultHook: i.DeleteCustomAvatar,
|
defaultHook: i.DeleteCustomAvatar,
|
||||||
},
|
},
|
||||||
|
DeleteEmailFunc: &UsersStoreDeleteEmailFunc{
|
||||||
|
defaultHook: i.DeleteEmail,
|
||||||
|
},
|
||||||
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
DeleteInactivatedFunc: &UsersStoreDeleteInactivatedFunc{
|
||||||
defaultHook: i.DeleteInactivated,
|
defaultHook: i.DeleteInactivated,
|
||||||
},
|
},
|
||||||
@ -3514,6 +3598,9 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||||||
GetByUsernameFunc: &UsersStoreGetByUsernameFunc{
|
GetByUsernameFunc: &UsersStoreGetByUsernameFunc{
|
||||||
defaultHook: i.GetByUsername,
|
defaultHook: i.GetByUsername,
|
||||||
},
|
},
|
||||||
|
GetEmailFunc: &UsersStoreGetEmailFunc{
|
||||||
|
defaultHook: i.GetEmail,
|
||||||
|
},
|
||||||
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
GetMailableEmailsByUsernamesFunc: &UsersStoreGetMailableEmailsByUsernamesFunc{
|
||||||
defaultHook: i.GetMailableEmailsByUsernames,
|
defaultHook: i.GetMailableEmailsByUsernames,
|
||||||
},
|
},
|
||||||
@ -3526,12 +3613,21 @@ func NewMockUsersStoreFrom(i db.UsersStore) *MockUsersStore {
|
|||||||
ListFunc: &UsersStoreListFunc{
|
ListFunc: &UsersStoreListFunc{
|
||||||
defaultHook: i.List,
|
defaultHook: i.List,
|
||||||
},
|
},
|
||||||
|
ListEmailsFunc: &UsersStoreListEmailsFunc{
|
||||||
|
defaultHook: i.ListEmails,
|
||||||
|
},
|
||||||
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
ListFollowersFunc: &UsersStoreListFollowersFunc{
|
||||||
defaultHook: i.ListFollowers,
|
defaultHook: i.ListFollowers,
|
||||||
},
|
},
|
||||||
ListFollowingsFunc: &UsersStoreListFollowingsFunc{
|
ListFollowingsFunc: &UsersStoreListFollowingsFunc{
|
||||||
defaultHook: i.ListFollowings,
|
defaultHook: i.ListFollowings,
|
||||||
},
|
},
|
||||||
|
MarkEmailActivatedFunc: &UsersStoreMarkEmailActivatedFunc{
|
||||||
|
defaultHook: i.MarkEmailActivated,
|
||||||
|
},
|
||||||
|
MarkEmailPrimaryFunc: &UsersStoreMarkEmailPrimaryFunc{
|
||||||
|
defaultHook: i.MarkEmailPrimary,
|
||||||
|
},
|
||||||
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
SearchByNameFunc: &UsersStoreSearchByNameFunc{
|
||||||
defaultHook: i.SearchByName,
|
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
|
// UsersStoreAuthenticateFunc describes the behavior when the Authenticate
|
||||||
// method of the parent MockUsersStore instance is invoked.
|
// method of the parent MockUsersStore instance is invoked.
|
||||||
type UsersStoreAuthenticateFunc struct {
|
type UsersStoreAuthenticateFunc struct {
|
||||||
@ -4197,6 +4404,114 @@ func (c UsersStoreDeleteCustomAvatarFuncCall) Results() []interface{} {
|
|||||||
return []interface{}{c.Result0}
|
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
|
// UsersStoreDeleteInactivatedFunc describes the behavior when the
|
||||||
// DeleteInactivated method of the parent MockUsersStore instance is
|
// DeleteInactivated method of the parent MockUsersStore instance is
|
||||||
// invoked.
|
// invoked.
|
||||||
@ -4836,6 +5151,120 @@ func (c UsersStoreGetByUsernameFuncCall) Results() []interface{} {
|
|||||||
return []interface{}{c.Result0, c.Result1}
|
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
|
// UsersStoreGetMailableEmailsByUsernamesFunc describes the behavior when
|
||||||
// the GetMailableEmailsByUsernames method of the parent MockUsersStore
|
// the GetMailableEmailsByUsernames method of the parent MockUsersStore
|
||||||
// instance is invoked.
|
// instance is invoked.
|
||||||
@ -5274,6 +5703,114 @@ func (c UsersStoreListFuncCall) Results() []interface{} {
|
|||||||
return []interface{}{c.Result0, c.Result1}
|
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
|
// UsersStoreListFollowersFunc describes the behavior when the ListFollowers
|
||||||
// method of the parent MockUsersStore instance is invoked.
|
// method of the parent MockUsersStore instance is invoked.
|
||||||
type UsersStoreListFollowersFunc struct {
|
type UsersStoreListFollowersFunc struct {
|
||||||
@ -5502,6 +6039,223 @@ func (c UsersStoreListFollowingsFuncCall) Results() []interface{} {
|
|||||||
return []interface{}{c.Result0, c.Result1}
|
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
|
// UsersStoreSearchByNameFunc describes the behavior when the SearchByName
|
||||||
// method of the parent MockUsersStore instance is invoked.
|
// method of the parent MockUsersStore instance is invoked.
|
||||||
type UsersStoreSearchByNameFunc struct {
|
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
|
data := com.ToStr(user.ID) + email + user.LowerName + user.Password + user.Rands
|
||||||
|
|
||||||
if tool.VerifyTimeLimitCode(data, minutes, prefix) {
|
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 {
|
if err == nil {
|
||||||
return emailAddress
|
return emailAddress
|
||||||
}
|
}
|
||||||
@ -515,8 +515,10 @@ func ActivateEmail(c *context.Context) {
|
|||||||
|
|
||||||
// Verify code.
|
// Verify code.
|
||||||
if email := verifyActiveEmailCode(code, emailAddr); email != nil {
|
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")
|
c.Error(err, "activate email")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Email activated: %s", email.Email)
|
log.Trace("Email activated: %s", email.Email)
|
||||||
|
@ -223,7 +223,7 @@ func SettingsEmails(c *context.Context) {
|
|||||||
c.Title("settings.emails")
|
c.Title("settings.emails")
|
||||||
c.PageIs("SettingsEmails")
|
c.PageIs("SettingsEmails")
|
||||||
|
|
||||||
emails, err := db.GetEmailAddresses(c.User.ID)
|
emails, err := db.Users.ListEmails(c.Req.Context(), c.User.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Errorf(err, "get email addresses")
|
c.Errorf(err, "get email addresses")
|
||||||
return
|
return
|
||||||
@ -237,9 +237,9 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||||||
c.Title("settings.emails")
|
c.Title("settings.emails")
|
||||||
c.PageIs("SettingsEmails")
|
c.PageIs("SettingsEmails")
|
||||||
|
|
||||||
// Make emailaddress primary.
|
|
||||||
if c.Query("_method") == "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")
|
c.Errorf(err, "make email primary")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add Email address.
|
// Add Email address.
|
||||||
emails, err := db.GetEmailAddresses(c.User.ID)
|
emails, err := db.Users.ListEmails(c.Req.Context(), c.User.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Errorf(err, "get email addresses")
|
c.Errorf(err, "get email addresses")
|
||||||
return
|
return
|
||||||
@ -261,12 +261,8 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emailAddr := &db.EmailAddress{
|
err = db.Users.AddEmail(c.Req.Context(), c.User.ID, f.Email, !conf.Auth.RequireEmailConfirmation)
|
||||||
UserID: c.User.ID,
|
if err != nil {
|
||||||
Email: f.Email,
|
|
||||||
IsActivated: !conf.Auth.RequireEmailConfirmation,
|
|
||||||
}
|
|
||||||
if err := db.AddEmailAddress(emailAddr); err != nil {
|
|
||||||
if db.IsErrEmailAlreadyUsed(err) {
|
if db.IsErrEmailAlreadyUsed(err) {
|
||||||
c.RenderWithErr(c.Tr("form.email_been_used"), SETTINGS_EMAILS, &f)
|
c.RenderWithErr(c.Tr("form.email_been_used"), SETTINGS_EMAILS, &f)
|
||||||
} else {
|
} else {
|
||||||
@ -277,12 +273,12 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
|
|||||||
|
|
||||||
// Send confirmation email
|
// Send confirmation email
|
||||||
if conf.Auth.RequireEmailConfirmation {
|
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 {
|
if err := c.Cache.Put("MailResendLimit_"+c.User.LowerName, c.User.LowerName, 180); err != nil {
|
||||||
log.Error("Set cache 'MailResendLimit' failed: %v", err)
|
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 {
|
} else {
|
||||||
c.Flash.Success(c.Tr("settings.add_email_success"))
|
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) {
|
func DeleteEmail(c *context.Context) {
|
||||||
if err := db.DeleteEmailAddress(&db.EmailAddress{
|
email := c.Query("id") // The "id" here is the actual email address
|
||||||
ID: c.QueryInt64("id"),
|
if c.User.Email == email {
|
||||||
UserID: c.User.ID,
|
c.Flash.Error(c.Tr("settings.email_deletion_primary"))
|
||||||
}); err != nil {
|
c.JSONSuccess(map[string]any{
|
||||||
c.Errorf(err, "delete email address")
|
"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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
{{if .IsPrimary}}<span class="ui green tiny primary label">{{$.i18n.Tr "settings.primary"}}</span>{{end}}
|
{{if .IsPrimary}}<span class="ui green tiny primary label">{{$.i18n.Tr "settings.primary"}}</span>{{end}}
|
||||||
{{if not .IsPrimary}}
|
{{if not .IsPrimary}}
|
||||||
<div class="ui right">
|
<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"}}
|
{{$.i18n.Tr "settings.delete_email"}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<form action="{{$.Link}}" method="post">
|
<form action="{{$.Link}}" method="post">
|
||||||
{{$.CSRFTokenHTML}}
|
{{$.CSRFTokenHTML}}
|
||||||
<input name="_method" type="hidden" value="PRIMARY">
|
<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>
|
<button class="ui green tiny basic button">{{$.i18n.Tr "settings.primary_email"}}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user