]> source.dussan.org Git - gitea.git/commitdiff
Finish new edit team page, add member to team
authorUnknwon <joe2010xtmf@163.com>
Sun, 24 Aug 2014 13:09:05 +0000 (21:09 +0800)
committerUnknwon <joe2010xtmf@163.com>
Sun, 24 Aug 2014 13:09:05 +0000 (21:09 +0800)
24 files changed:
cmd/web.go
conf/locale/locale_en-US.ini
conf/locale/locale_zh-CN.ini
gogs.go
models/org.go
models/repo.go
models/user.go
modules/middleware/context.go
modules/middleware/org.go
modules/middleware/repo.go
public/ng/css/gogs.css
public/ng/js/gogs.js
public/ng/less/gogs/organization.less
public/ng/less/gogs/repository.less
routers/org/members.go
routers/org/teams.go
routers/repo/setting.go
templates/.VERSION
templates/org/home.tmpl
templates/org/member/members.tmpl
templates/org/team/members.tmpl
templates/org/team/new.tmpl
templates/org/team/sidebar.tmpl
templates/org/team/teams.tmpl

index 064a0358f934287be56008ea0617a1baca645150..0b3b84ab6a6c767c813140b9bc3a503d8a436e1d 100644 (file)
@@ -48,7 +48,7 @@ and it takes care of all the other things for you`,
        Flags:  []cli.Flag{},
 }
 
-// checkVersion checks if binary matches the version of temolate files.
+// checkVersion checks if binary matches the version of templates files.
 func checkVersion() {
        data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/.VERSION"))
        if err != nil {
@@ -235,7 +235,7 @@ func runWeb(*cli.Context) {
                        r.Get("/members/action/:action", org.MembersAction)
 
                        r.Get("/teams", org.Teams)
-                       r.Get("/teams/:team", org.SingleTeam)
+                       r.Get("/teams/:team", org.TeamMembers)
                        r.Get("/teams/:team/action/:action", org.TeamsAction)
                }, middleware.OrgAssignment(true, true))
 
@@ -243,6 +243,8 @@ func runWeb(*cli.Context) {
                        r.Get("/teams/new", org.NewTeam)
                        r.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
                        r.Get("/teams/:team/edit", org.EditTeam)
+                       r.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost)
+                       r.Post("/teams/:team/delete", org.DeleteTeam)
 
                        m.Group("/settings", func(r *macaron.Router) {
                                r.Get("", org.Settings)
index b9e7966c2fef9bfcffddaa71cf03c7c51159371c..9437bfd19706b111ec0d33dd7791ba0a8d94fe45 100644 (file)
@@ -283,6 +283,13 @@ teams.no_desc = This team has no description
 teams.settings = Settings
 teams.owners_permission_desc = Owners have full access to <strong>all repositories</strong> and have <strong>admin rights</strong> to the organization.
 teams.members = Team Members
+teams.update_settings = Update Settings
+teams.delete_team = Delete This Team
+teams.add_team_member = Add Team Member
+teams.delete_team_success = Given team has been successfully deleted.
+teams.read_permission_desc = This team grants <strong>Read</strong> access: members can view and clone the team's repositories.
+teams.write_permission_desc = This team grants <strong>Write</strong> access: members can read from and push to the team's repositories.
+teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to, and add collaborators to the team's repositories.
 
 [action]
 create_repo = created repository <a href="/%s">%s</a>
index 7e5ac6486eda3620be1ed98387510063c9617f59..dbc94f3a0c6a704dfe15c3edff7c449865889d48 100644 (file)
@@ -283,6 +283,13 @@ teams.no_desc = 该团队暂无描述
 teams.settings = 团队设置
 teams.owners_permission_desc = 管理员团队对 <strong>所有仓库</strong> 具有操作权限,且对组织具有 <strong>管理员权限</strong>。
 teams.members = 团队成员
+teams.update_settings = 更新团队设置
+teams.delete_team = 删除当前团队
+teams.add_team_member = 添加团队成员
+teams.delete_team_success = 指定团队已经被成功删除!
+teams.read_permission_desc = 该团队拥有对所属仓库的 <strong>读取</strong> 权限,团队成员可以进行查看和克隆等只读操作。
+teams.write_permission_desc = 该团队拥有对所属仓库的 <strong>读取</strong> 和 <strong>写入</strong> 的权限。
+teams.admin_permission_desc = 该团队拥有一定的 <strong>管理</strong> 权限,团队成员可以读取、克隆、推送以及添加其它仓库协作者。
 
 [action]
 create_repo = 创建了仓库 <a href="/%s">%s</a>
diff --git a/gogs.go b/gogs.go
index 1e493b92bca7affabb06956088b5ac6bf8e3c518..5c93ae26d543ea67d1186d61c89fb720b182e002 100644 (file)
--- a/gogs.go
+++ b/gogs.go
@@ -17,7 +17,7 @@ import (
        "github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.4.7.0823 Alpha"
+const APP_VER = "0.4.7.0824 Alpha"
 
 func init() {
        runtime.GOMAXPROCS(runtime.NumCPU())
index cd4163bab3316818ff607a460d558bea69ccdc53..27228382d362ee3b46b081bbece2644a93fb515b 100644 (file)
@@ -6,11 +6,13 @@ package models
 
 import (
        "errors"
+       "fmt"
        "os"
        "path"
        "strings"
 
        "github.com/Unknwon/com"
+       "github.com/go-xorm/xorm"
 
        "github.com/gogits/gogs/modules/base"
 )
@@ -134,10 +136,10 @@ func CreateOrganization(org, owner *User) (*User, error) {
 
        // Add initial creator to organization and owner team.
        ou := &OrgUser{
-               Uid:     owner.Id,
-               OrgId:   org.Id,
-               IsOwner: true,
-               NumTeam: 1,
+               Uid:      owner.Id,
+               OrgId:    org.Id,
+               IsOwner:  true,
+               NumTeams: 1,
        }
        if _, err = sess.Insert(ou); err != nil {
                sess.Rollback()
@@ -199,7 +201,7 @@ type OrgUser struct {
        OrgId    int64 `xorm:"INDEX UNIQUE(s)"`
        IsPublic bool
        IsOwner  bool
-       NumTeam  int
+       NumTeams int
 }
 
 // IsOrganizationOwner returns true if given user is in the owner team.
@@ -255,17 +257,17 @@ func AddOrgUser(orgId, uid int64) error {
                return nil
        }
 
-       ou := &OrgUser{
-               Uid:   uid,
-               OrgId: orgId,
-       }
-
        sess := x.NewSession()
        defer sess.Close()
        if err := sess.Begin(); err != nil {
                return err
        }
 
+       ou := &OrgUser{
+               Uid:   uid,
+               OrgId: orgId,
+       }
+
        if _, err := sess.Insert(ou); err != nil {
                sess.Rollback()
                return err
@@ -288,12 +290,17 @@ func RemoveOrgUser(orgId, uid int64) error {
                return nil
        }
 
+       u, err := GetUserById(uid)
+       if err != nil {
+               return err
+       }
+       org, err := GetUserById(orgId)
+       if err != nil {
+               return err
+       }
+
        // Check if the user to delete is the last member in owner team.
        if IsOrganizationOwner(orgId, uid) {
-               org, err := GetUserById(orgId)
-               if err != nil {
-                       return err
-               }
                t, err := org.GetOwnerTeam()
                if err != nil {
                        return err
@@ -317,6 +324,33 @@ func RemoveOrgUser(orgId, uid int64) error {
                return err
        }
 
+       // Delete all repository accesses.
+       if err = org.GetRepositories(); err != nil {
+               sess.Rollback()
+               return err
+       }
+       access := &Access{
+               UserName: u.LowerName,
+       }
+       for _, repo := range org.Repos {
+               access.RepoName = path.Join(org.LowerName, repo.LowerName)
+               if _, err = sess.Delete(access); err != nil {
+                       sess.Rollback()
+                       return err
+               }
+       }
+
+       // Delete member in his/her teams.
+       ts, err := GetUserTeams(org.Id, u.Id)
+       if err != nil {
+               return err
+       }
+       for _, t := range ts {
+               if err = removeTeamMemberWithSess(org.Id, t.Id, u.Id, sess); err != nil {
+                       return err
+               }
+       }
+
        return sess.Commit()
 }
 
@@ -352,6 +386,11 @@ type Team struct {
        NumMembers  int
 }
 
+// IsOwnerTeam returns true if team is owner team.
+func (t *Team) IsOwnerTeam() bool {
+       return t.Name == OWNER_TEAM
+}
+
 // IsTeamMember returns true if given user is a member of team.
 func (t *Team) IsMember(uid int64) bool {
        return IsTeamMember(t.OrgId, t.Id, uid)
@@ -362,7 +401,10 @@ func (t *Team) GetRepositories() error {
        idStrs := strings.Split(t.RepoIds, "|")
        t.Repos = make([]*Repository, 0, len(idStrs))
        for _, str := range idStrs {
-               id := com.StrTo(str).MustInt64()
+               if len(str) == 0 {
+                       continue
+               }
+               id := com.StrTo(str[1:]).MustInt64()
                if id == 0 {
                        continue
                }
@@ -459,15 +501,177 @@ func GetTeamById(teamId int64) (*Team, error) {
        return t, nil
 }
 
+// GetHighestAuthorize returns highest repository authorize level for given user and team.
+func GetHighestAuthorize(orgId, uid, teamId, repoId int64) (AuthorizeType, error) {
+       ts, err := GetUserTeams(orgId, uid)
+       if err != nil {
+               return 0, err
+       }
+
+       var auth AuthorizeType = 0
+       for _, t := range ts {
+               // Not current team and has given repository.
+               if t.Id != teamId && strings.Contains(t.RepoIds, "$"+com.ToStr(repoId)+"|") {
+                       // Fast return.
+                       if t.Authorize == ORG_WRITABLE {
+                               return ORG_WRITABLE, nil
+                       }
+                       if t.Authorize > auth {
+                               auth = t.Authorize
+                       }
+               }
+       }
+       return auth, nil
+}
+
 // UpdateTeam updates information of team.
-func UpdateTeam(t *Team) error {
+func UpdateTeam(t *Team, authChanged bool) (err error) {
+       if !IsLegalName(t.Name) {
+               return ErrTeamNameIllegal
+       }
+
        if len(t.Description) > 255 {
                t.Description = t.Description[:255]
        }
 
+       sess := x.NewSession()
+       defer sess.Close()
+       if err = sess.Begin(); err != nil {
+               return err
+       }
+
+       // Update access for team members if needed.
+       if authChanged && !t.IsOwnerTeam() {
+               if err = t.GetRepositories(); err != nil {
+                       return err
+               } else if err = t.GetMembers(); err != nil {
+                       return err
+               }
+
+               // Get organization.
+               org, err := GetUserById(t.OrgId)
+               if err != nil {
+                       return err
+               }
+
+               mode := READABLE
+               if t.Authorize > ORG_READABLE {
+                       mode = WRITABLE
+               }
+               access := &Access{
+                       Mode: mode,
+               }
+
+               for _, repo := range t.Repos {
+                       access.RepoName = path.Join(org.LowerName, repo.LowerName)
+                       for _, u := range t.Members {
+                               // ORG_WRITABLE is the highest authorize level for now.
+                               // Skip checking others if current team has this level.
+                               if t.Authorize < ORG_WRITABLE {
+                                       auth, err := GetHighestAuthorize(org.Id, u.Id, t.Id, repo.Id)
+                                       if err != nil {
+                                               sess.Rollback()
+                                               return err
+                                       }
+                                       if auth >= t.Authorize {
+                                               continue // Other team has higher or same authorize level.
+                                       }
+                               }
+
+                               access.UserName = u.LowerName
+                               if _, err = sess.Update(access); err != nil {
+                                       sess.Rollback()
+                                       return err
+                               }
+                       }
+               }
+       }
+
        t.LowerName = strings.ToLower(t.Name)
-       _, err := x.Id(t.Id).AllCols().Update(t)
-       return err
+       if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+               sess.Rollback()
+               return err
+       }
+       return sess.Commit()
+}
+
+// DeleteTeam deletes given team.
+// It's caller's responsibility to assign organization ID.
+func DeleteTeam(t *Team) error {
+       if err := t.GetRepositories(); err != nil {
+               return err
+       } else if err = t.GetMembers(); err != nil {
+               return err
+       }
+
+       // Get organization.
+       org, err := GetUserById(t.OrgId)
+       if err != nil {
+               return err
+       }
+
+       sess := x.NewSession()
+       defer sess.Close()
+       if err = sess.Begin(); err != nil {
+               return err
+       }
+
+       // Delete all accesses.
+       mode := READABLE
+       if t.Authorize > ORG_READABLE {
+               mode = WRITABLE
+       }
+       access := new(Access)
+
+       for _, repo := range t.Repos {
+               access.RepoName = path.Join(org.LowerName, repo.LowerName)
+               for _, u := range t.Members {
+                       access.UserName = u.LowerName
+                       access.Mode = mode
+                       auth, err := GetHighestAuthorize(org.Id, u.Id, t.Id, repo.Id)
+                       if err != nil {
+                               sess.Rollback()
+                               return err
+                       }
+
+                       if auth == 0 {
+                               if _, err = sess.Delete(access); err != nil {
+                                       sess.Rollback()
+                                       return err
+                               }
+                       } else if auth < t.Authorize {
+                               // Downgrade authorize level.
+                               mode := READABLE
+                               if auth > ORG_READABLE {
+                                       mode = WRITABLE
+                               }
+                               access.Mode = mode
+                               if _, err = sess.Update(access); err != nil {
+                                       sess.Rollback()
+                                       return err
+                               }
+                       }
+               }
+       }
+
+       // Delete team-user.
+       if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.Id).Delete(new(TeamUser)); err != nil {
+               sess.Rollback()
+               return err
+       }
+
+       // Delete team.
+       if _, err = sess.Id(t.Id).Delete(new(Team)); err != nil {
+               sess.Rollback()
+               return err
+       }
+       // Update organization number of teams.
+       if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams - 1 WHERE id = ?", t.OrgId); err != nil {
+               sess.Rollback()
+               return err
+       }
+
+       return sess.Commit()
 }
 
 // ___________                    ____ ___
@@ -509,12 +713,37 @@ func GetTeamMembers(orgId, teamId int64) ([]*User, error) {
        return us, nil
 }
 
+// GetUserTeams returns all teams that user belongs to in given origanization.
+func GetUserTeams(orgId, uid int64) ([]*Team, error) {
+       tus := make([]*TeamUser, 0, 5)
+       if err := x.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
+               return nil, err
+       }
+
+       ts := make([]*Team, len(tus))
+       for i, tu := range tus {
+               t := new(Team)
+               has, err := x.Id(tu.TeamId).Get(t)
+               if err != nil {
+                       return nil, err
+               } else if !has {
+                       return nil, ErrTeamNotExist
+               }
+               ts[i] = t
+       }
+       return ts, nil
+}
+
 // AddTeamMember adds new member to given team of given organization.
 func AddTeamMember(orgId, teamId, uid int64) error {
-       if !IsOrganizationMember(orgId, uid) || IsTeamMember(orgId, teamId, uid) {
+       if IsTeamMember(orgId, teamId, uid) {
                return nil
        }
 
+       if err := AddOrgUser(orgId, uid); err != nil {
+               return err
+       }
+
        // Get team and its repositories.
        t, err := GetTeamById(teamId)
        if err != nil {
@@ -569,18 +798,49 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 
        // Give access to team repositories.
        for _, repo := range t.Repos {
-               access.RepoName = path.Join(org.LowerName, repo.LowerName)
-               if _, err = sess.Insert(access); err != nil {
+               auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
+               if err != nil {
                        sess.Rollback()
                        return err
                }
+
+               access.Id = 0
+               access.RepoName = path.Join(org.LowerName, repo.LowerName)
+               // Equal 0 means given access doesn't exist.
+               if auth == 0 {
+                       if _, err = sess.Insert(access); err != nil {
+                               sess.Rollback()
+                               return err
+                       }
+               } else if auth < t.Authorize {
+                       if _, err = sess.Update(access); err != nil {
+                               sess.Rollback()
+                               return err
+                       }
+               }
+       }
+       fmt.Println("kao")
+
+       // We make sure it exists before.
+       ou := new(OrgUser)
+       _, err = sess.Where("uid=?", uid).And("org_id=?", orgId).Get(ou)
+       if err != nil {
+               sess.Rollback()
+               return err
+       }
+       ou.NumTeams++
+       if t.IsOwnerTeam() {
+               ou.IsOwner = true
+       }
+       if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil {
+               sess.Rollback()
+               return err
        }
 
        return sess.Commit()
 }
 
-// RemoveTeamMember removes member from given team of given organization.
-func RemoveTeamMember(orgId, teamId, uid int64) error {
+func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) error {
        if !IsTeamMember(orgId, teamId, uid) {
                return nil
        }
@@ -590,6 +850,12 @@ func RemoveTeamMember(orgId, teamId, uid int64) error {
        if err != nil {
                return err
        }
+
+       // Check if the user to delete is the last member in owner team.
+       if t.IsOwnerTeam() && t.NumMembers == 1 {
+               return ErrLastOrgOwner
+       }
+
        t.NumMembers--
 
        if err = t.GetRepositories(); err != nil {
@@ -608,22 +874,12 @@ func RemoveTeamMember(orgId, teamId, uid int64) error {
                return err
        }
 
-       sess := x.NewSession()
-       defer sess.Close()
-       if err := sess.Begin(); err != nil {
-               return err
-       }
-
        tu := &TeamUser{
                Uid:    uid,
                OrgId:  orgId,
                TeamId: teamId,
        }
 
-       access := &Access{
-               UserName: u.LowerName,
-       }
-
        if _, err := sess.Delete(tu); err != nil {
                sess.Rollback()
                return err
@@ -633,13 +889,63 @@ func RemoveTeamMember(orgId, teamId, uid int64) error {
        }
 
        // Delete access to team repositories.
+       access := &Access{
+               UserName: u.LowerName,
+       }
+
        for _, repo := range t.Repos {
-               access.RepoName = path.Join(org.LowerName, repo.LowerName)
-               if _, err = sess.Delete(access); err != nil {
+               auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
+               if err != nil {
                        sess.Rollback()
                        return err
                }
+
+               // Delete access if this is the last team user belongs to.
+               if auth == 0 {
+                       access.RepoName = path.Join(org.LowerName, repo.LowerName)
+                       _, err = sess.Delete(access)
+               } else if auth < t.Authorize {
+                       // Downgrade authorize level.
+                       mode := READABLE
+                       if auth > ORG_READABLE {
+                               mode = WRITABLE
+                       }
+                       access.Mode = mode
+                       _, err = sess.Update(access)
+               }
+               if err != nil {
+                       sess.Rollback()
+                       return err
+               }
+       }
+
+       // This must exist.
+       ou := new(OrgUser)
+       _, err = sess.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou)
+       if err != nil {
+               sess.Rollback()
+               return err
+       }
+       ou.NumTeams--
+       if t.IsOwnerTeam() {
+               ou.IsOwner = false
+       }
+       if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil {
+               sess.Rollback()
+               return err
        }
+       return nil
+}
 
+// RemoveTeamMember removes member from given team of given organization.
+func RemoveTeamMember(orgId, teamId, uid int64) error {
+       sess := x.NewSession()
+       defer sess.Close()
+       if err := sess.Begin(); err != nil {
+               return err
+       }
+       if err := removeTeamMemberWithSess(orgId, teamId, uid, sess); err != nil {
+               return err
+       }
        return sess.Commit()
 }
index 9666192df66b50d01f6e92de9e478099ee97a602..f5d1ca834e29cc6512084e0f9855ef51bb05338f 100644 (file)
@@ -525,6 +525,7 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
                        return nil, err
                }
                for _, u := range us {
+                       access.Id = 0
                        access.UserName = u.LowerName
                        if _, err = sess.Insert(access); err != nil {
                                sess.Rollback()
@@ -707,6 +708,10 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) (err error) {
 
 // ChangeRepositoryName changes all corresponding setting from old repository name to new one.
 func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) {
+       if !IsLegalName(newRepoName) {
+               return ErrRepoNameIllegal
+       }
+
        // Update accesses.
        accesses := make([]Access, 0, 10)
        if err = x.Find(&accesses, &Access{RepoName: strings.ToLower(userName + "/" + oldRepoName)}); err != nil {
index 757c290b9c6c03a8397255b61545e3e72f22bba1..e8db1ad157985cd3af0b676dbe7f0cf9b82aff89 100644 (file)
@@ -54,7 +54,8 @@ type User struct {
        LoginSource   int64 `xorm:"not null default 0"`
        LoginName     string
        Type          UserType
-       Orgs          []*User `xorm:"-"`
+       Orgs          []*User       `xorm:"-"`
+       Repos         []*Repository `xorm:"-"`
        NumFollowers  int
        NumFollowings int
        NumStars      int
@@ -143,6 +144,12 @@ func (u *User) GetOrganizationCount() (int64, error) {
        return x.Where("uid=?", u.Id).Count(new(OrgUser))
 }
 
+// GetRepositories returns all repositories that user owns, including private repositories.
+func (u *User) GetRepositories() (err error) {
+       u.Repos, err = GetRepositories(u.Id, true)
+       return err
+}
+
 // GetOrganizations returns all organizations that user belongs to.
 func (u *User) GetOrganizations() error {
        ous, err := GetOrgUsersByUserId(u.Id)
index 80975e999e5846557f3fee34f0d6b1a1c2e98bca..3ef1b1d620843a0cae82cd67b8f0e2cb5aa4a032 100644 (file)
@@ -46,6 +46,7 @@ type Context struct {
                IsBranch    bool
                IsTag       bool
                IsCommit    bool
+               IsAdmin     bool // Current user is admin level.
                HasAccess   bool
                Repository  *models.Repository
                Owner       *models.User
index c85221a5abbde0964b86bf3e0ef216288d790476..ee4460b274bf992dcef0b8121eaed75431f2765b 100644 (file)
@@ -8,6 +8,7 @@ import (
        "github.com/Unknwon/macaron"
 
        "github.com/gogits/gogs/models"
+       "github.com/gogits/gogs/modules/log"
 )
 
 func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
@@ -35,6 +36,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
                        if err == models.ErrUserNotExist {
                                ctx.Handle(404, "GetUserByName", err)
                        } else if redirect {
+                               log.Error(4, "GetUserByName", err)
                                ctx.Redirect("/")
                        } else {
                                ctx.Handle(500, "GetUserByName", err)
@@ -52,17 +54,14 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
                        } else {
                                if org.IsOrgMember(ctx.User.Id) {
                                        ctx.Org.IsMember = true
-                                       // TODO: ctx.Org.IsAdminTeam
                                }
                        }
                }
                if (requireMember && !ctx.Org.IsMember) ||
-                       (requireOwner && !ctx.Org.IsOwner) ||
-                       (requireAdminTeam && !ctx.Org.IsAdminTeam) {
+                       (requireOwner && !ctx.Org.IsOwner) {
                        ctx.Handle(404, "OrgAssignment", err)
                        return
                }
-               ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam
                ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
 
                ctx.Org.OrgLink = "/org/" + org.Name
@@ -76,6 +75,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
                                if err == models.ErrTeamNotExist {
                                        ctx.Handle(404, "GetTeam", err)
                                } else if redirect {
+                                       log.Error(4, "GetTeam", err)
                                        ctx.Redirect("/")
                                } else {
                                        ctx.Handle(500, "GetTeam", err)
@@ -83,6 +83,12 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
                                return
                        }
                        ctx.Data["Team"] = ctx.Org.Team
+                       ctx.Org.IsAdminTeam = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize == models.ORG_ADMIN
+               }
+               ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam
+               if requireAdminTeam && !ctx.Org.IsAdminTeam {
+                       ctx.Handle(404, "OrgAssignment", err)
+                       return
                }
        }
 }
index 3db1932af0be6458f76c4fe6452f511973aaf8f9..68a9a2d7fb2cde775bd5a5f3dd47da3060fda34c 100644 (file)
@@ -59,6 +59,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
                                if err == models.ErrUserNotExist {
                                        ctx.Handle(404, "GetUserByName", err)
                                } else if redirect {
+                                       log.Error(4, "GetUserByName", err)
                                        ctx.Redirect("/")
                                } else {
                                        ctx.Handle(500, "GetUserByName", err)
@@ -84,7 +85,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
                        ctx.Repo.IsTrueOwner = true
                }
 
-               // get repository
+               // Get repository.
                repo, err := models.GetRepositoryByName(u.Id, repoName)
                if err != nil {
                        if err == models.ErrRepoNotExist {
@@ -102,8 +103,22 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
                }
 
                // Check if the mirror repository owner(mirror repository doesn't have access).
-               if ctx.IsSigned && !ctx.Repo.IsOwner && repo.OwnerId == ctx.User.Id {
-                       ctx.Repo.IsOwner = true
+               if ctx.IsSigned && !ctx.Repo.IsOwner {
+                       if repo.OwnerId == ctx.User.Id {
+                               ctx.Repo.IsOwner = true
+                       }
+                       // Check if current user has admin permission to repository.
+                       if u.IsOrganization() {
+                               auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, 0, repo.Id)
+                               if err != nil {
+                                       ctx.Handle(500, "GetHighestAuthorize", err)
+                                       return
+                               }
+                               if auth == models.ORG_ADMIN {
+                                       ctx.Repo.IsOwner = true
+                                       ctx.Repo.IsAdmin = true
+                               }
+                       }
                }
 
                // Check access.
@@ -281,7 +296,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 
 func RequireTrueOwner() macaron.Handler {
        return func(ctx *Context) {
-               if !ctx.Repo.IsTrueOwner {
+               if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
                        if !ctx.IsSigned {
                                ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI))
                                ctx.Redirect("/user/login")
index 9aa7206bdf41bfae3983c68953f4794f8430a99b..48468c7e80b8a28130b9f7a96368a0bda3cd741f 100644 (file)
@@ -1298,27 +1298,33 @@ The register and sign-in page style
 .repo-setting-zone {
   padding: 30px;
 }
+#team-members-list,
 #repo-collab-list {
   list-style: none;
   padding: 10px 0 5px 0;
 }
+#team-members-list li.collab,
 #repo-collab-list li.collab {
   clear: both;
   height: 50px;
   padding: 0 15px 0 15px;
 }
+#team-members-list a.member,
 #repo-collab-list a.member {
   color: #444;
   height: 50px;
   line-height: 50px;
 }
+#team-members-list a.member:hover,
 #repo-collab-list a.member:hover {
   color: #4183C4;
 }
+#team-members-list .avatar,
 #repo-collab-list .avatar {
   margin-right: 1em;
   width: 40px;
 }
+#team-members-list .remove-collab,
 #repo-collab-list .remove-collab {
   color: #DD4B39;
 }
@@ -1871,3 +1877,14 @@ textarea#issue-add-content {
 #org-team-card .panel-footer {
   padding: 10px 20px;
 }
+#team-members-list .panel-body .search {
+  padding: 4px 0 10px 10px;
+  border-bottom: 1px solid #dddddd;
+}
+#team-members-list li.collab {
+  padding-top: 10px !important;
+  border-bottom: 1px solid #dddddd;
+}
+#team-members-list li.collab:last-child {
+  border-bottom: 0;
+}
index 5e6a6a6b91f9dc0816339921d1db6112236c7b53..52000f3664cc37753510b60d29b68e3b95145190 100644 (file)
@@ -351,6 +351,41 @@ function initInvite() {
     });
 }
 
+function initOrgTeamCreate() {
+    // Delete team.
+    $('#org-team-delete').click(function (e) {
+        if (!confirm('This team is going to be deleted, do you want to continue?')) {
+            e.preventDefault();
+            return true;
+        }
+        var $form = $('#team-create-form')
+        $form.attr('action', $form.data('delete-url'));
+    });
+}
+
+function initTeamMembersList() {
+    // Add team member.
+    var $ul = $('#org-team-members-list');
+    $('#org-team-members-add').on('keyup', function () {
+        var $this = $(this);
+        if (!$this.val()) {
+            $ul.toggleHide();
+            return;
+        }
+        Gogs.searchUsers($this.val(), $ul);
+    }).on('focus', function () {
+        if (!$(this).val()) {
+            $ul.toggleHide();
+        } else {
+            $ul.toggleShow();
+        }
+    }).next().next().find('ul').on("click", 'li', function () {
+        $('#org-team-members-add').val($(this).text());
+        $ul.toggleHide();
+    });
+
+}
+
 $(document).ready(function () {
     initCore();
     if ($('#user-profile-setting').length) {
@@ -368,6 +403,12 @@ $(document).ready(function () {
     if ($('#invite-box').length) {
         initInvite();
     }
+    if ($('#team-create-form').length) {
+        initOrgTeamCreate();
+    }
+    if ($('#team-members-list').length) {
+        initTeamMembersList();
+    }
 
     Tabs('#dashboard-sidebar-menu');
 
index 58039d90b3d96fbb72645c9843895aba8d7b7dc8..a62dcbb3e043ac0ff0333f26bfeb8b643548a9dc 100644 (file)
        .panel-footer {
                padding: 10px 20px;
        }
+}
+#team-members-list {
+       .panel-body .search {
+               padding: 4px 0 10px 10px;
+               border-bottom: 1px solid #dddddd;
+       }
+}
+#team-members-list {
+       li.collab {
+               padding-top: 10px !important;
+               border-bottom: 1px solid #dddddd;
+               &:last-child {
+                       border-bottom: 0;
+               }
+       }
 }
\ No newline at end of file
index 8f9a97fa02a6c23f3ca74957d8e7115bbd3aab1c..2f97289852e085652b341f76fb8a535742b2e587 100644 (file)
@@ -426,6 +426,7 @@ border-top-right-radius: .25em;
 .repo-setting-zone {
        padding: 30px;
 }
+#team-members-list,
 #repo-collab-list {
        list-style: none;
        padding: 10px 0 5px 0;
index 1e249e8be2e86c4346ee12cf28ceb3792e3f38f2..823daec94831647c135cf3dbe2a022d56b2948af 100644 (file)
@@ -82,7 +82,12 @@ func MembersAction(ctx *middleware.Context) {
                })
                return
        }
-       ctx.Redirect(ctx.Org.OrgLink + "/members")
+
+       if ctx.Params(":action") != "leave" {
+               ctx.Redirect(ctx.Org.OrgLink + "/members")
+       } else {
+               ctx.Redirect("/")
+       }
 }
 
 func Invitation(ctx *middleware.Context) {
index 8eb86c49fd8b6e1871ee41edda04621c9c5bf256..4c986d4aa0c5c51b2fa07a1a03cd2405399c1aa0 100644 (file)
@@ -5,6 +5,8 @@
 package org
 
 import (
+       "github.com/Unknwon/com"
+
        "github.com/gogits/gogs/models"
        "github.com/gogits/gogs/modules/auth"
        "github.com/gogits/gogs/modules/base"
@@ -39,23 +41,71 @@ func Teams(ctx *middleware.Context) {
 }
 
 func TeamsAction(ctx *middleware.Context) {
+       uid := com.StrTo(ctx.Query("uid")).MustInt64()
+       if uid == 0 {
+               ctx.Redirect(ctx.Org.OrgLink + "/teams")
+               return
+       }
+
+       page := ctx.Query("page")
        var err error
        switch ctx.Params(":action") {
        case "join":
+               if !ctx.Org.IsOwner {
+                       ctx.Error(404)
+                       return
+               }
                err = ctx.Org.Team.AddMember(ctx.User.Id)
        case "leave":
                err = ctx.Org.Team.RemoveMember(ctx.User.Id)
+       case "remove":
+               if !ctx.Org.IsOwner {
+                       ctx.Error(404)
+                       return
+               }
+               err = ctx.Org.Team.RemoveMember(uid)
+               page = "team"
+       case "add":
+               if !ctx.Org.IsOwner {
+                       ctx.Error(404)
+                       return
+               }
+               uname := ctx.Query("uname")
+               var u *models.User
+               u, err = models.GetUserByName(uname)
+               if err != nil {
+                       if err == models.ErrUserNotExist {
+                               ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
+                               ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
+                       } else {
+                               ctx.Handle(500, " GetUserByName", err)
+                       }
+                       return
+               }
+
+               err = ctx.Org.Team.AddMember(u.Id)
+               page = "team"
        }
 
        if err != nil {
-               log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
-               ctx.JSON(200, map[string]interface{}{
-                       "ok":  false,
-                       "err": err.Error(),
-               })
-               return
+               if err == models.ErrLastOrgOwner {
+                       ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
+               } else {
+                       log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
+                       ctx.JSON(200, map[string]interface{}{
+                               "ok":  false,
+                               "err": err.Error(),
+                       })
+                       return
+               }
+       }
+
+       switch page {
+       case "team":
+               ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
+       default:
+               ctx.Redirect(ctx.Org.OrgLink + "/teams")
        }
-       ctx.Redirect(ctx.Org.OrgLink + "/teams")
 }
 
 func NewTeam(ctx *middleware.Context) {
@@ -116,13 +166,76 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
        ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
 }
 
+func TeamMembers(ctx *middleware.Context) {
+       ctx.Data["Title"] = ctx.Org.Team.Name
+       ctx.Data["PageIsOrgTeams"] = true
+       if err := ctx.Org.Team.GetMembers(); err != nil {
+               ctx.Handle(500, "GetMembers", err)
+               return
+       }
+       ctx.HTML(200, TEAM_MEMBERS)
+}
+
 func EditTeam(ctx *middleware.Context) {
-       ctx.Data["Title"] = "Organization " + ctx.Params(":org") + " Edit Team"
-       ctx.HTML(200, "org/edit_team")
+       ctx.Data["Title"] = ctx.Org.Organization.FullName
+       ctx.Data["PageIsOrgTeams"] = true
+       ctx.Data["team_name"] = ctx.Org.Team.Name
+       ctx.Data["desc"] = ctx.Org.Team.Description
+       ctx.HTML(200, TEAM_NEW)
 }
 
-func SingleTeam(ctx *middleware.Context) {
-       ctx.Data["Title"] = ctx.Org.Team.Name
+func EditTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
+       t := ctx.Org.Team
+       ctx.Data["Title"] = ctx.Org.Organization.FullName
        ctx.Data["PageIsOrgTeams"] = true
-       ctx.HTML(200, TEAM_MEMBERS)
+       ctx.Data["team_name"] = t.Name
+       ctx.Data["desc"] = t.Description
+
+       if ctx.HasError() {
+               ctx.HTML(200, TEAM_NEW)
+               return
+       }
+
+       isAuthChanged := false
+       if !t.IsOwnerTeam() {
+               // Validate permission level.
+               var auth models.AuthorizeType
+               switch form.Permission {
+               case "read":
+                       auth = models.ORG_READABLE
+               case "write":
+                       auth = models.ORG_WRITABLE
+               case "admin":
+                       auth = models.ORG_ADMIN
+               default:
+                       ctx.Error(401)
+                       return
+               }
+
+               t.Name = form.TeamName
+               if t.Authorize != auth {
+                       isAuthChanged = true
+                       t.Authorize = auth
+               }
+       }
+       t.Description = form.Description
+       if err := models.UpdateTeam(t, isAuthChanged); err != nil {
+               if err == models.ErrTeamNameIllegal {
+                       ctx.Data["Err_TeamName"] = true
+                       ctx.RenderWithErr(ctx.Tr("form.illegal_team_name"), TEAM_NEW, &form)
+               } else {
+                       ctx.Handle(500, "UpdateTeam", err)
+               }
+               return
+       }
+       ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
+}
+
+func DeleteTeam(ctx *middleware.Context) {
+       if err := models.DeleteTeam(ctx.Org.Team); err != nil {
+               ctx.Handle(500, "DeleteTeam", err)
+               return
+       }
+       ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
+       ctx.Redirect(ctx.Org.OrgLink + "/teams")
 }
index 10a6f72db7f929320735cb173b7be96c1578a273..866c0cd9362b09afd17c6089a6de2367c2d8bff9 100644 (file)
@@ -56,7 +56,12 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
                                ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, nil)
                                return
                        } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
-                               ctx.Handle(500, "ChangeRepositoryName", err)
+                               if err == models.ErrRepoNameIllegal {
+                                       ctx.Data["Err_RepoName"] = true
+                                       ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), SETTINGS_OPTIONS, nil)
+                               } else {
+                                       ctx.Handle(500, "ChangeRepositoryName", err)
+                               }
                                return
                        }
                        log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName)
@@ -185,9 +190,24 @@ func SettingsCollaboration(ctx *middleware.Context) {
        // Delete collaborator.
        remove := strings.ToLower(ctx.Query("remove"))
        if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
-               if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil {
-                       ctx.Handle(500, "DeleteAccess", err)
-                       return
+               needDelete := true
+               if ctx.User.IsOrganization() {
+                       // Check if user belongs to a team that has access to this repository.
+                       auth, err := models.GetHighestAuthorize(ctx.Repo.Owner.Id, ctx.User.Id, 0, ctx.Repo.Repository.Id)
+                       if err != nil {
+                               ctx.Handle(500, "GetHighestAuthorize", err)
+                               return
+                       }
+                       if auth > 0 {
+                               needDelete = false
+                       }
+               }
+
+               if needDelete {
+                       if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil {
+                               ctx.Handle(500, "DeleteAccess", err)
+                               return
+                       }
                }
                ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
                ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
index f152becda31fde36514aeed73dca772b54efefbf..3e68f858b5548eb2ecabb9cd096c364da9752e1e 100644 (file)
@@ -1 +1 @@
-0.4.7.0823 Alpha
\ No newline at end of file
+0.4.7.0824 Alpha
\ No newline at end of file
index afd695ec7d6b37e5a7be329d9366853a76778aef..6f3078de50259b1db51ec63987e2e9c19a40541f 100644 (file)
@@ -18,6 +18,7 @@
        </div>
 </div>
 <div class="container">
+       {{$isMember := .Org.IsOrgMember $.SignedUser.Id}}
     <div id="org-home-repo-list" class="left grid-2-3">
         <div class="clear">
                {{if .IsOrganizationOwner}}
@@ -26,6 +27,7 @@
         </div>
         <div id="org-repo-list">
                        {{range .Repos}}
+                               {{if or $isMember (not .IsPrivate)}}
                                <div class="org-repo-item">
                     <ul class="org-repo-status right">
                         <li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
@@ -35,6 +37,7 @@
                                        <p class="org-repo-description">{{.Description}}</p>
                                        <p class="org-repo-updated">{{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Updated $.i18n.Lang}}</p>
                                </div>
+                               {{end}}
                        {{end}}
         </div>
     </div>
        <div class="org-sidebar">
                <div class="panel panel-radius">
                        <div class="panel-header">
+                               {{if $isMember}}
                                <a class="text-grey right" href="/org/{{.Org.LowerName}}/members"><strong>{{.Org.NumMembers}}</strong><span class="octicon octicon-chevron-right"></span></a>
+                               {{end}}
                                <strong>{{.i18n.Tr "org.people"}}</strong>
                        </div>
                        <div class="panel-body member-avatar-group">
                                {{range .Members}}
-                               <a href="/{{.Name}}" title="{{.Name}}"><img src="{{.AvatarLink}}"></a>
+                                               {{if or $isMember (.IsPublicMember $.Org.Id)}}
+                                       <a href="/{{.Name}}" title="{{.Name}}"><img src="{{.AvatarLink}}"></a>
+                                       {{end}}
                                {{end}}
                        </div>
                        {{if .IsOrganizationOwner}}
@@ -56,6 +63,7 @@
                        </div>
                        {{end}}
                </div>
+               {{if $isMember}}
                <br>
                <div class="panel panel-radius">
                        <div class="panel-header">
@@ -76,9 +84,9 @@
                        <div class="panel-footer">
                                <a class="btn btn-medium btn-blue btn-link btn-radius" href="/org/{{$.Org.LowerName}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a>
                        </div>
-
                        {{end}}
                </div>
+               {{end}}
        </div>
     </div>
 </div>
index 3ab92bbfc0b651694f7b05f86a34a8b10f010459..1c530982fb22229c6c0dc14ef15fa28e76f6c272 100644 (file)
@@ -6,7 +6,7 @@
                {{template "ng/base/alert" .}}
        </div>
        <div class="org-toolbar clear">
-               {{if .IsAdminTeam}}
+               {{if .IsOrganizationOwner}}
         <a class="btn btn-green btn-large btn-link btn-radius right" href="{{.OrgLink}}/invitations/new"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "org.invite_someone"}}</a>
                {{end}}
        </div>
index 8faeb9ef31acb3fff429cc615c120d34d5c3a33b..d3176be1526275cfe0efc192d2f0798825ca92c3 100644 (file)
@@ -2,7 +2,8 @@
 {{template "ng/base/header" .}}
 {{template "org/base/header" .}}
 <div id="setting-wrapper" class="main-wrapper">
-    <div id="org-setting" class="container clear">
+    <div id="team-members-list" class="container clear">
+               {{template "ng/base/alert" .}}
                {{template "org/team/sidebar" .}}
                <div class="grid-2-3 left">
                        <div class="setting-content">
                                        <div class="panel-header">
                                                {{.i18n.Tr "org.teams.members"}}
                                        </div>
+                    <ul class="panel-body setting-list" id="team-members-list">
+                       {{if .IsOrganizationOwner}}
+                                               <li class="search">
+                                       <form class="form form-align" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/add" id="repo-collab-form">
+                                           {{.CsrfTokenHtml}}
+                                           <input type="hidden" name="uid" value="{{.SignedUser.Id}}">
+                                <input class="ipt ipt-large ipt-radius" id="org-team-members-add" name="uname" autocomplete="off" required />
+                                <button class="btn btn-blue btn-large btn-radius">{{.i18n.Tr "org.teams.add_team_member"}}</button>
+                                                               <div class="repo-user-list-block">
+                                                                       <ul class="menu-down-show menu-vertical menu-radius switching-list user-list" id="org-team-members-list"></ul>
+                                                               </div>
+                                       </form>
+                                               </li>
+                                               {{end}}
+                               {{range .Team.Members}}
+                               <li class="collab">
+                                       {{if $.IsOrganizationOwner}}
+                                                       <a class="btn btn-small btn-red btn-radius right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/remove?uid={{.Id}}">{{$.i18n.Tr "org.members.remove"}}</a>
+                                                       {{end}}
+                                                       <a class="member" href="/{{.Name}}">
+                                                           <img alt="{{.Name}}" class="pull-left avatar" src="{{.AvatarLink}}">
+                                                           <strong>{{.FullName}}</strong> ({{.Name}})
+                                                       </a>
+                               </li>
+                               {{end}}
+                    </ul>
                                </div>
                        </div>
                </div>
index f02512e0525367612b415d35cb6c25ffe9ed071e..ce8c15236bd7f9395c94fd7876cd30c1c2a26a12 100644 (file)
@@ -2,16 +2,21 @@
 {{template "ng/base/header" .}}
 {{template "org/base/header" .}}
 <div id="repo-wrapper">
-    <form id="team-create-form" class="form form-align panel panel-radius" action="{{.OrgLink}}/teams/new" method="post">
+    <form id="team-create-form" class="form form-align panel panel-radius" action="{{if .PageIsOrgTeamsNew}}{{.OrgLink}}/teams/new{{else}}{{.OrgLink}}/teams/{{.Team.LowerName}}/edit{{end}}" data-delete-url="{{.OrgLink}}/teams/{{.Team.LowerName}}/delete" method="post">
         {{.CsrfTokenHtml}}
         <div class="panel-header">
-            <h2>{{.i18n.Tr "org.create_new_team"}}</h2>
+            <h2>
+                {{if .PageIsOrgTeamsNew}}{{.i18n.Tr "org.create_new_team"}}{{else}}{{.i18n.Tr "org.teams.settings"}}{{end}}
+            </h2>
         </div>
         <div class="panel-content">
             {{template "ng/base/alert" .}}
             <div class="field">
                 <label class="req" for="team-name">{{.i18n.Tr "org.team_name"}}</label>
-                <input class="ipt ipt-large ipt-radius {{if .Err_TeamName}}ipt-error{{end}}" id="team-name" name="team_name" value="{{.team_name}}" required />
+                {{if eq .Team.LowerName "owners"}}
+                <input type="hidden" name="team_name" value="{{.team_name}}">
+                {{end}}
+                <input class="ipt ipt-large ipt-radius {{if .Err_TeamName}}ipt-error{{end}}" id="team-name" name="team_name" value="{{.team_name}}" required {{if eq .Team.LowerName "owners"}}disabled{{end}} />
                 <span class="form-label"></span>
                 <span class="help">{{.i18n.Tr "org.team_name_helper"}}</span>
             </div>
@@ -21,6 +26,7 @@
                 <span class="form-label"></span>
                 <span class="help">{{.i18n.Tr "org.team_desc_helper"}}</span>
             </div>
+            {{if not (eq .Team.LowerName "owners")}}
             <div class="field">
                 <h4 class="text-center">{{.i18n.Tr "org.team_permission_desc"}}</h4>
                 <label></label>
                 <p class="text-grey note">{{.i18n.Tr "org.teams.admin_access_helper"}}</p>
             </div>
             <hr>
+            {{end}}
             <div class="field">
                 <label></label>
-                <button class="btn btn-large btn-blue btn-radius">{{.i18n.Tr "org.create_new_team"}}</button>
-                <a class="btn btn-small btn-gray btn-radius" id="repo-create-cancel" href="{{.OrgLink}}/teams"><strong>{{.i18n.Tr "cancel"}}</strong></a>
+                {{if .PageIsOrgTeamsNew}}
+                    <button class="btn btn-large btn-blue btn-radius">{{.i18n.Tr "org.create_new_team"}}</button>
+                    <a class="btn btn-small btn-gray btn-radius" id="repo-create-cancel" href="{{.OrgLink}}/teams"><strong>{{.i18n.Tr "cancel"}}</strong></a>
+                {{else}}
+                    <button class="btn btn-large btn-green btn-radius">{{.i18n.Tr "org.teams.update_settings"}}</button>
+                    {{if not (eq .Team.LowerName "owners")}}
+                    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+                    <button class="btn btn-large btn-red btn-radius" id="org-team-delete">{{.i18n.Tr "org.teams.delete_team"}}</button>
+                    {{end}}
+                {{end}}
             </div>
         </div>
     </form>
index 1ab24e9aacdade983206b7409daa24e68e27e913..b760e002fc8c6b32d66837e039be220900c5a294 100644 (file)
@@ -1,9 +1,9 @@
 <div class="grid-1-3 panel panel-radius left" id="org-team-card">
     <div class="panel-header">
                {{if .Team.IsMember $.SignedUser.Id}}
-        <a class="btn btn-small btn-red btn-header btn-radius right" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/action/leave?page=team">{{$.i18n.Tr "org.teams.leave"}}</a>
-        {{else}}
-        <a class="btn btn-small btn-blue btn-header btn-radius right" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/action/join?page=team">{{$.i18n.Tr "org.teams.join"}}</a>
+        <a class="btn btn-small btn-red btn-header btn-radius right" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/action/leave?uid={{$.SignedUser.Id}}&page=team">{{$.i18n.Tr "org.teams.leave"}}</a>
+        {{else if .IsOrganizationOwner}}
+        <a class="btn btn-small btn-blue btn-header btn-radius right" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/action/join?uid={{$.SignedUser.Id}}&page=team">{{$.i18n.Tr "org.teams.join"}}</a>
         {{end}}
        <strong>{{.Team.Name}}</strong>
     </div>
        <p class="desc">{{if .Team.Description}}{{.Team.Description}}{{else}}{{.i18n.Tr "org.teams.no_desc"}}{{end}}</p>
        <hr>
        <div class="team-stats">
-               <a class="text-black"><strong>{{.Team.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
-               <a class="text-black"><strong>{{.Team.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
+               <a class="text-black" href="{{.OrgLink}}/teams/{{.Team.LowerName}}"><strong>{{.Team.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
+               <a class="text-black" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/repositories"><strong>{{.Team.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
        </div>
        <p class="desc">
                {{if eq .Team.LowerName "owners"}}
                {{.i18n.Tr "org.teams.owners_permission_desc" | Str2html}}
+            {{else if (eq .Team.Authorize 1)}}
+            {{.i18n.Tr "org.teams.read_permission_desc" | Str2html}}
+            {{else if (eq .Team.Authorize 2)}}
+            {{.i18n.Tr "org.teams.write_permission_desc" | Str2html}}
+            {{else if (eq .Team.Authorize 3)}}
+            {{.i18n.Tr "org.teams.admin_permission_desc" | Str2html}}
                {{end}}
        </p>
     </div>
+    {{if .IsOrganizationOwner}}
     <div class="panel-footer">
        <a class="btn btn-medium btn-green btn-link btn-radius" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/edit"><span class="octicon octicon-gear"></span> {{$.i18n.Tr "org.teams.settings"}}</a>
     </div>
+    {{end}}
 </div>
\ No newline at end of file
index 3e0846d652a94ec6e69bf7337e95e4c236b85a7c..9c47cb5a4151bfa0832ff342da8035b7ed0bed50 100644 (file)
@@ -6,7 +6,7 @@
                {{template "ng/base/alert" .}}
        </div>
        <div class="org-toolbar clear">
-               {{if .IsAdminTeam}}
+               {{if .IsOrganizationOwner}}
         <a class="btn btn-green btn-large btn-link btn-radius right" href="{{.OrgLink}}/teams/new"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "org.create_new_team"}}</a>
                {{end}}
        </div>
@@ -16,9 +16,9 @@
                        <div class="panel panel-radius">
                                <div class="panel-header">
                                        {{if .IsMember $.SignedUser.Id}}
-                    <a class="btn btn-small btn-red btn-header btn-radius right" href="{{$.OrgLink}}/teams/{{.LowerName}}/action/leave">{{$.i18n.Tr "org.teams.leave"}}</a>
-                    {{else}}
-                    <a class="btn btn-small btn-blue btn-header btn-radius right" href="{{$.OrgLink}}/teams/{{.LowerName}}/action/join">{{$.i18n.Tr "org.teams.join"}}</a>
+                    <a class="btn btn-small btn-red btn-header btn-radius right" href="{{$.OrgLink}}/teams/{{.LowerName}}/action/leave?uid={{$.SignedUser.Id}}">{{$.i18n.Tr "org.teams.leave"}}</a>
+                    {{else if $.IsOrganizationOwner}}
+                    <a class="btn btn-small btn-blue btn-header btn-radius right" href="{{$.OrgLink}}/teams/{{.LowerName}}/action/join?uid={{$.SignedUser.Id}}">{{$.i18n.Tr "org.teams.join"}}</a>
                     {{end}}
                     <a class="text-black" href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.Name}}</strong></a>
                                </div>