| @@ -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) | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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()) | |||
| @@ -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() | |||
| } | |||
| @@ -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 { | |||
| @@ -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) | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| } | |||
| } | |||
| @@ -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") | |||
| @@ -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; | |||
| } | |||
| @@ -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'); | |||
| @@ -196,4 +196,19 @@ | |||
| .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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -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) { | |||
| @@ -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") | |||
| } | |||
| @@ -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") | |||
| @@ -1 +1 @@ | |||
| 0.4.7.0823 Alpha | |||
| 0.4.7.0824 Alpha | |||
| @@ -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> | |||
| @@ -42,12 +45,16 @@ | |||
| <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> | |||
| @@ -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> | |||
| @@ -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"> | |||
| @@ -10,6 +11,32 @@ | |||
| <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> | |||
| @@ -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> | |||
| @@ -37,10 +43,19 @@ | |||
| <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")}} | |||
| | |||
| <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> | |||
| @@ -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> | |||
| @@ -11,16 +11,24 @@ | |||
| <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> | |||
| @@ -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> | |||