@@ -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> | |||