-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Pin repos on profile #30961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Pin repos on profile #30961
Changes from all commits
5f890b5
fd47c82
e8dca1e
0754ed7
f8b1756
5fba27c
0887277
c9d2f49
dae3c8a
2140062
b941b04
a019e44
3704d23
805dbc7
859b1ce
764b08b
a392779
9479423
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright 2024 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package repo | ||
|
||
import ( | ||
"context" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
user_model "code.gitea.io/gitea/models/user" | ||
"code.gitea.io/gitea/modules/timeutil" | ||
) | ||
|
||
type Pin struct { | ||
ID int64 `xorm:"pk autoincr"` | ||
UID int64 `xorm:"UNIQUE(s)"` | ||
RepoID int64 `xorm:"UNIQUE(s)"` | ||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||
} | ||
|
||
// TableName sets the table name for the pin struct | ||
func (s *Pin) TableName() string { | ||
return "repository_pin" | ||
} | ||
|
||
func init() { | ||
db.RegisterModel(new(Pin)) | ||
} | ||
|
||
func IsPinned(ctx context.Context, userID, repoID int64) bool { | ||
exists, _ := db.GetEngine(ctx).Exist(&Pin{UID: userID, RepoID: repoID}) | ||
|
||
return exists | ||
} | ||
|
||
func PinRepo(ctx context.Context, doer *user_model.User, repo *Repository, pin bool) error { | ||
return db.WithTx(ctx, func(ctx context.Context) error { | ||
pinned := IsPinned(ctx, doer.ID, repo.ID) | ||
|
||
if pin { | ||
// Already pinned, nothing to do | ||
if pinned { | ||
return nil | ||
} | ||
|
||
if err := db.Insert(ctx, &Pin{UID: doer.ID, RepoID: repo.ID}); err != nil { | ||
return err | ||
} | ||
} else { | ||
// Not pinned, nothing to do | ||
if !pinned { | ||
return nil | ||
} | ||
|
||
if _, err := db.DeleteByBean(ctx, &Pin{UID: doer.ID, RepoID: repo.ID}); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2024 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package repo_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
repo_model "code.gitea.io/gitea/models/repo" | ||
"code.gitea.io/gitea/models/unittest" | ||
user_model "code.gitea.io/gitea/models/user" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPinRepoFunctionality(t *testing.T) { | ||
assert.NoError(t, unittest.PrepareTestDatabase()) | ||
|
||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||
|
||
unittest.AssertNotExistsBean(t, &repo_model.Pin{UID: user.ID, RepoID: repo.ID}) | ||
assert.NoError(t, repo_model.PinRepo(db.DefaultContext, user, repo, true)) | ||
unittest.AssertExistsAndLoadBean(t, &repo_model.Pin{UID: user.ID, RepoID: repo.ID}) | ||
assert.NoError(t, repo_model.PinRepo(db.DefaultContext, user, repo, true)) | ||
unittest.AssertExistsAndLoadBean(t, &repo_model.Pin{UID: user.ID, RepoID: repo.ID}) | ||
assert.NoError(t, repo_model.PinRepo(db.DefaultContext, user, repo, false)) | ||
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: user.ID, RepoID: repo.ID}) | ||
} | ||
|
||
func TestIsPinned(t *testing.T) { | ||
assert.NoError(t, unittest.PrepareTestDatabase()) | ||
|
||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||
|
||
unittest.AssertNotExistsBean(t, &repo_model.Pin{UID: user.ID, RepoID: repo.ID}) | ||
assert.NoError(t, repo_model.PinRepo(db.DefaultContext, user, repo, true)) | ||
unittest.AssertExistsAndLoadBean(t, &repo_model.Pin{UID: user.ID, RepoID: repo.ID}) | ||
|
||
assert.True(t, repo_model.IsPinned(db.DefaultContext, user.ID, repo.ID)) | ||
|
||
assert.NoError(t, repo_model.PinRepo(db.DefaultContext, user, repo, false)) | ||
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: user.ID, RepoID: repo.ID}) | ||
|
||
assert.False(t, repo_model.IsPinned(db.DefaultContext, user.ID, repo.ID)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ import ( | |
"code.gitea.io/gitea/models/db" | ||
git_model "code.gitea.io/gitea/models/git" | ||
issue_model "code.gitea.io/gitea/models/issues" | ||
"code.gitea.io/gitea/models/organization" | ||
access_model "code.gitea.io/gitea/models/perm/access" | ||
repo_model "code.gitea.io/gitea/models/repo" | ||
unit_model "code.gitea.io/gitea/models/unit" | ||
|
@@ -51,6 +52,7 @@ import ( | |
"code.gitea.io/gitea/services/context" | ||
issue_service "code.gitea.io/gitea/services/issue" | ||
files_service "code.gitea.io/gitea/services/repository/files" | ||
user_service "code.gitea.io/gitea/services/user" | ||
|
||
"github.com/nektos/act/pkg/model" | ||
|
||
|
@@ -792,6 +794,14 @@ func Home(ctx *context.Context) { | |
return | ||
} | ||
|
||
if ctx.IsSigned { | ||
err := loadPinData(ctx) | ||
if err != nil { | ||
ctx.ServerError("loadPinData", err) | ||
carlosfelgueiras marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return | ||
} | ||
} | ||
|
||
renderHomeCode(ctx) | ||
} | ||
|
||
|
@@ -1179,3 +1189,37 @@ func Forks(ctx *context.Context) { | |
|
||
ctx.HTML(http.StatusOK, tplForks) | ||
} | ||
|
||
func loadPinData(ctx *context.Context) error { | ||
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// First, cleanup any pins that are no longer valid | ||
err := user_service.CleanupPins(ctx, ctx.Doer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why cleanup pins when user home page be visited? Looks wired. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are cleaning the pins to avoid an edge case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When changing the visibility and user has no permission to access this pin, means user2 forcibly unpinned it, so this should be handled when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is still a concern. Cleaning should not be done when a page is visiting. |
||
if err != nil { | ||
return err | ||
} | ||
|
||
ctx.Data["IsPinningRepo"] = repo_model.IsPinned(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) | ||
ctx.Data["CanPinRepo"], err = user_service.CanPin(ctx, ctx.Doer, ctx.Repo.Repository) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if ctx.Repo.Repository.Owner.IsOrganization() { | ||
org := organization.OrgFromUser(ctx.Repo.Repository.Owner) | ||
|
||
isAdmin, err := org.IsOrgAdmin(ctx, ctx.Doer.ID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if isAdmin { | ||
ctx.Data["CanUserPinToOrg"] = true | ||
ctx.Data["IsOrgPinningRepo"] = repo_model.IsPinned(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) | ||
ctx.Data["CanOrgPinRepo"], err = user_service.CanPin(ctx, ctx.Repo.Repository.Owner, ctx.Repo.Repository) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not
UserID
? i do not think UID is a good name. It looks like UUID