summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Svantesson <davidsvantesson@gmail.com>2019-11-09 01:39:37 +0100
committerLunny Xiao <xiaolunwen@gmail.com>2019-11-09 08:39:37 +0800
commit9ae4c17cb1fde0eaa1556e545a8871b6a70d2efa (patch)
tree0a62a8bd50dcb4d067f915d02b24207e2a714e5f
parentd2aee2a3e259de0d9dfd20452d55eb0c34ccc874 (diff)
downloadgitea-9ae4c17cb1fde0eaa1556e545a8871b6a70d2efa.tar.gz
gitea-9ae4c17cb1fde0eaa1556e545a8871b6a70d2efa.zip
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 <18600385+guillep2k@users.noreply.github.com> * Move set num repos to lower function. * Make general language sentences
-rw-r--r--models/org_team.go109
-rw-r--r--options/locale/locale_en-US.ini10
-rw-r--r--public/css/index.css5
-rw-r--r--public/js/index.js30
-rw-r--r--public/less/_organization.less5
-rw-r--r--routers/org/teams.go8
-rw-r--r--templates/org/team/repositories.tmpl53
7 files changed, 175 insertions, 45 deletions
diff --git a/models/org_team.go b/models/org_team.go
index d740e1c240..126a8c896a 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -243,6 +243,21 @@ func (t *Team) addAllRepositories(e Engine) error {
return nil
}
+// AddAllRepositories adds all repositories to the team
+func (t *Team) AddAllRepositories() (err error) {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return err
+ }
+
+ if err = t.addAllRepositories(sess); err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
+
// AddRepository adds new repository to team of organization.
func (t *Team) AddRepository(repo *Repository) (err error) {
if repo.OwnerID != t.OrgID {
@@ -264,6 +279,69 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
return sess.Commit()
}
+// RemoveAllRepositories removes all repositories from team and recalculates access
+func (t *Team) RemoveAllRepositories() (err error) {
+ if t.IncludesAllRepositories {
+ return nil
+ }
+
+ sess := x.NewSession()
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return err
+ }
+
+ if err = t.removeAllRepositories(sess); err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
+
+// removeAllRepositories removes all repositories from team and recalculates access
+// Note: Shall not be called if team includes all repositories
+func (t *Team) removeAllRepositories(e Engine) (err error) {
+ // Delete all accesses.
+ for _, repo := range t.Repos {
+ if err := repo.recalculateTeamAccesses(e, t.ID); err != nil {
+ return err
+ }
+
+ // Remove watches from all users and now unaccessible repos
+ for _, user := range t.Members {
+ has, err := hasAccess(e, user.ID, repo)
+ if err != nil {
+ return err
+ } else if has {
+ continue
+ }
+
+ if err = watchRepo(e, user.ID, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err = removeIssueWatchersByRepoID(e, user.ID, repo.ID); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Delete team-repo
+ if _, err := e.
+ Where("team_id=?", t.ID).
+ Delete(new(TeamRepo)); err != nil {
+ return err
+ }
+
+ t.NumRepos = 0
+ if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
+ return err
+ }
+
+ return nil
+}
+
// removeRepository removes a repository from a team and recalculates access
// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
@@ -577,36 +655,7 @@ func DeleteTeam(t *Team) error {
return err
}
- // Delete all accesses.
- for _, repo := range t.Repos {
- if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
- return err
- }
-
- // Remove watches from all users and now unaccessible repos
- for _, user := range t.Members {
- has, err := hasAccess(sess, user.ID, repo)
- if err != nil {
- return err
- } else if has {
- continue
- }
-
- if err = watchRepo(sess, user.ID, repo.ID, false); err != nil {
- return err
- }
-
- // Remove all IssueWatches a user has subscribed to in the repositories
- if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil {
- return err
- }
- }
- }
-
- // Delete team-repo
- if _, err := sess.
- Where("team_id=?", t.ID).
- Delete(new(TeamRepo)); err != nil {
+ if err := t.removeAllRepositories(sess); err != nil {
return err
}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 932d0bceac..4433c5bb2a 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -68,6 +68,10 @@ pull_requests = Pull Requests
issues = Issues
cancel = Cancel
+add = Add
+add_all = Add All
+remove = Remove
+remove_all = Remove All
write = Write
preview = Preview
@@ -1583,8 +1587,10 @@ teams.write_permission_desc = This team grants <strong>Write</strong> access: me
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to and add collaborators to team repositories.
teams.repositories = Team Repositories
teams.search_repo_placeholder = Search repository…
-teams.add_team_repository = Add Team Repository
-teams.remove_repo = Remove
+teams.remove_all_repos_title = Remove all team repositories
+teams.remove_all_repos_desc = This will remove all repositories from the team.
+teams.add_all_repos_title = Add all repositories
+teams.add_all_repos_desc = This will add all the organization's repositories to the team.
teams.add_nonexistent_repo = "The repository you're trying to add does not exist; please create it first."
teams.add_duplicate_users = User is already a team member.
teams.repos.none = No repositories could be accessed by this team.
diff --git a/public/css/index.css b/public/css/index.css
index 3ec47eb85c..1a6032cf3e 100644
--- a/public/css/index.css
+++ b/public/css/index.css
@@ -945,8 +945,9 @@ tbody.commit-list{vertical-align:baseline}
.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}
.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #ddd}
.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}
-.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}
-.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}
+.organization.teams #add-member-form input,.organization.teams #add-repo-form input,.organization.teams #repo-multiple-form input{margin-left:0}
+.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}
+.organization.teams #repo-top-segment{height:60px}
.user:not(.icon){padding-top:15px}
.user.profile .ui.card .username{display:block}
.user.profile .ui.card .extra.content{padding:0}
diff --git a/public/js/index.js b/public/js/index.js
index 53650890f0..93ac6a2178 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -2263,6 +2263,7 @@ $(document).ready(function () {
// Helpers.
$('.delete-button').click(showDeletePopup);
+ $('.add-all-button').click(showAddAllPopup);
$('.delete-branch-button').click(showDeletePopup);
@@ -2501,6 +2502,35 @@ function showDeletePopup() {
return false;
}
+function showAddAllPopup() {
+ const $this = $(this);
+ let filter = "";
+ if ($this.attr("id")) {
+ filter += "#" + $this.attr("id")
+ }
+
+ const dialog = $('.addall.modal' + filter);
+ dialog.find('.name').text($this.data('name'));
+
+ dialog.modal({
+ closable: false,
+ onApprove: function() {
+ if ($this.data('type') == "form") {
+ $($this.data('form')).submit();
+ return;
+ }
+
+ $.post($this.data('url'), {
+ "_csrf": csrf,
+ "id": $this.data("id")
+ }).done(function(data) {
+ window.location.href = data.redirect;
+ });
+ }
+ }).modal('show');
+ return false;
+}
+
function initVueComponents(){
const vueDelimeters = ['${', '}'];
diff --git a/public/less/_organization.less b/public/less/_organization.less
index 27dc7544fd..6071604cbc 100644
--- a/public/less/_organization.less
+++ b/public/less/_organization.less
@@ -152,6 +152,7 @@
}
#add-repo-form,
+ #repo-multiple-form,
#add-member-form {
input {
margin-left: 0;
@@ -162,5 +163,9 @@
margin-top: -3px;
}
}
+
+ #repo-top-segment {
+ height: 60px;
+ }
}
}
diff --git a/routers/org/teams.go b/routers/org/teams.go
index 24612459a4..36a2847355 100644
--- a/routers/org/teams.go
+++ b/routers/org/teams.go
@@ -155,6 +155,10 @@ func TeamsRepoAction(ctx *context.Context) {
err = ctx.Org.Team.AddRepository(repo)
case "remove":
err = ctx.Org.Team.RemoveRepository(com.StrTo(ctx.Query("repoid")).MustInt64())
+ case "addall":
+ err = ctx.Org.Team.AddAllRepositories()
+ case "removeall":
+ err = ctx.Org.Team.RemoveAllRepositories()
}
if err != nil {
@@ -162,6 +166,10 @@ func TeamsRepoAction(ctx *context.Context) {
ctx.ServerError("TeamsRepoAction", err)
return
}
+
+ ctx.JSON(200, map[string]interface{}{
+ "redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories",
+ })
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
}
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index 1b2a411c2b..66af5195b0 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -9,25 +9,33 @@
{{template "org/team/navbar" .}}
{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
{{if $canAddRemove}}
- <div class="ui attached segment">
- <form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
- {{.CsrfTokenHtml}}
- <div class="inline field ui left">
- <div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
- <div class="ui input">
- <input class="prompt" name="repo_name" placeholder="{{.i18n.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
+ <div class="ui attached segment" id="repo-top-segment">
+ <div class="inline ui field left">
+ <form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="inline field ui left">
+ <div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
+ <div class="ui input">
+ <input class="prompt" name="repo_name" placeholder="{{.i18n.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
+ </div>
</div>
</div>
- </div>
- <button class="ui green button">{{.i18n.Tr "org.teams.add_team_repository"}}</button>
- </form>
+ <button class="ui green button">{{.i18n.Tr "add"}}</button>
+ </form>
+ </div>
+ <div class="inline ui field right">
+ <form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/repositories" method="post">
+ <button class="ui red button delete-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/removeall">{{.i18n.Tr "remove_all"}}</button>
+ <button class="ui green button add-all-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/addall">{{.i18n.Tr "add_all"}}</button>
+ </form>
+ </div>
</div>
{{end}}
<div class="ui bottom attached table segment repositories">
{{range .Team.Repos}}
<div class="item">
{{if $canAddRemove}}
- <a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "org.teams.remove_repo"}}</a>
+ <a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "remove"}}</a>
{{end}}
<a class="member" href="{{AppSubUrl}}/{{$.Org.Name}}/{{.Name}}">
<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 @@
</div>
</div>
</div>
+
+<div class="ui small basic delete modal">
+ <div class="ui icon header">
+ <i class="trash icon"></i>
+ {{.i18n.Tr "org.teams.remove_all_repos_title"}}
+ </div>
+ <div class="content">
+ <p>{{.i18n.Tr "org.teams.remove_all_repos_desc"}}</p>
+ </div>
+ {{template "base/delete_modal_actions" .}}
+</div>
+
+<div class="ui small basic addall modal">
+ <div class="ui icon header">
+ <i class="globe icon"></i>
+ {{.i18n.Tr "org.teams.add_all_repos_title"}}
+ </div>
+ <div class="content">
+ <p>{{.i18n.Tr "org.teams.add_all_repos_desc"}}</p>
+ </div>
+ {{template "base/delete_modal_actions" .}}
+</div>
+
{{template "base/footer" .}}