Skip to content

Commit 0470646

Browse files
authored
Show lock owner instead of repo owner on LFS setting page (go-gitea#31788)
Fix go-gitea#31784. Before: <img width="1648" alt="image" src="https://github.com/user-attachments/assets/03f32545-4a85-42ed-bafc-2b193a5d8023"> After: <img width="1653" alt="image" src="https://github.com/user-attachments/assets/e5bcaf93-49cb-421f-aac1-5122bc488b02">
1 parent e45a4c9 commit 0470646

File tree

5 files changed

+162
-10
lines changed

5 files changed

+162
-10
lines changed

models/git/lfs_lock.go

+38-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package git
66
import (
77
"context"
88
"errors"
9+
"fmt"
910
"strings"
1011
"time"
1112

@@ -21,11 +22,12 @@ import (
2122

2223
// LFSLock represents a git lfs lock of repository.
2324
type LFSLock struct {
24-
ID int64 `xorm:"pk autoincr"`
25-
RepoID int64 `xorm:"INDEX NOT NULL"`
26-
OwnerID int64 `xorm:"INDEX NOT NULL"`
27-
Path string `xorm:"TEXT"`
28-
Created time.Time `xorm:"created"`
25+
ID int64 `xorm:"pk autoincr"`
26+
RepoID int64 `xorm:"INDEX NOT NULL"`
27+
OwnerID int64 `xorm:"INDEX NOT NULL"`
28+
Owner *user_model.User `xorm:"-"`
29+
Path string `xorm:"TEXT"`
30+
Created time.Time `xorm:"created"`
2931
}
3032

3133
func init() {
@@ -37,6 +39,35 @@ func (l *LFSLock) BeforeInsert() {
3739
l.Path = util.PathJoinRel(l.Path)
3840
}
3941

42+
// LoadAttributes loads attributes of the lock.
43+
func (l *LFSLock) LoadAttributes(ctx context.Context) error {
44+
// Load owner
45+
if err := l.LoadOwner(ctx); err != nil {
46+
return fmt.Errorf("load owner: %w", err)
47+
}
48+
49+
return nil
50+
}
51+
52+
// LoadOwner loads owner of the lock.
53+
func (l *LFSLock) LoadOwner(ctx context.Context) error {
54+
if l.Owner != nil {
55+
return nil
56+
}
57+
58+
owner, err := user_model.GetUserByID(ctx, l.OwnerID)
59+
if err != nil {
60+
if user_model.IsErrUserNotExist(err) {
61+
l.Owner = user_model.NewGhostUser()
62+
return nil
63+
}
64+
return err
65+
}
66+
l.Owner = owner
67+
68+
return nil
69+
}
70+
4071
// CreateLFSLock creates a new lock.
4172
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
4273
dbCtx, committer, err := db.TxContext(ctx)
@@ -94,7 +125,7 @@ func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
94125
}
95126

96127
// GetLFSLockByRepoID returns a list of locks of repository.
97-
func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ([]*LFSLock, error) {
128+
func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (LFSLockList, error) {
98129
e := db.GetEngine(ctx)
99130
if page >= 0 && pageSize > 0 {
100131
start := 0
@@ -103,7 +134,7 @@ func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (
103134
}
104135
e.Limit(pageSize, start)
105136
}
106-
lfsLocks := make([]*LFSLock, 0, pageSize)
137+
lfsLocks := make(LFSLockList, 0, pageSize)
107138
return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID})
108139
}
109140

models/git/lfs_lock_list.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package git
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"code.gitea.io/gitea/models/db"
11+
user_model "code.gitea.io/gitea/models/user"
12+
"code.gitea.io/gitea/modules/container"
13+
)
14+
15+
// LFSLockList is a list of LFSLock
16+
type LFSLockList []*LFSLock
17+
18+
// LoadAttributes loads the attributes for the given locks
19+
func (locks LFSLockList) LoadAttributes(ctx context.Context) error {
20+
if len(locks) == 0 {
21+
return nil
22+
}
23+
24+
if err := locks.LoadOwner(ctx); err != nil {
25+
return fmt.Errorf("load owner: %w", err)
26+
}
27+
28+
return nil
29+
}
30+
31+
// LoadOwner loads the owner of the locks
32+
func (locks LFSLockList) LoadOwner(ctx context.Context) error {
33+
if len(locks) == 0 {
34+
return nil
35+
}
36+
37+
usersIDs := container.FilterSlice(locks, func(lock *LFSLock) (int64, bool) {
38+
return lock.OwnerID, true
39+
})
40+
users := make(map[int64]*user_model.User, len(usersIDs))
41+
if err := db.GetEngine(ctx).
42+
In("id", usersIDs).
43+
Find(&users); err != nil {
44+
return fmt.Errorf("find users: %w", err)
45+
}
46+
for _, v := range locks {
47+
v.Owner = users[v.OwnerID]
48+
if v.Owner == nil { // not exist
49+
v.Owner = user_model.NewGhostUser()
50+
}
51+
}
52+
53+
return nil
54+
}

routers/web/repo/setting/lfs.go

+5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ func LFSLocks(ctx *context.Context) {
9595
ctx.ServerError("LFSLocks", err)
9696
return
9797
}
98+
if err := lfsLocks.LoadAttributes(ctx); err != nil {
99+
ctx.ServerError("LFSLocks", err)
100+
return
101+
}
102+
98103
ctx.Data["LFSLocks"] = lfsLocks
99104

100105
if len(lfsLocks) == 0 {

templates/repo/settings/lfs_locks.tmpl

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
{{end}}
3131
</td>
3232
<td>
33-
<a href="{{$.Owner.HomeLink}}">
34-
{{ctx.AvatarUtils.Avatar $.Owner}}
35-
{{$.Owner.DisplayName}}
33+
<a href="{{$lock.Owner.HomeLink}}">
34+
{{ctx.AvatarUtils.Avatar $lock.Owner}}
35+
{{$lock.Owner.DisplayName}}
3636
</a>
3737
</td>
3838
<td>{{TimeSince .Created ctx.Locale}}</td>

tests/integration/lfs_view_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@
44
package integration
55

66
import (
7+
"context"
8+
"fmt"
79
"net/http"
10+
"strings"
811
"testing"
912

13+
repo_model "code.gitea.io/gitea/models/repo"
14+
"code.gitea.io/gitea/models/unittest"
15+
user_model "code.gitea.io/gitea/models/user"
16+
"code.gitea.io/gitea/modules/lfs"
17+
api "code.gitea.io/gitea/modules/structs"
1018
"code.gitea.io/gitea/tests"
1119

1220
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
1322
)
1423

1524
// check that files stored in LFS render properly in the web UI
@@ -81,3 +90,56 @@ func TestLFSRender(t *testing.T) {
8190
assert.Contains(t, content, "Testing READMEs in LFS")
8291
})
8392
}
93+
94+
// TestLFSLockView tests the LFS lock view on settings page of repositories
95+
func TestLFSLockView(t *testing.T) {
96+
defer tests.PrepareTestEnv(t)()
97+
98+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // in org 3
99+
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // own by org 3
100+
session := loginUser(t, user2.Name)
101+
102+
// create a lock
103+
lockPath := "test_lfs_lock_view.zip"
104+
lockID := ""
105+
{
106+
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", repo3.FullName()), map[string]string{"path": lockPath})
107+
req.Header.Set("Accept", lfs.AcceptHeader)
108+
req.Header.Set("Content-Type", lfs.MediaType)
109+
resp := session.MakeRequest(t, req, http.StatusCreated)
110+
lockResp := &api.LFSLockResponse{}
111+
DecodeJSON(t, resp, lockResp)
112+
lockID = lockResp.Lock.ID
113+
}
114+
defer func() {
115+
// release the lock
116+
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/%s/unlock", repo3.FullName(), lockID), map[string]string{})
117+
req.Header.Set("Accept", lfs.AcceptHeader)
118+
req.Header.Set("Content-Type", lfs.MediaType)
119+
session.MakeRequest(t, req, http.StatusOK)
120+
}()
121+
122+
t.Run("owner name", func(t *testing.T) {
123+
defer tests.PrintCurrentTest(t)()
124+
125+
// make sure the display names are different, or the test is meaningless
126+
require.NoError(t, repo3.LoadOwner(context.Background()))
127+
require.NotEqual(t, user2.DisplayName(), repo3.Owner.DisplayName())
128+
129+
req := NewRequest(t, "GET", fmt.Sprintf("/%s/settings/lfs/locks", repo3.FullName()))
130+
resp := session.MakeRequest(t, req, http.StatusOK)
131+
132+
doc := NewHTMLParser(t, resp.Body).doc
133+
134+
tr := doc.Find("table#lfs-files-locks-table tbody tr")
135+
require.Equal(t, 1, tr.Length())
136+
137+
td := tr.First().Find("td")
138+
require.Equal(t, 4, td.Length())
139+
140+
// path
141+
assert.Equal(t, lockPath, strings.TrimSpace(td.Eq(0).Text()))
142+
// owner name
143+
assert.Equal(t, user2.DisplayName(), strings.TrimSpace(td.Eq(1).Text()))
144+
})
145+
}

0 commit comments

Comments
 (0)