Skip to content

Commit 9ae4c17

Browse files
davidsvantessonlunny
authored andcommitted
Allow to add and remove all repositories to/from team. (#8867)
* Allow to add and remove all repositories to team. * Change style, buttons on same row. * Apply suggestions from code review Grammar Co-Authored-By: guillep2k <[email protected]> * Move set num repos to lower function. * Make general language sentences
1 parent d2aee2a commit 9ae4c17

File tree

7 files changed

+175
-45
lines changed

7 files changed

+175
-45
lines changed

models/org_team.go

+79-30
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,21 @@ func (t *Team) addAllRepositories(e Engine) error {
243243
return nil
244244
}
245245

246+
// AddAllRepositories adds all repositories to the team
247+
func (t *Team) AddAllRepositories() (err error) {
248+
sess := x.NewSession()
249+
defer sess.Close()
250+
if err = sess.Begin(); err != nil {
251+
return err
252+
}
253+
254+
if err = t.addAllRepositories(sess); err != nil {
255+
return err
256+
}
257+
258+
return sess.Commit()
259+
}
260+
246261
// AddRepository adds new repository to team of organization.
247262
func (t *Team) AddRepository(repo *Repository) (err error) {
248263
if repo.OwnerID != t.OrgID {
@@ -264,6 +279,69 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
264279
return sess.Commit()
265280
}
266281

282+
// RemoveAllRepositories removes all repositories from team and recalculates access
283+
func (t *Team) RemoveAllRepositories() (err error) {
284+
if t.IncludesAllRepositories {
285+
return nil
286+
}
287+
288+
sess := x.NewSession()
289+
defer sess.Close()
290+
if err = sess.Begin(); err != nil {
291+
return err
292+
}
293+
294+
if err = t.removeAllRepositories(sess); err != nil {
295+
return err
296+
}
297+
298+
return sess.Commit()
299+
}
300+
301+
// removeAllRepositories removes all repositories from team and recalculates access
302+
// Note: Shall not be called if team includes all repositories
303+
func (t *Team) removeAllRepositories(e Engine) (err error) {
304+
// Delete all accesses.
305+
for _, repo := range t.Repos {
306+
if err := repo.recalculateTeamAccesses(e, t.ID); err != nil {
307+
return err
308+
}
309+
310+
// Remove watches from all users and now unaccessible repos
311+
for _, user := range t.Members {
312+
has, err := hasAccess(e, user.ID, repo)
313+
if err != nil {
314+
return err
315+
} else if has {
316+
continue
317+
}
318+
319+
if err = watchRepo(e, user.ID, repo.ID, false); err != nil {
320+
return err
321+
}
322+
323+
// Remove all IssueWatches a user has subscribed to in the repositories
324+
if err = removeIssueWatchersByRepoID(e, user.ID, repo.ID); err != nil {
325+
return err
326+
}
327+
}
328+
}
329+
330+
// Delete team-repo
331+
if _, err := e.
332+
Where("team_id=?", t.ID).
333+
Delete(new(TeamRepo)); err != nil {
334+
return err
335+
}
336+
337+
t.NumRepos = 0
338+
if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
339+
return err
340+
}
341+
342+
return nil
343+
}
344+
267345
// removeRepository removes a repository from a team and recalculates access
268346
// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
269347
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
@@ -577,36 +655,7 @@ func DeleteTeam(t *Team) error {
577655
return err
578656
}
579657

580-
// Delete all accesses.
581-
for _, repo := range t.Repos {
582-
if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
583-
return err
584-
}
585-
586-
// Remove watches from all users and now unaccessible repos
587-
for _, user := range t.Members {
588-
has, err := hasAccess(sess, user.ID, repo)
589-
if err != nil {
590-
return err
591-
} else if has {
592-
continue
593-
}
594-
595-
if err = watchRepo(sess, user.ID, repo.ID, false); err != nil {
596-
return err
597-
}
598-
599-
// Remove all IssueWatches a user has subscribed to in the repositories
600-
if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil {
601-
return err
602-
}
603-
}
604-
}
605-
606-
// Delete team-repo
607-
if _, err := sess.
608-
Where("team_id=?", t.ID).
609-
Delete(new(TeamRepo)); err != nil {
658+
if err := t.removeAllRepositories(sess); err != nil {
610659
return err
611660
}
612661

options/locale/locale_en-US.ini

+8-2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ pull_requests = Pull Requests
6868
issues = Issues
6969

7070
cancel = Cancel
71+
add = Add
72+
add_all = Add All
73+
remove = Remove
74+
remove_all = Remove All
7175

7276
write = Write
7377
preview = Preview
@@ -1583,8 +1587,10 @@ teams.write_permission_desc = This team grants <strong>Write</strong> access: me
15831587
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to and add collaborators to team repositories.
15841588
teams.repositories = Team Repositories
15851589
teams.search_repo_placeholder = Search repository…
1586-
teams.add_team_repository = Add Team Repository
1587-
teams.remove_repo = Remove
1590+
teams.remove_all_repos_title = Remove all team repositories
1591+
teams.remove_all_repos_desc = This will remove all repositories from the team.
1592+
teams.add_all_repos_title = Add all repositories
1593+
teams.add_all_repos_desc = This will add all the organization's repositories to the team.
15881594
teams.add_nonexistent_repo = "The repository you're trying to add does not exist; please create it first."
15891595
teams.add_duplicate_users = User is already a team member.
15901596
teams.repos.none = No repositories could be accessed by this team.

public/css/index.css

+3-2
Original file line numberDiff line numberDiff line change
@@ -945,8 +945,9 @@ tbody.commit-list{vertical-align:baseline}
945945
.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}
946946
.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #ddd}
947947
.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}
948-
.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}
949-
.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}
948+
.organization.teams #add-member-form input,.organization.teams #add-repo-form input,.organization.teams #repo-multiple-form input{margin-left:0}
949+
.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button,.organization.teams #repo-multiple-form .ui.button{margin-left:5px;margin-top:-3px}
950+
.organization.teams #repo-top-segment{height:60px}
950951
.user:not(.icon){padding-top:15px}
951952
.user.profile .ui.card .username{display:block}
952953
.user.profile .ui.card .extra.content{padding:0}

public/js/index.js

+30
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,7 @@ $(document).ready(function () {
22632263

22642264
// Helpers.
22652265
$('.delete-button').click(showDeletePopup);
2266+
$('.add-all-button').click(showAddAllPopup);
22662267

22672268
$('.delete-branch-button').click(showDeletePopup);
22682269

@@ -2501,6 +2502,35 @@ function showDeletePopup() {
25012502
return false;
25022503
}
25032504

2505+
function showAddAllPopup() {
2506+
const $this = $(this);
2507+
let filter = "";
2508+
if ($this.attr("id")) {
2509+
filter += "#" + $this.attr("id")
2510+
}
2511+
2512+
const dialog = $('.addall.modal' + filter);
2513+
dialog.find('.name').text($this.data('name'));
2514+
2515+
dialog.modal({
2516+
closable: false,
2517+
onApprove: function() {
2518+
if ($this.data('type') == "form") {
2519+
$($this.data('form')).submit();
2520+
return;
2521+
}
2522+
2523+
$.post($this.data('url'), {
2524+
"_csrf": csrf,
2525+
"id": $this.data("id")
2526+
}).done(function(data) {
2527+
window.location.href = data.redirect;
2528+
});
2529+
}
2530+
}).modal('show');
2531+
return false;
2532+
}
2533+
25042534
function initVueComponents(){
25052535
const vueDelimeters = ['${', '}'];
25062536

public/less/_organization.less

+5
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
}
153153

154154
#add-repo-form,
155+
#repo-multiple-form,
155156
#add-member-form {
156157
input {
157158
margin-left: 0;
@@ -162,5 +163,9 @@
162163
margin-top: -3px;
163164
}
164165
}
166+
167+
#repo-top-segment {
168+
height: 60px;
169+
}
165170
}
166171
}

routers/org/teams.go

+8
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,21 @@ func TeamsRepoAction(ctx *context.Context) {
155155
err = ctx.Org.Team.AddRepository(repo)
156156
case "remove":
157157
err = ctx.Org.Team.RemoveRepository(com.StrTo(ctx.Query("repoid")).MustInt64())
158+
case "addall":
159+
err = ctx.Org.Team.AddAllRepositories()
160+
case "removeall":
161+
err = ctx.Org.Team.RemoveAllRepositories()
158162
}
159163

160164
if err != nil {
161165
log.Error("Action(%s): '%s' %v", ctx.Params(":action"), ctx.Org.Team.Name, err)
162166
ctx.ServerError("TeamsRepoAction", err)
163167
return
164168
}
169+
170+
ctx.JSON(200, map[string]interface{}{
171+
"redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories",
172+
})
165173
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
166174
}
167175

templates/org/team/repositories.tmpl

+42-11
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,33 @@
99
{{template "org/team/navbar" .}}
1010
{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
1111
{{if $canAddRemove}}
12-
<div class="ui attached segment">
13-
<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
14-
{{.CsrfTokenHtml}}
15-
<div class="inline field ui left">
16-
<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
17-
<div class="ui input">
18-
<input class="prompt" name="repo_name" placeholder="{{.i18n.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
12+
<div class="ui attached segment" id="repo-top-segment">
13+
<div class="inline ui field left">
14+
<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
15+
{{.CsrfTokenHtml}}
16+
<div class="inline field ui left">
17+
<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
18+
<div class="ui input">
19+
<input class="prompt" name="repo_name" placeholder="{{.i18n.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
20+
</div>
1921
</div>
2022
</div>
21-
</div>
22-
<button class="ui green button">{{.i18n.Tr "org.teams.add_team_repository"}}</button>
23-
</form>
23+
<button class="ui green button">{{.i18n.Tr "add"}}</button>
24+
</form>
25+
</div>
26+
<div class="inline ui field right">
27+
<form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/repositories" method="post">
28+
<button class="ui red button delete-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/removeall">{{.i18n.Tr "remove_all"}}</button>
29+
<button class="ui green button add-all-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/addall">{{.i18n.Tr "add_all"}}</button>
30+
</form>
31+
</div>
2432
</div>
2533
{{end}}
2634
<div class="ui bottom attached table segment repositories">
2735
{{range .Team.Repos}}
2836
<div class="item">
2937
{{if $canAddRemove}}
30-
<a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "org.teams.remove_repo"}}</a>
38+
<a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "remove"}}</a>
3139
{{end}}
3240
<a class="member" href="{{AppSubUrl}}/{{$.Org.Name}}/{{.Name}}">
3341
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
@@ -44,4 +52,27 @@
4452
</div>
4553
</div>
4654
</div>
55+
56+
<div class="ui small basic delete modal">
57+
<div class="ui icon header">
58+
<i class="trash icon"></i>
59+
{{.i18n.Tr "org.teams.remove_all_repos_title"}}
60+
</div>
61+
<div class="content">
62+
<p>{{.i18n.Tr "org.teams.remove_all_repos_desc"}}</p>
63+
</div>
64+
{{template "base/delete_modal_actions" .}}
65+
</div>
66+
67+
<div class="ui small basic addall modal">
68+
<div class="ui icon header">
69+
<i class="globe icon"></i>
70+
{{.i18n.Tr "org.teams.add_all_repos_title"}}
71+
</div>
72+
<div class="content">
73+
<p>{{.i18n.Tr "org.teams.add_all_repos_desc"}}</p>
74+
</div>
75+
{{template "base/delete_modal_actions" .}}
76+
</div>
77+
4778
{{template "base/footer" .}}

0 commit comments

Comments
 (0)