aboutsummaryrefslogtreecommitdiffstats
path: root/models/user
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2021-11-17 17:58:31 +0800
committerGitHub <noreply@github.com>2021-11-17 17:58:31 +0800
commit95d3266bee797cbeb7228d361fe32531737906d2 (patch)
tree68d90e7ef3e890c1ac311cf79f08cbe12a0dafca /models/user
parentadda27668b9764f97822e56e0591a6eb22165a83 (diff)
downloadgitea-95d3266bee797cbeb7228d361fe32531737906d2.tar.gz
gitea-95d3266bee797cbeb7228d361fe32531737906d2.zip
Move user follow and openid into models/user/ (#17613)
* Move UserRedirect into models/user/ * Fix lint & test * Fix lint * Fix lint * remove nolint comment * Fix lint * Move user follow and openid into models/user * Ignore the lint * Ignore the lint * Fix test * ignore stutters lint on UserOpenID
Diffstat (limited to 'models/user')
-rw-r--r--models/user/follow.go80
-rw-r--r--models/user/follow_test.go22
-rw-r--r--models/user/main_test.go2
-rw-r--r--models/user/openid.go112
-rw-r--r--models/user/openid_test.go63
5 files changed, 279 insertions, 0 deletions
diff --git a/models/user/follow.go b/models/user/follow.go
new file mode 100644
index 0000000000..89675b5078
--- /dev/null
+++ b/models/user/follow.go
@@ -0,0 +1,80 @@
+// Copyright 2017 The Gitea 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 user
+
+import (
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/timeutil"
+)
+
+// Follow represents relations of user and his/her followers.
+type Follow struct {
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"UNIQUE(follow)"`
+ FollowID int64 `xorm:"UNIQUE(follow)"`
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+}
+
+func init() {
+ db.RegisterModel(new(Follow))
+}
+
+// IsFollowing returns true if user is following followID.
+func IsFollowing(userID, followID int64) bool {
+ has, _ := db.GetEngine(db.DefaultContext).Get(&Follow{UserID: userID, FollowID: followID})
+ return has
+}
+
+// FollowUser marks someone be another's follower.
+func FollowUser(userID, followID int64) (err error) {
+ if userID == followID || IsFollowing(userID, followID) {
+ return nil
+ }
+
+ sess := db.NewSession(db.DefaultContext)
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return err
+ }
+
+ if _, err = sess.Insert(&Follow{UserID: userID, FollowID: followID}); err != nil {
+ return err
+ }
+
+ if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil {
+ return err
+ }
+
+ if _, err = sess.Exec("UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
+
+// UnfollowUser unmarks someone as another's follower.
+func UnfollowUser(userID, followID int64) (err error) {
+ if userID == followID || !IsFollowing(userID, followID) {
+ return nil
+ }
+
+ sess := db.NewSession(db.DefaultContext)
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return err
+ }
+
+ if _, err = sess.Delete(&Follow{UserID: userID, FollowID: followID}); err != nil {
+ return err
+ }
+
+ if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
+ return err
+ }
+
+ if _, err = sess.Exec("UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
diff --git a/models/user/follow_test.go b/models/user/follow_test.go
new file mode 100644
index 0000000000..538c7b18a2
--- /dev/null
+++ b/models/user/follow_test.go
@@ -0,0 +1,22 @@
+// Copyright 2020 The Gitea 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 user
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestIsFollowing(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ assert.True(t, IsFollowing(4, 2))
+ assert.False(t, IsFollowing(2, 4))
+ assert.False(t, IsFollowing(5, unittest.NonexistentID))
+ assert.False(t, IsFollowing(unittest.NonexistentID, 5))
+ assert.False(t, IsFollowing(unittest.NonexistentID, unittest.NonexistentID))
+}
diff --git a/models/user/main_test.go b/models/user/main_test.go
index 1dd9fb2781..7862556a03 100644
--- a/models/user/main_test.go
+++ b/models/user/main_test.go
@@ -15,5 +15,7 @@ func TestMain(m *testing.M) {
unittest.MainTest(m, filepath.Join("..", ".."),
"email_address.yml",
"user_redirect.yml",
+ "follow.yml",
+ "user_open_id.yml",
)
}
diff --git a/models/user/openid.go b/models/user/openid.go
new file mode 100644
index 0000000000..8ca3c7f2c8
--- /dev/null
+++ b/models/user/openid.go
@@ -0,0 +1,112 @@
+// Copyright 2017 The Gitea 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 user
+
+import (
+ "errors"
+ "fmt"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+// ErrOpenIDNotExist openid is not known
+var ErrOpenIDNotExist = errors.New("OpenID is unknown")
+
+// UserOpenID is the list of all OpenID identities of a user.
+// Since this is a middle table, name it OpenID is not suitable, so we ignore the lint here
+type UserOpenID struct { //revive:disable-line:exported
+ ID int64 `xorm:"pk autoincr"`
+ UID int64 `xorm:"INDEX NOT NULL"`
+ URI string `xorm:"UNIQUE NOT NULL"`
+ Show bool `xorm:"DEFAULT false"`
+}
+
+func init() {
+ db.RegisterModel(new(UserOpenID))
+}
+
+// GetUserOpenIDs returns all openid addresses that belongs to given user.
+func GetUserOpenIDs(uid int64) ([]*UserOpenID, error) {
+ openids := make([]*UserOpenID, 0, 5)
+ if err := db.GetEngine(db.DefaultContext).
+ Where("uid=?", uid).
+ Asc("id").
+ Find(&openids); err != nil {
+ return nil, err
+ }
+
+ return openids, nil
+}
+
+// isOpenIDUsed returns true if the openid has been used.
+func isOpenIDUsed(e db.Engine, uri string) (bool, error) {
+ if len(uri) == 0 {
+ return true, nil
+ }
+
+ return e.Get(&UserOpenID{URI: uri})
+}
+
+// ErrOpenIDAlreadyUsed represents a "OpenIDAlreadyUsed" kind of error.
+type ErrOpenIDAlreadyUsed struct {
+ OpenID string
+}
+
+// IsErrOpenIDAlreadyUsed checks if an error is a ErrOpenIDAlreadyUsed.
+func IsErrOpenIDAlreadyUsed(err error) bool {
+ _, ok := err.(ErrOpenIDAlreadyUsed)
+ return ok
+}
+
+func (err ErrOpenIDAlreadyUsed) Error() string {
+ return fmt.Sprintf("OpenID already in use [oid: %s]", err.OpenID)
+}
+
+// NOTE: make sure openid.URI is normalized already
+func addUserOpenID(e db.Engine, openid *UserOpenID) error {
+ used, err := isOpenIDUsed(e, openid.URI)
+ if err != nil {
+ return err
+ } else if used {
+ return ErrOpenIDAlreadyUsed{openid.URI}
+ }
+
+ _, err = e.Insert(openid)
+ return err
+}
+
+// AddUserOpenID adds an pre-verified/normalized OpenID URI to given user.
+func AddUserOpenID(openid *UserOpenID) error {
+ return addUserOpenID(db.GetEngine(db.DefaultContext), openid)
+}
+
+// DeleteUserOpenID deletes an openid address of given user.
+func DeleteUserOpenID(openid *UserOpenID) (err error) {
+ var deleted int64
+ // ask to check UID
+ address := UserOpenID{
+ UID: openid.UID,
+ }
+ if openid.ID > 0 {
+ deleted, err = db.GetEngine(db.DefaultContext).ID(openid.ID).Delete(&address)
+ } else {
+ deleted, err = db.GetEngine(db.DefaultContext).
+ Where("openid=?", openid.URI).
+ Delete(&address)
+ }
+
+ if err != nil {
+ return err
+ } else if deleted != 1 {
+ return ErrOpenIDNotExist
+ }
+ return nil
+}
+
+// ToggleUserOpenIDVisibility toggles visibility of an openid address of given user.
+func ToggleUserOpenIDVisibility(id int64) (err error) {
+ _, err = db.GetEngine(db.DefaultContext).Exec("update `user_open_id` set `show` = not `show` where `id` = ?", id)
+ return err
+}
diff --git a/models/user/openid_test.go b/models/user/openid_test.go
new file mode 100644
index 0000000000..ba678ef864
--- /dev/null
+++ b/models/user/openid_test.go
@@ -0,0 +1,63 @@
+// Copyright 2017 The Gitea 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 user
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetUserOpenIDs(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ oids, err := GetUserOpenIDs(int64(1))
+ if assert.NoError(t, err) && assert.Len(t, oids, 2) {
+ assert.Equal(t, "https://user1.domain1.tld/", oids[0].URI)
+ assert.False(t, oids[0].Show)
+ assert.Equal(t, "http://user1.domain2.tld/", oids[1].URI)
+ assert.True(t, oids[1].Show)
+ }
+
+ oids, err = GetUserOpenIDs(int64(2))
+ if assert.NoError(t, err) && assert.Len(t, oids, 1) {
+ assert.Equal(t, "https://domain1.tld/user2/", oids[0].URI)
+ assert.True(t, oids[0].Show)
+ }
+}
+
+func TestToggleUserOpenIDVisibility(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ oids, err := GetUserOpenIDs(int64(2))
+ if !assert.NoError(t, err) || !assert.Len(t, oids, 1) {
+ return
+ }
+ assert.True(t, oids[0].Show)
+
+ err = ToggleUserOpenIDVisibility(oids[0].ID)
+ if !assert.NoError(t, err) {
+ return
+ }
+
+ oids, err = GetUserOpenIDs(int64(2))
+ if !assert.NoError(t, err) || !assert.Len(t, oids, 1) {
+ return
+ }
+ assert.False(t, oids[0].Show)
+ err = ToggleUserOpenIDVisibility(oids[0].ID)
+ if !assert.NoError(t, err) {
+ return
+ }
+
+ oids, err = GetUserOpenIDs(int64(2))
+ if !assert.NoError(t, err) {
+ return
+ }
+ if assert.Len(t, oids, 1) {
+ assert.True(t, oids[0].Show)
+ }
+}