Skip to content

Commit d272df5

Browse files
authored
Merge branch 'main' into refactor-cookie
2 parents 1a9872c + b7221be commit d272df5

File tree

11 files changed

+180
-48
lines changed

11 files changed

+180
-48
lines changed

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,8 @@ var migrations = []Migration{
481481
NewMigration("Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch),
482482
// v251 -> v252
483483
NewMigration("Fix incorrect owner team unit access mode", v1_20.FixIncorrectOwnerTeamUnitAccessMode),
484+
// v252 -> v253
485+
NewMigration("Fix incorrect admin team unit access mode", v1_20.FixIncorrectAdminTeamUnitAccessMode),
484486
}
485487

486488
// GetCurrentDBVersion returns the current db version

models/migrations/v1_20/v252.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_20 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/modules/log"
8+
9+
"xorm.io/xorm"
10+
)
11+
12+
func FixIncorrectAdminTeamUnitAccessMode(x *xorm.Engine) error {
13+
type UnitType int
14+
type AccessMode int
15+
16+
type TeamUnit struct {
17+
ID int64 `xorm:"pk autoincr"`
18+
OrgID int64 `xorm:"INDEX"`
19+
TeamID int64 `xorm:"UNIQUE(s)"`
20+
Type UnitType `xorm:"UNIQUE(s)"`
21+
AccessMode AccessMode
22+
}
23+
24+
const (
25+
// AccessModeAdmin admin access
26+
AccessModeAdmin = 3
27+
)
28+
29+
sess := x.NewSession()
30+
defer sess.Close()
31+
32+
if err := sess.Begin(); err != nil {
33+
return err
34+
}
35+
36+
count, err := sess.Table("team_unit").
37+
Where("team_id IN (SELECT id FROM team WHERE authorize = ?)", AccessModeAdmin).
38+
Update(&TeamUnit{
39+
AccessMode: AccessModeAdmin,
40+
})
41+
if err != nil {
42+
return err
43+
}
44+
log.Debug("Updated %d admin team unit access mode to belong to admin instead of none", count)
45+
46+
return sess.Commit()
47+
}

options/locale/locale_en-US.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ buttons.list.task.tooltip = Add a list of tasks
133133
buttons.mention.tooltip = Mention a user or team
134134
buttons.ref.tooltip = Reference an issue or pull request
135135
buttons.switch_to_legacy.tooltip = Use the legacy editor instead
136+
buttons.enable_monospace_font = Enable monospace font
137+
buttons.disable_monospace_font = Disable monospace font
136138

137139
[filter]
138140
string.asc = A - Z

routers/api/v1/org/team.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
166166
}
167167
}
168168

169+
func attachAdminTeamUnits(team *organization.Team) {
170+
team.Units = make([]*organization.TeamUnit, 0, len(unit_model.AllRepoUnitTypes))
171+
for _, ut := range unit_model.AllRepoUnitTypes {
172+
up := perm.AccessModeAdmin
173+
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
174+
up = perm.AccessModeRead
175+
}
176+
team.Units = append(team.Units, &organization.TeamUnit{
177+
OrgID: team.OrgID,
178+
Type: ut,
179+
AccessMode: up,
180+
})
181+
}
182+
}
183+
169184
// CreateTeam api for create a team
170185
func CreateTeam(ctx *context.APIContext) {
171186
// swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
@@ -213,6 +228,8 @@ func CreateTeam(ctx *context.APIContext) {
213228
ctx.Error(http.StatusInternalServerError, "getTeamUnits", errors.New("units permission should not be empty"))
214229
return
215230
}
231+
} else {
232+
attachAdminTeamUnits(team)
216233
}
217234

218235
if err := models.NewTeam(team); err != nil {
@@ -300,6 +317,8 @@ func EditTeam(ctx *context.APIContext) {
300317
} else if len(form.Units) > 0 {
301318
attachTeamUnits(team, form.Units)
302319
}
320+
} else {
321+
attachAdminTeamUnits(team)
303322
}
304323

305324
if err := models.UpdateTeam(team, isAuthChanged, isIncludeAllChanged); err != nil {

routers/web/org/teams.go

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org
66

77
import (
8+
"fmt"
89
"net/http"
910
"net/url"
1011
"path"
@@ -264,14 +265,26 @@ func NewTeam(ctx *context.Context) {
264265
ctx.HTML(http.StatusOK, tplTeamNew)
265266
}
266267

267-
func getUnitPerms(forms url.Values) map[unit_model.Type]perm.AccessMode {
268+
func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode {
268269
unitPerms := make(map[unit_model.Type]perm.AccessMode)
269-
for k, v := range forms {
270-
if strings.HasPrefix(k, "unit_") {
271-
t, _ := strconv.Atoi(k[5:])
272-
if t > 0 {
273-
vv, _ := strconv.Atoi(v[0])
274-
unitPerms[unit_model.Type(t)] = perm.AccessMode(vv)
270+
for _, ut := range unit_model.AllRepoUnitTypes {
271+
// Default accessmode is none
272+
unitPerms[ut] = perm.AccessModeNone
273+
274+
v, ok := forms[fmt.Sprintf("unit_%d", ut)]
275+
if ok {
276+
vv, _ := strconv.Atoi(v[0])
277+
if teamPermission >= perm.AccessModeAdmin {
278+
unitPerms[ut] = teamPermission
279+
// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
280+
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
281+
unitPerms[ut] = perm.AccessModeRead
282+
}
283+
} else {
284+
unitPerms[ut] = perm.AccessMode(vv)
285+
if unitPerms[ut] >= perm.AccessModeAdmin {
286+
unitPerms[ut] = perm.AccessModeWrite
287+
}
275288
}
276289
}
277290
}
@@ -282,8 +295,8 @@ func getUnitPerms(forms url.Values) map[unit_model.Type]perm.AccessMode {
282295
func NewTeamPost(ctx *context.Context) {
283296
form := web.GetForm(ctx).(*forms.CreateTeamForm)
284297
includesAllRepositories := form.RepoAccess == "all"
285-
unitPerms := getUnitPerms(ctx.Req.Form)
286298
p := perm.ParseAccessMode(form.Permission)
299+
unitPerms := getUnitPerms(ctx.Req.Form, p)
287300
if p < perm.AccessModeAdmin {
288301
// if p is less than admin accessmode, then it should be general accessmode,
289302
// so we should calculate the minial accessmode from units accessmodes.
@@ -299,17 +312,15 @@ func NewTeamPost(ctx *context.Context) {
299312
CanCreateOrgRepo: form.CanCreateOrgRepo,
300313
}
301314

302-
if t.AccessMode < perm.AccessModeAdmin {
303-
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
304-
for tp, perm := range unitPerms {
305-
units = append(units, &org_model.TeamUnit{
306-
OrgID: ctx.Org.Organization.ID,
307-
Type: tp,
308-
AccessMode: perm,
309-
})
310-
}
311-
t.Units = units
315+
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
316+
for tp, perm := range unitPerms {
317+
units = append(units, &org_model.TeamUnit{
318+
OrgID: ctx.Org.Organization.ID,
319+
Type: tp,
320+
AccessMode: perm,
321+
})
312322
}
323+
t.Units = units
313324

314325
ctx.Data["Title"] = ctx.Org.Organization.FullName
315326
ctx.Data["PageIsOrgTeams"] = true
@@ -422,8 +433,11 @@ func SearchTeam(ctx *context.Context) {
422433
func EditTeam(ctx *context.Context) {
423434
ctx.Data["Title"] = ctx.Org.Organization.FullName
424435
ctx.Data["PageIsOrgTeams"] = true
425-
ctx.Data["team_name"] = ctx.Org.Team.Name
426-
ctx.Data["desc"] = ctx.Org.Team.Description
436+
if err := ctx.Org.Team.LoadUnits(ctx); err != nil {
437+
ctx.ServerError("LoadUnits", err)
438+
return
439+
}
440+
ctx.Data["Team"] = ctx.Org.Team
427441
ctx.Data["Units"] = unit_model.Units
428442
ctx.HTML(http.StatusOK, tplTeamNew)
429443
}
@@ -432,7 +446,13 @@ func EditTeam(ctx *context.Context) {
432446
func EditTeamPost(ctx *context.Context) {
433447
form := web.GetForm(ctx).(*forms.CreateTeamForm)
434448
t := ctx.Org.Team
435-
unitPerms := getUnitPerms(ctx.Req.Form)
449+
newAccessMode := perm.ParseAccessMode(form.Permission)
450+
unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode)
451+
if newAccessMode < perm.AccessModeAdmin {
452+
// if newAccessMode is less than admin accessmode, then it should be general accessmode,
453+
// so we should calculate the minial accessmode from units accessmodes.
454+
newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
455+
}
436456
isAuthChanged := false
437457
isIncludeAllChanged := false
438458
includesAllRepositories := form.RepoAccess == "all"
@@ -443,14 +463,6 @@ func EditTeamPost(ctx *context.Context) {
443463
ctx.Data["Units"] = unit_model.Units
444464

445465
if !t.IsOwnerTeam() {
446-
// Validate permission level.
447-
newAccessMode := perm.ParseAccessMode(form.Permission)
448-
if newAccessMode < perm.AccessModeAdmin {
449-
// if p is less than admin accessmode, then it should be general accessmode,
450-
// so we should calculate the minial accessmode from units accessmodes.
451-
newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
452-
}
453-
454466
t.Name = form.TeamName
455467
if t.AccessMode != newAccessMode {
456468
isAuthChanged = true
@@ -467,21 +479,16 @@ func EditTeamPost(ctx *context.Context) {
467479
}
468480

469481
t.Description = form.Description
470-
if t.AccessMode < perm.AccessModeAdmin {
471-
units := make([]org_model.TeamUnit, 0, len(unitPerms))
472-
for tp, perm := range unitPerms {
473-
units = append(units, org_model.TeamUnit{
474-
OrgID: t.OrgID,
475-
TeamID: t.ID,
476-
Type: tp,
477-
AccessMode: perm,
478-
})
479-
}
480-
if err := org_model.UpdateTeamUnits(t, units); err != nil {
481-
ctx.Error(http.StatusInternalServerError, "UpdateTeamUnits", err.Error())
482-
return
483-
}
482+
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
483+
for tp, perm := range unitPerms {
484+
units = append(units, &org_model.TeamUnit{
485+
OrgID: t.OrgID,
486+
TeamID: t.ID,
487+
Type: tp,
488+
AccessMode: perm,
489+
})
484490
}
491+
t.Units = units
485492

486493
if ctx.HasError() {
487494
ctx.HTML(http.StatusOK, tplTeamNew)

templates/org/team/new.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
</td>
110110
<td class="center aligned">
111111
<div class="ui radio checkbox">
112-
<input type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (eq ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.write_access"}}">
112+
<input type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (ge ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.write_access"}}">
113113
</div>
114114
</td>
115115
</tr>
@@ -137,7 +137,7 @@
137137
{{else}}
138138
<button class="ui green button">{{.locale.Tr "org.teams.update_settings"}}</button>
139139
{{if not (eq .Team.LowerName "owners")}}
140-
<button class="ui red button delete-button" data-url="{{.OrgLink}}/teams/{{.team_name | PathEscape}}/delete">{{.locale.Tr "org.teams.delete_team"}}</button>
140+
<button class="ui red button delete-button" data-url="{{.OrgLink}}/teams/{{.Team.Name | PathEscape}}/delete">{{.locale.Tr "org.teams.delete_team"}}</button>
141141
{{end}}
142142
{{end}}
143143
</div>

templates/shared/combomarkdowneditor.tmpl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,18 @@ Template Attributes:
4040
<md-ref class="markdown-toolbar-button" data-tooltip-content="{{.locale.Tr "editor.buttons.ref.tooltip"}}">{{svg "octicon-cross-reference"}}</md-ref>
4141
</div>
4242
<div class="markdown-toolbar-group">
43+
<button class="markdown-toolbar-button markdown-switch-monospace" role="switch" data-enable-text="{{.locale.Tr "editor.buttons.enable_monospace_font"}}" data-disable-text="{{.locale.Tr "editor.buttons.disable_monospace_font"}}">{{svg "octicon-typography"}}</button>
4344
<button class="markdown-toolbar-button markdown-switch-easymde" data-tooltip-content="{{.locale.Tr "editor.buttons.switch_to_legacy.tooltip"}}">{{svg "octicon-arrow-switch"}}</button>
4445
</div>
4546
</markdown-toolbar>
4647
<text-expander keys=": @">
4748
<textarea class="markdown-text-editor js-quick-submit"{{if .TextareaName}} name="{{.TextareaName}}"{{end}}{{if .TextareaPlaceholder}} placeholder="{{.TextareaPlaceholder}}"{{end}}{{if .TextareaAriaLabel}} aria-label="{{.TextareaAriaLabel}}"{{end}}>{{.TextareaContent}}</textarea>
4849
</text-expander>
50+
<script>
51+
if (localStorage?.getItem('markdown-editor-monospace') === 'true') {
52+
document.querySelector('.markdown-text-editor').classList.add('gt-mono');
53+
}
54+
</script>
4955
</div>
5056
<div class="ui tab markup" data-tab-panel="markdown-previewer">
5157
{{.locale.Tr "loading"}}

tests/integration/api_team_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
auth_model "code.gitea.io/gitea/models/auth"
1313
"code.gitea.io/gitea/models/db"
1414
"code.gitea.io/gitea/models/organization"
15+
"code.gitea.io/gitea/models/perm"
1516
"code.gitea.io/gitea/models/repo"
1617
"code.gitea.io/gitea/models/unit"
1718
"code.gitea.io/gitea/models/unittest"
@@ -189,6 +190,36 @@ func TestAPITeam(t *testing.T) {
189190
req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
190191
MakeRequest(t, req, http.StatusNoContent)
191192
unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID})
193+
194+
// Create admin team
195+
teamToCreate = &api.CreateTeamOption{
196+
Name: "teamadmin",
197+
Description: "team admin",
198+
IncludesAllRepositories: true,
199+
Permission: "admin",
200+
}
201+
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate)
202+
resp = MakeRequest(t, req, http.StatusCreated)
203+
apiTeam = api.Team{}
204+
DecodeJSON(t, resp, &apiTeam)
205+
for _, ut := range unit.AllRepoUnitTypes {
206+
up := perm.AccessModeAdmin
207+
if ut == unit.TypeExternalTracker || ut == unit.TypeExternalWiki {
208+
up = perm.AccessModeRead
209+
}
210+
unittest.AssertExistsAndLoadBean(t, &organization.TeamUnit{
211+
OrgID: org.ID,
212+
TeamID: apiTeam.ID,
213+
Type: ut,
214+
AccessMode: up,
215+
})
216+
}
217+
teamID = apiTeam.ID
218+
219+
// Delete team.
220+
req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
221+
MakeRequest(t, req, http.StatusNoContent)
222+
unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID})
192223
}
193224

194225
func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) {

web_src/css/editor-markdown.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
user-select: none;
2525
padding: 5px;
2626
cursor: pointer;
27+
color: var(--color-text);
2728
}
2829

2930
.combo-markdown-editor .markdown-toolbar-button:hover {

web_src/css/helpers.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
.gt-mono {
3131
font-family: var(--fonts-monospace) !important;
32-
font-size: .9em !important; /* compensate for monospace fonts being usually slightly larger */
32+
font-size: .95em !important; /* compensate for monospace fonts being usually slightly larger */
3333
}
3434

3535
.gt-bold { font-weight: 600 !important; }

web_src/js/features/comp/ComboMarkdownEditor.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,25 @@ class ComboMarkdownEditor {
7373
// upstream bug: The role code is never executed in base MarkdownButtonElement https://github.com/github/markdown-toolbar-element/issues/70
7474
el.setAttribute('role', 'button');
7575
}
76-
this.switchToEasyMDEButton = this.container.querySelector('.markdown-switch-easymde');
77-
this.switchToEasyMDEButton?.addEventListener('click', async (e) => {
76+
77+
const monospaceButton = this.container.querySelector('.markdown-switch-monospace');
78+
const monospaceEnabled = localStorage?.getItem('markdown-editor-monospace') === 'true';
79+
const monospaceText = monospaceButton.getAttribute(monospaceEnabled ? 'data-disable-text' : 'data-enable-text');
80+
monospaceButton.setAttribute('data-tooltip-content', monospaceText);
81+
monospaceButton.setAttribute('aria-checked', String(monospaceEnabled));
82+
83+
monospaceButton?.addEventListener('click', (e) => {
84+
e.preventDefault();
85+
const enabled = localStorage?.getItem('markdown-editor-monospace') !== 'true';
86+
localStorage.setItem('markdown-editor-monospace', String(enabled));
87+
this.textarea.classList.toggle('gt-mono', enabled);
88+
const text = monospaceButton.getAttribute(enabled ? 'data-disable-text' : 'data-enable-text');
89+
monospaceButton.setAttribute('data-tooltip-content', text);
90+
monospaceButton.setAttribute('aria-checked', String(enabled));
91+
});
92+
93+
const easymdeButton = this.container.querySelector('.markdown-switch-easymde');
94+
easymdeButton?.addEventListener('click', async (e) => {
7895
e.preventDefault();
7996
this.userPreferredEditor = 'easymde';
8097
await this.switchToEasyMDE();

0 commit comments

Comments
 (0)