diff options
-rw-r--r-- | models/org_team.go | 109 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 10 | ||||
-rw-r--r-- | public/css/index.css | 5 | ||||
-rw-r--r-- | public/js/index.js | 30 | ||||
-rw-r--r-- | public/less/_organization.less | 5 | ||||
-rw-r--r-- | routers/org/teams.go | 8 | ||||
-rw-r--r-- | templates/org/team/repositories.tmpl | 53 |
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" .}} |