Skip to content

Commit e5f8538

Browse files
committed
Merge branch 'main' into lunny/remove_unnecessary_repo_branch
2 parents c01127b + bf9500b commit e5f8538

File tree

28 files changed

+1482
-1025
lines changed

28 files changed

+1482
-1025
lines changed

models/migrations/migrations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ func prepareMigrationTasks() []*migration {
378378
newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
379379
newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
380380
newMigration(317, "Add new index for action for heatmap", v1_24.AddNewIndexForUserDashboard),
381+
newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
381382
}
382383
return preparedMigrations
383384
}

models/migrations/v1_24/v318.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/models/perm"
8+
9+
"xorm.io/xorm"
10+
)
11+
12+
func AddRepoUnitAnonymousAccessMode(x *xorm.Engine) error {
13+
type RepoUnit struct { //revive:disable-line:exported
14+
AnonymousAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
15+
}
16+
return x.Sync(&RepoUnit{})
17+
}

models/perm/access/repo_permission.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ type Permission struct {
2525
units []*repo_model.RepoUnit
2626
unitsMode map[unit.Type]perm_model.AccessMode
2727

28-
everyoneAccessMode map[unit.Type]perm_model.AccessMode
28+
everyoneAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for every signed-in user
29+
anonymousAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for anonymous (non-signed-in) user
2930
}
3031

3132
// IsOwner returns true if current user is the owner of repository.
@@ -39,7 +40,7 @@ func (p *Permission) IsAdmin() bool {
3940
}
4041

4142
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
42-
// It doesn't count the "everyone access mode".
43+
// It doesn't count the "public(anonymous/everyone) access mode".
4344
func (p *Permission) HasAnyUnitAccess() bool {
4445
for _, v := range p.unitsMode {
4546
if v >= perm_model.AccessModeRead {
@@ -49,7 +50,12 @@ func (p *Permission) HasAnyUnitAccess() bool {
4950
return p.AccessMode >= perm_model.AccessModeRead
5051
}
5152

52-
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
53+
func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
54+
for _, v := range p.anonymousAccessMode {
55+
if v >= perm_model.AccessModeRead {
56+
return true
57+
}
58+
}
5359
for _, v := range p.everyoneAccessMode {
5460
if v >= perm_model.AccessModeRead {
5561
return true
@@ -73,14 +79,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
7379
}
7480

7581
// UnitAccessMode returns current user access mode to the specify unit of the repository
76-
// It also considers "everyone access mode"
82+
// It also considers "public (anonymous/everyone) access mode"
7783
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
7884
// if the units map contains the access mode, use it, but admin/owner mode could override it
7985
if m, ok := p.unitsMode[unitType]; ok {
8086
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
8187
}
8288
// if the units map does not contain the access mode, return the default access mode if the unit exists
83-
unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
89+
unitDefaultAccessMode := p.AccessMode
90+
unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
91+
unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
8492
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
8593
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
8694
}
@@ -171,27 +179,38 @@ func (p *Permission) LogString() string {
171179
format += "\n\tunitsMode[%-v]: %-v"
172180
args = append(args, key.LogString(), value.LogString())
173181
}
182+
format += "\n\tanonymousAccessMode: %-v"
183+
args = append(args, p.anonymousAccessMode)
174184
format += "\n\teveryoneAccessMode: %-v"
175185
args = append(args, p.everyoneAccessMode)
176186
format += "\n\t]>"
177187
return fmt.Sprintf(format, args...)
178188
}
179189

190+
func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
191+
if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
192+
if *modeMap == nil {
193+
*modeMap = make(map[unit.Type]perm_model.AccessMode)
194+
}
195+
(*modeMap)[unitType] = accessMode
196+
}
197+
}
198+
180199
func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
200+
// apply public (anonymous) access permissions
201+
for _, u := range perm.units {
202+
applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
203+
}
204+
181205
if user == nil || user.ID <= 0 {
182206
// for anonymous access, it could be:
183207
// AccessMode is None or Read, units has repo units, unitModes is nil
184208
return
185209
}
186210

187-
// apply everyone access permissions
211+
// apply public (everyone) access permissions
188212
for _, u := range perm.units {
189-
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
190-
if perm.everyoneAccessMode == nil {
191-
perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
192-
}
193-
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
194-
}
213+
applyPublicAccessPermission(u.Type, u.EveryoneAccessMode, &perm.everyoneAccessMode)
195214
}
196215

197216
if perm.unitsMode == nil {
@@ -209,6 +228,11 @@ func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
209228
break
210229
}
211230
}
231+
for t := range perm.anonymousAccessMode {
232+
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
233+
break
234+
}
235+
}
212236
for t := range perm.everyoneAccessMode {
213237
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
214238
break

models/perm/access/repo_permission_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,21 @@ func TestHasAnyUnitAccess(t *testing.T) {
2222
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
2323
}
2424
assert.False(t, perm.HasAnyUnitAccess())
25-
assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
25+
assert.False(t, perm.HasAnyUnitAccessOrPublicAccess())
2626

2727
perm = Permission{
2828
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
2929
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
3030
}
3131
assert.False(t, perm.HasAnyUnitAccess())
32-
assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
32+
assert.True(t, perm.HasAnyUnitAccessOrPublicAccess())
33+
34+
perm = Permission{
35+
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
36+
anonymousAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
37+
}
38+
assert.False(t, perm.HasAnyUnitAccess())
39+
assert.True(t, perm.HasAnyUnitAccessOrPublicAccess())
3340

3441
perm = Permission{
3542
AccessMode: perm_model.AccessModeRead,
@@ -43,7 +50,7 @@ func TestHasAnyUnitAccess(t *testing.T) {
4350
assert.True(t, perm.HasAnyUnitAccess())
4451
}
4552

46-
func TestApplyEveryoneRepoPermission(t *testing.T) {
53+
func TestApplyPublicAccessRepoPermission(t *testing.T) {
4754
perm := Permission{
4855
AccessMode: perm_model.AccessModeNone,
4956
units: []*repo_model.RepoUnit{
@@ -53,6 +60,15 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
5360
finalProcessRepoUnitPermission(nil, &perm)
5461
assert.False(t, perm.CanRead(unit.TypeWiki))
5562

63+
perm = Permission{
64+
AccessMode: perm_model.AccessModeNone,
65+
units: []*repo_model.RepoUnit{
66+
{Type: unit.TypeWiki, AnonymousAccessMode: perm_model.AccessModeRead},
67+
},
68+
}
69+
finalProcessRepoUnitPermission(nil, &perm)
70+
assert.True(t, perm.CanRead(unit.TypeWiki))
71+
5672
perm = Permission{
5773
AccessMode: perm_model.AccessModeNone,
5874
units: []*repo_model.RepoUnit{

models/repo/repo_unit.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ func (err ErrUnitTypeNotExist) Unwrap() error {
4242

4343
// RepoUnit describes all units of a repository
4444
type RepoUnit struct { //revive:disable-line:exported
45-
ID int64
46-
RepoID int64 `xorm:"INDEX(s)"`
47-
Type unit.Type `xorm:"INDEX(s)"`
48-
Config convert.Conversion `xorm:"TEXT"`
49-
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
50-
EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
45+
ID int64
46+
RepoID int64 `xorm:"INDEX(s)"`
47+
Type unit.Type `xorm:"INDEX(s)"`
48+
Config convert.Conversion `xorm:"TEXT"`
49+
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
50+
AnonymousAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
51+
EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
5152
}
5253

5354
func init() {

modules/badge/badge.go

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,56 @@
44
package badge
55

66
import (
7+
"strings"
8+
"unicode"
9+
710
actions_model "code.gitea.io/gitea/models/actions"
811
)
912

1013
// The Badge layout: |offset|label|message|
1114
// We use 10x scale to calculate more precisely
1215
// Then scale down to normal size in tmpl file
1316

14-
type Label struct {
15-
text string
16-
width int
17-
}
18-
19-
func (l Label) Text() string {
20-
return l.text
21-
}
22-
23-
func (l Label) Width() int {
24-
return l.width
25-
}
26-
27-
func (l Label) TextLength() int {
28-
return int(float64(l.width-defaultOffset) * 9.5)
29-
}
30-
31-
func (l Label) X() int {
32-
return l.width*5 + 10
33-
}
34-
35-
type Message struct {
17+
type Text struct {
3618
text string
3719
width int
3820
x int
3921
}
4022

41-
func (m Message) Text() string {
42-
return m.text
23+
func (t Text) Text() string {
24+
return t.text
4325
}
4426

45-
func (m Message) Width() int {
46-
return m.width
27+
func (t Text) Width() int {
28+
return t.width
4729
}
4830

49-
func (m Message) X() int {
50-
return m.x
31+
func (t Text) X() int {
32+
return t.x
5133
}
5234

53-
func (m Message) TextLength() int {
54-
return int(float64(m.width-defaultOffset) * 9.5)
35+
func (t Text) TextLength() int {
36+
return int(float64(t.width-defaultOffset) * 10)
5537
}
5638

5739
type Badge struct {
58-
Color string
59-
FontSize int
60-
Label Label
61-
Message Message
40+
IDPrefix string
41+
FontFamily string
42+
Color string
43+
FontSize int
44+
Label Text
45+
Message Text
6246
}
6347

6448
func (b Badge) Width() int {
6549
return b.Label.width + b.Message.width
6650
}
6751

6852
const (
69-
defaultOffset = 9
70-
defaultFontSize = 11
71-
DefaultColor = "#9f9f9f" // Grey
72-
defaultFontWidth = 7 // approximate speculation
53+
defaultOffset = 10
54+
defaultFontSize = 11
55+
DefaultColor = "#9f9f9f" // Grey
56+
DefaultFontFamily = "DejaVu Sans,Verdana,Geneva,sans-serif"
7357
)
7458

7559
var StatusColorMap = map[actions_model.Status]string{
@@ -85,20 +69,43 @@ var StatusColorMap = map[actions_model.Status]string{
8569

8670
// GenerateBadge generates badge with given template
8771
func GenerateBadge(label, message, color string) Badge {
88-
lw := defaultFontWidth*len(label) + defaultOffset
89-
mw := defaultFontWidth*len(message) + defaultOffset
90-
x := lw*10 + mw*5 - 10
72+
lw := calculateTextWidth(label) + defaultOffset
73+
mw := calculateTextWidth(message) + defaultOffset
74+
75+
lx := lw * 5
76+
mx := lw*10 + mw*5 - 10
9177
return Badge{
92-
Label: Label{
78+
FontFamily: DefaultFontFamily,
79+
Label: Text{
9380
text: label,
9481
width: lw,
82+
x: lx,
9583
},
96-
Message: Message{
84+
Message: Text{
9785
text: message,
9886
width: mw,
99-
x: x,
87+
x: mx,
10088
},
10189
FontSize: defaultFontSize * 10,
10290
Color: color,
10391
}
10492
}
93+
94+
func calculateTextWidth(text string) int {
95+
width := 0
96+
widthData := DejaVuGlyphWidthData()
97+
for _, char := range strings.TrimSpace(text) {
98+
charWidth, ok := widthData[char]
99+
if !ok {
100+
// use the width of 'm' in case of missing glyph width data for a printable character
101+
if unicode.IsPrint(char) {
102+
charWidth = widthData['m']
103+
} else {
104+
charWidth = 0
105+
}
106+
}
107+
width += int(charWidth)
108+
}
109+
110+
return width
111+
}

0 commit comments

Comments
 (0)