]> source.dussan.org Git - gitea.git/commitdiff
Allow to add and remove all repositories to/from team. (#8867)
authorDavid Svantesson <davidsvantesson@gmail.com>
Sat, 9 Nov 2019 00:39:37 +0000 (01:39 +0100)
committerLunny Xiao <xiaolunwen@gmail.com>
Sat, 9 Nov 2019 00:39:37 +0000 (08:39 +0800)
* 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

models/org_team.go
options/locale/locale_en-US.ini
public/css/index.css
public/js/index.js
public/less/_organization.less
routers/org/teams.go
templates/org/team/repositories.tmpl

index d740e1c24075fbd0111fc7ee9d79b365f9ffffb9..126a8c896a2129860f9062b7efc2a84a92079fca 100644 (file)
@@ -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
        }
 
index 932d0bceac93a213a251c21a420b81386c67a884..4433c5bb2abc4769594a47d423c6aa18dd4c01a2 100644 (file)
@@ -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.
index 3ec47eb85cd2d30255954dc67547dcf5fb64d00d..1a6032cf3e3980dba3225a066c1e17fd2132ddb9 100644 (file)
@@ -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}
index 53650890f0c97b540a41b981723621bb9cdf64d4..93ac6a217810ec4660a0325004e8f94811542ad9 100644 (file)
@@ -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 = ['${', '}'];
 
index 27dc7544fda072cf27756ff38d0ad19ac946b55b..6071604cbcd63401d4d947bd459268f3670b98aa 100644 (file)
         }
 
         #add-repo-form,
+        #repo-multiple-form,
         #add-member-form {
             input {
                 margin-left: 0;
                 margin-top: -3px;
             }
         }
+
+        #repo-top-segment {
+            height: 60px;
+        }
     }
 }
index 24612459a4fe7f80ff91b6c5fbaad5c713c4a8dc..36a2847355ae57870e0d5da0c3a0d9484b2a16c1 100644 (file)
@@ -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")
 }
 
index 1b2a411c2bb0d294fa43b8e43b87ea66e536afc4..66af5195b0035f757a191f9f5814102ddebe1b5d 100644 (file)
@@ -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>
                </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" .}}