@@ -236,6 +236,7 @@ func runWeb(*cli.Context) { | |||
r.Get("/teams", org.Teams) | |||
r.Get("/teams/:team", org.SingleTeam) | |||
r.Get("/teams/:team/action/:action", org.TeamsAction) | |||
}, middleware.OrgAssignment(true, true)) | |||
m.Group("/:org", func(r *macaron.Router) { | |||
@@ -248,11 +249,9 @@ func runWeb(*cli.Context) { | |||
r.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) | |||
r.Route("/delete", "GET,POST", org.SettingsDelete) | |||
}) | |||
}, middleware.OrgAssignment(true, true, true)) | |||
m.Group("/:org", func(r *macaron.Router) { | |||
r.Route("/invitations/new", "GET,POST", org.Invitation) | |||
}, middleware.OrgAssignment(true, false, false, true)) | |||
}, middleware.OrgAssignment(true, true, true)) | |||
}, reqSignIn) | |||
// Repository routers. | |||
@@ -79,6 +79,7 @@ Retype = Re-type password | |||
SSHTitle = SSH key name | |||
HttpsUrl = HTTPS URL | |||
PayloadUrl = Payload URL | |||
TeamName = Team name | |||
require_error = ` cannot be empty.` | |||
alpha_dash_error = ` must be valid alpha or numeric or dash(-_) characters.` | |||
@@ -94,16 +95,19 @@ password_not_match = Password and re-type password are not same. | |||
username_been_taken = Username has been already taken. | |||
repo_name_been_taken = Repository name has been already taken. | |||
org_name_been_taken = Organization name has been already taken. | |||
team_name_been_taken = Team name has been already taken. | |||
email_been_used = E-mail address has been already used. | |||
ssh_key_been_used = Public key name has been used. | |||
illegal_username = Your username contains illegal characters. | |||
illegal_repo_name = Repository name contains illegal characters. | |||
illegal_org_name = Organization name contains illegal characters. | |||
illegal_team_name = Team name contains illegal characters. | |||
username_password_incorrect = Username or password is not correct. | |||
enterred_invalid_repo_name = Please make sure you entered repository name is correct. | |||
enterred_invalid_owner_name = Please make sure you entered owner name is correct. | |||
enterred_invalid_password = Please make sure you entered password is correct. | |||
user_not_exist = Given user does not exist. | |||
last_org_owner = The user to remove is the last member in owner team. There must be another owner. | |||
invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s | |||
auth_failed = Authentication failed: %v | |||
@@ -237,6 +241,11 @@ lower_members = members | |||
lower_repositories = repositories | |||
create_new_team = Create New Team | |||
org_desc = Description | |||
team_name = Team Name | |||
team_desc = Description | |||
team_name_helper = You'll use this name to mention this team in conversations. | |||
team_desc_helper = What is this team all about? | |||
team_permission_desc = What permission level should this team have? | |||
settings = Settings | |||
settings.options = Options | |||
@@ -258,9 +267,19 @@ members.owner = Owner | |||
members.member = Member | |||
members.conceal = Conceal | |||
members.remove = Remove | |||
members.leave = Leave | |||
members.invite_desc = Start typing a username to invite a new member to %s: | |||
members.invite_now = Invite Now | |||
teams.join = Join | |||
teams.leave = Leave | |||
teams.read_access = Read Access | |||
teams.read_access_helper = This team will be able to view and clone its repositories. | |||
teams.write_access = Write Access | |||
teams.write_access_helper = This team will be able to read its repositories, as well as push to them. | |||
teams.admin_access = Admin Access | |||
teams.admin_access_helper = This team will be able to push/pull to its repositories, as well as add other collaborators to them. | |||
[action] | |||
create_repo = created repository <a href="/%s">%s</a> | |||
commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a> | |||
@@ -79,6 +79,7 @@ Retype = 确认密码 | |||
SSHTitle = SSH 密钥名称 | |||
HttpsUrl = HTTPS URL 地址 | |||
PayloadUrl = 推送地址 | |||
TeamName = 团队名称 | |||
require_error = 不能为空。 | |||
alpha_dash_error = 必须为英文字母、阿拉伯数字或横线(-_)。 | |||
@@ -94,16 +95,19 @@ password_not_match = 密码与确认密码未匹配。 | |||
username_been_taken = 用户名已经被占用。 | |||
repo_name_been_taken = 仓库名称已经被占用。 | |||
org_name_been_taken = 组织名称已经被占用。 | |||
team_name_been_taken = 团队名称已经被占用。 | |||
email_been_used = 邮箱地址已经被使用。 | |||
ssh_key_been_used = SSH 密钥已经被使用。 | |||
illegal_username = 您的用户名包含非法字符。 | |||
illegal_repo_name = 仓库名称包含非法字符。 | |||
illegal_org_name = 组织名称包含非法字符。 | |||
illegal_team_name = 团队名称包含非法字符。 | |||
username_password_incorrect = 用户名或密码不正确。 | |||
enterred_invalid_repo_name = 请检查您输入的仓库名称是正确。 | |||
enterred_invalid_owner_name = 请检查您输入的新所有者用户名是否正确。 | |||
enterred_invalid_password = 请检查您输入的密码是否正确。 | |||
user_not_exist = 被操作的用户不存在! | |||
last_org_owner = 被移除用户为最后一位管理员。请添加一位新的管理员再进行移除成员操作! | |||
invalid_ssh_key = 很抱歉,我们无法验证您输入的 SSH 密钥:%s | |||
auth_failed = 授权验证失败:%v | |||
@@ -237,6 +241,11 @@ lower_members = 名成员 | |||
lower_repositories = 个仓库 | |||
create_new_team = 创建新的团队 | |||
org_desc = 组织描述 | |||
team_name = 团队名称 | |||
team_desc = 团队描述 | |||
team_name_helper = 您可以使用该名称来通知改组全体成员。 | |||
team_desc_helper = 一句话描述这个团队是做什么的。 | |||
team_permission_desc = 请选择该团队所具有的权限等级: | |||
settings = 组织设置 | |||
settings.options = 基本设置 | |||
@@ -258,9 +267,19 @@ members.owner = 管理员 | |||
members.member = 普通成员 | |||
members.conceal = 隐藏身份 | |||
members.remove = 移除成员 | |||
members.leave = 离开组织 | |||
members.invite_desc = 请输入被邀请到组织 %s 的用户名称: | |||
members.invite_now = 立即邀请 | |||
teams.join = 加入团队 | |||
teams.leave = 离开团队 | |||
teams.read_access = 读取权限 | |||
teams.read_access_helper = 这个团队将拥有查看和克隆所属仓库的权限。 | |||
teams.write_access = 写入权限 | |||
teams.write_access_helper = 这个团队将拥有查看、克隆和推送所属仓库的权限。 | |||
teams.admin_access = 管理权限 | |||
teams.admin_access_helper = 这个团队将拥有查看、克隆、推送和添加其他组织成员到团队的权限。 | |||
[action] | |||
create_repo = 创建了仓库 <a href="/%s">%s</a> | |||
commit_repo = 推送了 <a href="/%s/src/%s">%s</a> 分支的代码到 <a href="/%s">%s</a> | |||
@@ -17,7 +17,7 @@ import ( | |||
"github.com/gogits/gogs/modules/setting" | |||
) | |||
const APP_VER = "0.4.7.0815 Alpha" | |||
const APP_VER = "0.4.7.0816 Alpha" | |||
func init() { | |||
runtime.GOMAXPROCS(runtime.NumCPU()) | |||
@@ -15,6 +15,9 @@ import ( | |||
var ( | |||
ErrOrgNotExist = errors.New("Organization does not exist") | |||
ErrTeamAlreadyExist = errors.New("Team already exist") | |||
ErrTeamNotExist = errors.New("Team does not exist") | |||
ErrTeamNameIllegal = errors.New("Team name contains illegal characters") | |||
ErrLastOrgOwner = errors.New("The user to remove is the last member in owner team") | |||
) | |||
// IsOrgOwner returns true if given user is in the owner team. | |||
@@ -27,14 +30,14 @@ func (org *User) IsOrgMember(uid int64) bool { | |||
return IsOrganizationMember(org.Id, uid) | |||
} | |||
// GetTeam returns named team of organization. | |||
func (org *User) GetTeam(name string) (*Team, error) { | |||
return GetTeam(org.Id, name) | |||
} | |||
// GetOwnerTeam returns owner team of organization. | |||
func (org *User) GetOwnerTeam() (*Team, error) { | |||
t := &Team{ | |||
OrgId: org.Id, | |||
Name: OWNER_TEAM, | |||
} | |||
_, err := x.Get(t) | |||
return t, err | |||
return org.GetTeam(OWNER_TEAM) | |||
} | |||
// GetTeams returns all teams that belong to organization. | |||
@@ -179,96 +182,6 @@ func DeleteOrganization(org *User) (err error) { | |||
return sess.Commit() | |||
} | |||
// ___________ | |||
// \__ ___/___ _____ _____ | |||
// | |_/ __ \\__ \ / \ | |||
// | |\ ___/ / __ \| Y Y \ | |||
// |____| \___ >____ /__|_| / | |||
// \/ \/ \/ | |||
type AuthorizeType int | |||
const ( | |||
ORG_READABLE AuthorizeType = iota + 1 | |||
ORG_WRITABLE | |||
ORG_ADMIN | |||
) | |||
const OWNER_TEAM = "Owners" | |||
// Team represents a organization team. | |||
type Team struct { | |||
Id int64 | |||
OrgId int64 `xorm:"INDEX"` | |||
LowerName string | |||
Name string | |||
Description string | |||
Authorize AuthorizeType | |||
RepoIds string `xorm:"TEXT"` | |||
NumMembers int | |||
NumRepos int | |||
Members []*User `xorm:"-"` | |||
} | |||
// 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) | |||
} | |||
// GetMembers returns all members in given team of organization. | |||
func (t *Team) GetMembers() (err error) { | |||
t.Members, err = GetTeamMembers(t.OrgId, t.Id) | |||
return err | |||
} | |||
// NewTeam creates a record of new team. | |||
// It's caller's responsibility to assign organization ID. | |||
func NewTeam(t *Team) error { | |||
has, err := x.Id(t.OrgId).Get(new(User)) | |||
if err != nil { | |||
return err | |||
} else if !has { | |||
return ErrOrgNotExist | |||
} | |||
t.LowerName = strings.ToLower(t.Name) | |||
has, err = x.Where("org_id=?", t.OrgId).And("lower_name=?", t.LowerName).Get(new(Team)) | |||
if err != nil { | |||
return err | |||
} else if has { | |||
return ErrTeamAlreadyExist | |||
} | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
if _, err = sess.Insert(t); 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() | |||
} | |||
// UpdateTeam updates information of team. | |||
func UpdateTeam(t *Team) error { | |||
if len(t.Description) > 255 { | |||
t.Description = t.Description[:255] | |||
} | |||
t.LowerName = strings.ToLower(t.Name) | |||
_, err := x.Id(t.Id).AllCols().Update(t) | |||
return err | |||
} | |||
// ________ ____ ___ | |||
// \_____ \_______ ____ | | \______ ___________ | |||
// / | \_ __ \/ ___\| | / ___// __ \_ __ \ | |||
@@ -372,6 +285,21 @@ func RemoveOrgUser(orgId, uid int64) error { | |||
return nil | |||
} | |||
// 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 | |||
} | |||
if t.NumMembers == 1 { | |||
return ErrLastOrgOwner | |||
} | |||
} | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
@@ -389,6 +317,127 @@ func RemoveOrgUser(orgId, uid int64) error { | |||
return sess.Commit() | |||
} | |||
// ___________ | |||
// \__ ___/___ _____ _____ | |||
// | |_/ __ \\__ \ / \ | |||
// | |\ ___/ / __ \| Y Y \ | |||
// |____| \___ >____ /__|_| / | |||
// \/ \/ \/ | |||
type AuthorizeType int | |||
const ( | |||
ORG_READABLE AuthorizeType = iota + 1 | |||
ORG_WRITABLE | |||
ORG_ADMIN | |||
) | |||
const OWNER_TEAM = "Owners" | |||
// Team represents a organization team. | |||
type Team struct { | |||
Id int64 | |||
OrgId int64 `xorm:"INDEX"` | |||
LowerName string | |||
Name string | |||
Description string | |||
Authorize AuthorizeType | |||
RepoIds string `xorm:"TEXT"` | |||
NumMembers int | |||
NumRepos int | |||
Members []*User `xorm:"-"` | |||
} | |||
// 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) | |||
} | |||
// GetMembers returns all members in given team of organization. | |||
func (t *Team) GetMembers() (err error) { | |||
t.Members, err = GetTeamMembers(t.OrgId, t.Id) | |||
return err | |||
} | |||
// NewTeam creates a record of new team. | |||
// It's caller's responsibility to assign organization ID. | |||
func NewTeam(t *Team) error { | |||
if !IsLegalName(t.Name) { | |||
return ErrTeamNameIllegal | |||
} | |||
has, err := x.Id(t.OrgId).Get(new(User)) | |||
if err != nil { | |||
return err | |||
} else if !has { | |||
return ErrOrgNotExist | |||
} | |||
t.LowerName = strings.ToLower(t.Name) | |||
has, err = x.Where("org_id=?", t.OrgId).And("lower_name=?", t.LowerName).Get(new(Team)) | |||
if err != nil { | |||
return err | |||
} else if has { | |||
return ErrTeamAlreadyExist | |||
} | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
if _, err = sess.Insert(t); 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() | |||
} | |||
// GetTeam returns team by given team name and organization. | |||
func GetTeam(orgId int64, name string) (*Team, error) { | |||
t := &Team{ | |||
OrgId: orgId, | |||
LowerName: strings.ToLower(name), | |||
} | |||
has, err := x.Get(t) | |||
if err != nil { | |||
return nil, err | |||
} else if !has { | |||
return nil, ErrTeamNotExist | |||
} | |||
return t, nil | |||
} | |||
// GetTeamById returns team by given ID. | |||
func GetTeamById(teamId int64) (*Team, error) { | |||
t := new(Team) | |||
has, err := x.Id(teamId).Get(t) | |||
if err != nil { | |||
return nil, err | |||
} else if !has { | |||
return nil, ErrTeamNotExist | |||
} | |||
return t, nil | |||
} | |||
// UpdateTeam updates information of team. | |||
func UpdateTeam(t *Team) error { | |||
if len(t.Description) > 255 { | |||
t.Description = t.Description[:255] | |||
} | |||
t.LowerName = strings.ToLower(t.Name) | |||
_, err := x.Id(t.Id).AllCols().Update(t) | |||
return err | |||
} | |||
// ___________ ____ ___ | |||
// \__ ___/___ _____ _____ | | \______ ___________ | |||
// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \ | |||
@@ -427,3 +476,68 @@ func GetTeamMembers(orgId, teamId int64) ([]*User, error) { | |||
} | |||
return us, 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) { | |||
return nil | |||
} | |||
// We can use raw SQL here but we also want to vertify there is a such team. | |||
t, err := GetTeamById(teamId) | |||
if err != nil { | |||
return err | |||
} | |||
t.NumMembers++ | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
tu := &TeamUser{ | |||
Uid: uid, | |||
OrgId: orgId, | |||
TeamId: teamId, | |||
} | |||
if _, err = sess.Insert(tu); err != nil { | |||
sess.Rollback() | |||
return err | |||
} else if _, err = sess.Id(t.Id).Update(t); err != nil { | |||
sess.Rollback() | |||
return err | |||
} | |||
return sess.Commit() | |||
} | |||
// RemoveMember removes member from given team of given organization. | |||
func RemoveMember(orgId, teamId, uid int64) error { | |||
if !IsTeamMember(orgId, teamId, uid) { | |||
return nil | |||
} | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
tu := &TeamUser{ | |||
Uid: uid, | |||
OrgId: orgId, | |||
TeamId: teamId, | |||
} | |||
if _, err := sess.Delete(tu); err != nil { | |||
sess.Rollback() | |||
return err | |||
} else if _, err = sess.Exec("UPDATE `team` SET num_members = num_members - 1 WHERE id = ?", teamId); err != nil { | |||
sess.Rollback() | |||
return err | |||
} | |||
return sess.Commit() | |||
} |
@@ -49,7 +49,7 @@ func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs *binding.Erro | |||
// \/ \/ \/ | |||
type CreateTeamForm struct { | |||
TeamName string `form:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` | |||
TeamName string `form:"team_name" binding:"Required;AlphaDashDot;MaxSize(30)"` | |||
Description string `form:"desc" binding:"MaxSize(255)"` | |||
Permission string `form:"permission"` | |||
} | |||
@@ -71,6 +71,8 @@ type Context struct { | |||
IsAdminTeam bool // In owner team or team that has admin permission level. | |||
Organization *models.User | |||
OrgLink string | |||
Team *models.Team | |||
} | |||
} | |||
@@ -41,15 +41,16 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | |||
} | |||
return | |||
} | |||
ctx.Data["Org"] = ctx.Org.Organization | |||
org := ctx.Org.Organization | |||
ctx.Data["Org"] = org | |||
if ctx.IsSigned { | |||
ctx.Org.IsOwner = ctx.Org.Organization.IsOrgOwner(ctx.User.Id) | |||
ctx.Org.IsOwner = org.IsOrgOwner(ctx.User.Id) | |||
if ctx.Org.IsOwner { | |||
ctx.Org.IsMember = true | |||
ctx.Org.IsAdminTeam = true | |||
} else { | |||
if ctx.Org.Organization.IsOrgMember(ctx.User.Id) { | |||
if org.IsOrgMember(ctx.User.Id) { | |||
ctx.Org.IsMember = true | |||
// TODO: ctx.Org.IsAdminTeam | |||
} | |||
@@ -64,7 +65,24 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | |||
ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam | |||
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner | |||
ctx.Org.OrgLink = "/org/" + ctx.Org.Organization.Name | |||
ctx.Org.OrgLink = "/org/" + org.Name | |||
ctx.Data["OrgLink"] = ctx.Org.OrgLink | |||
// Team. | |||
teamName := ctx.Params(":team") | |||
if len(teamName) > 0 { | |||
ctx.Org.Team, err = org.GetTeam(teamName) | |||
if err != nil { | |||
if err == models.ErrTeamNotExist { | |||
ctx.Handle(404, "GetTeam", err) | |||
} else if redirect { | |||
ctx.Redirect("/") | |||
} else { | |||
ctx.Handle(500, "GetTeam", err) | |||
} | |||
return | |||
} | |||
ctx.Data["Team"] = ctx.Org.Team | |||
} | |||
} | |||
} |
@@ -1172,28 +1172,34 @@ The register and sign-in page style | |||
width: 520px; | |||
} | |||
/* repository create */ | |||
#team-create-form, | |||
#repo-migrate-form, | |||
#repo-create-form { | |||
width: 800px; | |||
margin: 60px auto auto auto; | |||
background: white; | |||
} | |||
#team-create-form h2, | |||
#repo-migrate-form h2, | |||
#repo-create-form h2 { | |||
margin: .5em 1em; | |||
} | |||
#team-create-form .field, | |||
#repo-migrate-form .field, | |||
#repo-create-form .field { | |||
margin: 1.2em 0 2em 0; | |||
} | |||
#team-create-form .ipt, | |||
#repo-migrate-form .ipt, | |||
#repo-create-form .ipt { | |||
width: 540px; | |||
} | |||
#team-create-form textarea, | |||
#repo-migrate-form textarea, | |||
#repo-create-form textarea { | |||
height: 120px; | |||
} | |||
#team-create-form .avatar, | |||
#repo-migrate-form .avatar, | |||
#repo-create-form .avatar { | |||
vertical-align: middle; | |||
@@ -1201,6 +1207,7 @@ The register and sign-in page style | |||
width: 28px; | |||
height: 28px; | |||
} | |||
#team-create-form:hover, | |||
#repo-migrate-form:hover, | |||
#repo-create-form:hover { | |||
box-shadow: 0px 0px 6px #CCC; | |||
@@ -1681,6 +1688,9 @@ textarea#issue-add-content { | |||
box-sizing: border-box; | |||
height: 120px; | |||
} | |||
.org-header-alert .alert { | |||
margin-top: 10px; | |||
} | |||
.org-header { | |||
padding: 16px 0; | |||
background-color: #FFF; | |||
@@ -1767,10 +1777,10 @@ textarea#issue-add-content { | |||
.org-sidebar .panel-footer { | |||
padding: .8em 1.2em; | |||
} | |||
#org-member-avatar-group { | |||
.org-sidebar .member-avatar-group { | |||
padding: 15px; | |||
} | |||
#org-member-avatar-group img { | |||
.org-sidebar .member-avatar-group img { | |||
width: 59px; | |||
height: 59px; | |||
border-radius: 3px; | |||
@@ -1801,13 +1811,14 @@ textarea#issue-add-content { | |||
margin-bottom: 0; | |||
color: #777; | |||
} | |||
#org-member-toolbar { | |||
.org-toolbar { | |||
padding: 10px 0; | |||
border-bottom: 1px solid #eee; | |||
} | |||
#org-member-list .org-member-item { | |||
height: 50px; | |||
line-height: 50px; | |||
border-top: 1px solid #eee; | |||
border-bottom: 1px solid #eee; | |||
padding: 15px 20px; | |||
} | |||
#org-member-list .org-member-item .member-name { | |||
@@ -1832,3 +1843,19 @@ textarea#issue-add-content { | |||
#org-member-list-block { | |||
padding-top: 2px; | |||
} | |||
.org-team-list .org-team-list-item { | |||
float: left; | |||
padding: 15px; | |||
width: 555px; | |||
} | |||
.org-team-list .org-team-list-item .member-avatar-group { | |||
padding: 5px 15px; | |||
} | |||
.org-team-list .org-team-list-item .member-avatar-group img { | |||
width: 38px; | |||
height: 38px; | |||
border-radius: 3px; | |||
} | |||
#team-create-form .note { | |||
margin-left: 153px; | |||
} |
@@ -1,3 +1,6 @@ | |||
.org-header-alert .alert { | |||
margin-top: 10px; | |||
} | |||
.org-header { | |||
padding: 16px 0; | |||
background-color: #FFF; | |||
@@ -91,13 +94,13 @@ | |||
.panel-footer { | |||
padding: .8em 1.2em; | |||
} | |||
} | |||
#org-member-avatar-group { | |||
padding: 15px; | |||
img { | |||
width: 59px; | |||
height: 59px; | |||
border-radius: 3px; | |||
.member-avatar-group { | |||
padding: 15px; | |||
img { | |||
width: 59px; | |||
height: 59px; | |||
border-radius: 3px; | |||
} | |||
} | |||
} | |||
#org-home-team-list { | |||
@@ -126,14 +129,15 @@ | |||
margin-bottom: 0; | |||
color: #777; | |||
} | |||
#org-member-toolbar { | |||
.org-toolbar { | |||
padding: 10px 0; | |||
border-bottom: 1px solid #eee; | |||
} | |||
#org-member-list { | |||
.org-member-item { | |||
height: 50px; | |||
line-height: 50px; | |||
border-top: 1px solid #eee; | |||
border-bottom: 1px solid #eee; | |||
padding: 15px 20px; | |||
.member-name { | |||
padding-left: 15px; | |||
@@ -158,4 +162,24 @@ | |||
} | |||
#org-member-list-block { | |||
padding-top: 2px; | |||
} | |||
.org-team-list { | |||
.org-team-list-item { | |||
float: left; | |||
padding: 15px; | |||
width: 555px; | |||
.member-avatar-group { | |||
padding: 5px 15px; | |||
img { | |||
width: 38px; | |||
height: 38px; | |||
border-radius: 3px; | |||
} | |||
} | |||
} | |||
} | |||
#team-create-form { | |||
.note { | |||
margin-left: 153px; | |||
} | |||
} |
@@ -310,6 +310,7 @@ border-top-right-radius: .25em; | |||
} | |||
/* repository create */ | |||
#team-create-form, | |||
#repo-migrate-form, | |||
#repo-create-form { | |||
width: 800px; | |||
@@ -14,13 +14,13 @@ import ( | |||
) | |||
const ( | |||
MEMBERS base.TplName = "org/members" | |||
INVITE base.TplName = "org/invite" | |||
MEMBERS base.TplName = "org/member/members" | |||
MEMBER_INVITE base.TplName = "org/member/invite" | |||
) | |||
func Members(ctx *middleware.Context) { | |||
org := ctx.Org.Organization | |||
ctx.Data["Title"] = org.Name | |||
ctx.Data["Title"] = org.FullName | |||
ctx.Data["PageIsOrgMembers"] = true | |||
if err := org.GetMembers(); err != nil { | |||
@@ -60,6 +60,18 @@ func MembersAction(ctx *middleware.Context) { | |||
return | |||
} | |||
err = org.RemoveMember(uid) | |||
if err == models.ErrLastOrgOwner { | |||
ctx.Flash.Error(ctx.Tr("form.last_org_owner")) | |||
ctx.Redirect(ctx.Org.OrgLink + "/members") | |||
return | |||
} | |||
case "leave": | |||
err = org.RemoveMember(ctx.User.Id) | |||
if err == models.ErrLastOrgOwner { | |||
ctx.Flash.Error(ctx.Tr("form.last_org_owner")) | |||
ctx.Redirect(ctx.Org.OrgLink + "/members") | |||
return | |||
} | |||
} | |||
if err != nil { | |||
@@ -75,7 +87,7 @@ func MembersAction(ctx *middleware.Context) { | |||
func Invitation(ctx *middleware.Context) { | |||
org := ctx.Org.Organization | |||
ctx.Data["Title"] = org.Name | |||
ctx.Data["Title"] = org.FullName | |||
ctx.Data["PageIsOrgMembers"] = true | |||
if ctx.Req.Method == "POST" { | |||
@@ -101,5 +113,5 @@ func Invitation(ctx *middleware.Context) { | |||
return | |||
} | |||
ctx.HTML(200, INVITE) | |||
ctx.HTML(200, MEMBER_INVITE) | |||
} |
@@ -19,7 +19,7 @@ const ( | |||
func Home(ctx *middleware.Context) { | |||
org := ctx.Org.Organization | |||
ctx.Data["Title"] = org.Name | |||
ctx.Data["Title"] = org.FullName | |||
repos, err := models.GetRepositories(org.Id, ctx.IsSigned && org.IsOrgMember(ctx.User.Id)) | |||
if err != nil { | |||
@@ -13,31 +13,22 @@ import ( | |||
) | |||
const ( | |||
TEAMS base.TplName = "org/teams" | |||
TEAM_NEW base.TplName = "org/team_new" | |||
TEAMS base.TplName = "org/team/teams" | |||
TEAM_NEW base.TplName = "org/team/new" | |||
) | |||
func Teams(ctx *middleware.Context) { | |||
ctx.Data["Title"] = "Organization " + ctx.Params(":org") + " Teams" | |||
org := ctx.Org.Organization | |||
ctx.Data["Title"] = org.FullName | |||
ctx.Data["PageIsOrgTeams"] = true | |||
org, err := models.GetUserByName(ctx.Params(":org")) | |||
if err != nil { | |||
if err == models.ErrUserNotExist { | |||
ctx.Handle(404, "org.Teams(GetUserByName)", err) | |||
} else { | |||
ctx.Handle(500, "org.Teams(GetUserByName)", err) | |||
} | |||
return | |||
} | |||
ctx.Data["Org"] = org | |||
if err = org.GetTeams(); err != nil { | |||
ctx.Handle(500, "org.Teams(GetTeams)", err) | |||
if err := org.GetTeams(); err != nil { | |||
ctx.Handle(500, "GetTeams", err) | |||
return | |||
} | |||
for _, t := range org.Teams { | |||
if err = t.GetMembers(); err != nil { | |||
ctx.Handle(500, "org.Home(GetMembers)", err) | |||
if err := t.GetMembers(); err != nil { | |||
ctx.Handle(500, "GetMembers", err) | |||
return | |||
} | |||
} | |||
@@ -46,44 +37,39 @@ func Teams(ctx *middleware.Context) { | |||
ctx.HTML(200, TEAMS) | |||
} | |||
func NewTeam(ctx *middleware.Context) { | |||
org, err := models.GetUserByName(ctx.Params(":org")) | |||
if err != nil { | |||
if err == models.ErrUserNotExist { | |||
ctx.Handle(404, "org.NewTeam(GetUserByName)", err) | |||
} else { | |||
ctx.Handle(500, "org.NewTeam(GetUserByName)", err) | |||
} | |||
return | |||
func TeamsAction(ctx *middleware.Context) { | |||
var err error | |||
switch ctx.Params(":action") { | |||
case "join": | |||
err = models.AddTeamMember(ctx.Org.Organization.Id, ctx.Org.Team.Id, ctx.User.Id) | |||
case "leave": | |||
err = models.RemoveMember(ctx.Org.Organization.Id, ctx.Org.Team.Id, ctx.User.Id) | |||
} | |||
ctx.Data["Org"] = org | |||
// Check ownership of organization. | |||
if !org.IsOrgOwner(ctx.User.Id) { | |||
ctx.Error(403) | |||
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 | |||
} | |||
ctx.Redirect(ctx.Org.OrgLink + "/teams") | |||
} | |||
func NewTeam(ctx *middleware.Context) { | |||
ctx.Data["Title"] = ctx.Org.Organization.FullName | |||
ctx.Data["PageIsOrgTeams"] = true | |||
ctx.Data["PageIsOrgTeamsNew"] = true | |||
ctx.Data["Team"] = &models.Team{} | |||
ctx.HTML(200, TEAM_NEW) | |||
} | |||
func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | |||
org, err := models.GetUserByName(ctx.Params(":org")) | |||
if err != nil { | |||
if err == models.ErrUserNotExist { | |||
ctx.Handle(404, "org.NewTeamPost(GetUserByName)", err) | |||
} else { | |||
ctx.Handle(500, "org.NewTeamPost(GetUserByName)", err) | |||
} | |||
return | |||
} | |||
ctx.Data["Org"] = org | |||
// Check ownership of organization. | |||
if !org.IsOrgOwner(ctx.User.Id) { | |||
ctx.Error(403) | |||
return | |||
} | |||
ctx.Data["Title"] = ctx.Org.Organization.FullName | |||
ctx.Data["PageIsOrgTeams"] = true | |||
ctx.Data["PageIsOrgTeamsNew"] = true | |||
ctx.Data["Team"] = &models.Team{} | |||
if ctx.HasError() { | |||
ctx.HTML(200, TEAM_NEW) | |||
@@ -104,23 +90,29 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | |||
return | |||
} | |||
org := ctx.Org.Organization | |||
t := &models.Team{ | |||
OrgId: org.Id, | |||
Name: form.TeamName, | |||
Description: form.Description, | |||
Authorize: auth, | |||
} | |||
if err = models.NewTeam(t); err != nil { | |||
if err == models.ErrTeamAlreadyExist { | |||
if err := models.NewTeam(t); err != nil { | |||
switch err { | |||
case models.ErrTeamNameIllegal: | |||
ctx.Data["Err_TeamName"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.illegal_team_name"), TEAM_NEW, &form) | |||
case models.ErrTeamAlreadyExist: | |||
ctx.Data["Err_TeamName"] = true | |||
ctx.RenderWithErr("Team name has already been used", TEAM_NEW, &form) | |||
} else { | |||
ctx.Handle(500, "org.NewTeamPost(NewTeam)", err) | |||
ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), TEAM_NEW, &form) | |||
default: | |||
ctx.Handle(500, "NewTeam", err) | |||
} | |||
return | |||
} | |||
log.Trace("%s Team created: %s/%s", ctx.Req.RequestURI, org.Name, t.Name) | |||
ctx.Redirect("/org/" + org.LowerName + "/teams/" + t.LowerName) | |||
log.Trace("Team created: %s/%s", org.Name, t.Name) | |||
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName) | |||
} | |||
func EditTeam(ctx *middleware.Context) { | |||
@@ -20,12 +20,11 @@ import ( | |||
) | |||
const ( | |||
SIGNIN base.TplName = "user/signin" | |||
SIGNUP base.TplName = "user/signup" | |||
DELETE base.TplName = "user/delete" | |||
ACTIVATE base.TplName = "user/activate" | |||
FORGOT_PASSWORD base.TplName = "user/forgot_passwd" | |||
RESET_PASSWORD base.TplName = "user/reset_passwd" | |||
SIGNIN base.TplName = "user/auth/signin" | |||
SIGNUP base.TplName = "user/auth/signup" | |||
ACTIVATE base.TplName = "user/auth/activate" | |||
FORGOT_PASSWORD base.TplName = "user/auth/forgot_passwd" | |||
RESET_PASSWORD base.TplName = "user/auth/reset_passwd" | |||
) | |||
func SignIn(ctx *middleware.Context) { | |||
@@ -1 +1 @@ | |||
0.4.7.0815 Alpha | |||
0.4.7.0816 Alpha |
@@ -4,7 +4,10 @@ | |||
<div class="container clear"> | |||
<img class="avatar-100 left" src="{{.Org.AvatarLink}}?s=140"/> | |||
<div id="org-home-header-info"> | |||
<h2>{{.Org.FullName}} <a class="text-grey" href="/org/{{.Org.LowerName}}/settings"><span class="octicon octicon-gear"></span></a></h2> | |||
<h2> | |||
{{.Org.FullName}} | |||
{{if .IsOrganizationOwner}}<a class="text-grey" href="{{.OrgLink}}/settings"><span class="octicon octicon-gear"></span></a>{{end}} | |||
</h2> | |||
{{if .Org.Description}}<p>{{.Org.Description}}</p>{{end}} | |||
<ul class="text-grey"> | |||
{{if .Org.Location}}<li><span class="octicon octicon-location"></span> <span>{{.Org.Location}}</span></li>{{end}} | |||
@@ -17,7 +20,7 @@ | |||
<div class="container"> | |||
<div id="org-home-repo-list" class="left grid-2-3"> | |||
<div class="clear"> | |||
{{if .IsAdminTeam}} | |||
{{if .IsOrganizationOwner}} | |||
<a class="btn btn-green btn-large btn-link btn-radius right" href="/repo/create?org={{.Org.Id}}"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "new_repo"}}</a> | |||
{{end}} | |||
</div> | |||
@@ -42,12 +45,12 @@ | |||
<a class="text-grey right" href="/org/{{.Org.LowerName}}/members"><strong>{{.Org.NumMembers}}</strong><span class="octicon octicon-chevron-right"></span></a> | |||
<strong>{{.i18n.Tr "org.people"}}</strong> | |||
</div> | |||
<div class="panel-body" id="org-member-avatar-group"> | |||
<div class="panel-body member-avatar-group"> | |||
{{range .Members}} | |||
<a href="/{{.Name}}"><img src="{{.AvatarLink}}"></a> | |||
<a href="/{{.Name}}" title="{{.Name}}"><img src="{{.AvatarLink}}"></a> | |||
{{end}} | |||
</div> | |||
{{if .IsAdminTeam}} | |||
{{if .IsOrganizationOwner}} | |||
<div class="panel-footer"> | |||
<a class="btn btn-medium btn-blue btn-link btn-radius" href="/org/{{.Org.LowerName}}/invitations/new">{{.i18n.Tr "org.invite_someone"}}</a> | |||
</div> | |||
@@ -1,6 +1,6 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
{{template "org/header" .}} | |||
{{template "org/base/header" .}} | |||
<div class="container"> | |||
<div class="invite-box" id="invite-box"> | |||
{{template "ng/base/alert" .}} |
@@ -1,9 +1,11 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
{{template "org/header" .}} | |||
{{template "org/base/header" .}} | |||
<div class="container"> | |||
{{template "ng/base/alert" .}} | |||
<div class="clear" id="org-member-toolbar"> | |||
<div class="org-header-alert"> | |||
{{template "ng/base/alert" .}} | |||
</div> | |||
<div class="org-toolbar clear"> | |||
{{if .IsAdminTeam}} | |||
<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}} | |||
@@ -25,15 +27,19 @@ | |||
{{end}} | |||
</li> | |||
<li class="grid-1-4">{{if .IsUserOrgOwner $.Org.Id}}<strong>{{$.i18n.Tr "org.members.owner"}}</strong>{{else}}{{$.i18n.Tr "org.members.member"}}{{end}}</li> | |||
{{if $.IsOrganizationOwner}} | |||
<li class="grid-1-6 right"> | |||
<a class="btn btn-red btn-link btn-radius" href="{{$.OrgLink}}/members/action/remove?uid={{.Id}}">{{$.i18n.Tr "org.members.remove"}}</a> | |||
</li> | |||
{{if $isPublic}} | |||
<li class="grid-1-6 right"> | |||
<a class="btn btn-blue btn-link btn-radius" href="{{$.OrgLink}}/members/action/private?uid={{.Id}}">{{$.i18n.Tr "org.members.conceal"}}</a> | |||
</li> | |||
{{end}} | |||
{{if eq $.SignedUser.Id .Id}} | |||
<li class="grid-1-6 right"> | |||
<a class="btn btn-red btn-link btn-radius" href="{{$.OrgLink}}/members/action/leave?uid={{.Id}}">{{$.i18n.Tr "org.members.leave"}}</a> | |||
</li> | |||
{{else if $.IsOrganizationOwner}} | |||
<li class="grid-1-6 right"> | |||
<a class="btn btn-red btn-link btn-radius" href="{{$.OrgLink}}/members/action/remove?uid={{.Id}}">{{$.i18n.Tr "org.members.remove"}}</a> | |||
</li> | |||
{{if $isPublic}} | |||
<li class="grid-1-6 right"> | |||
<a class="btn btn-blue btn-link btn-radius" href="{{$.OrgLink}}/members/action/private?uid={{.Id}}">{{$.i18n.Tr "org.members.conceal"}}</a> | |||
</li> | |||
{{end}} | |||
{{end}} | |||
</ul> | |||
</div> |
@@ -1,5 +1,6 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
{{template "org/header" .}} | |||
<div id="setting-wrapper" class="main-wrapper"> | |||
<div id="org-setting" class="container clear"> | |||
{{template "org/settings/nav" .}} | |||
@@ -1,5 +1,6 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
{{template "org/header" .}} | |||
<div id="setting-wrapper" class="main-wrapper"> | |||
<div id="org-setting" class="container clear"> | |||
{{template "org/settings/nav" .}} | |||
@@ -0,0 +1,48 @@ | |||
{{template "ng/base/head" .}} | |||
{{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"> | |||
{{.CsrfTokenHtml}} | |||
<div class="panel-header"> | |||
<h2>{{.i18n.Tr "org.create_new_team"}}</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 /> | |||
<span class="form-label"></span> | |||
<span class="help">{{.i18n.Tr "org.team_name_helper"}}</span> | |||
</div> | |||
<div class="field"> | |||
<label for="desc">{{.i18n.Tr "org.team_desc"}}</label> | |||
<input class="ipt ipt-large ipt-radius {{if .Err_Description}}ipt-error{{end}}" id="desc" name="desc" value="{{.desc}}" /> | |||
<span class="form-label"></span> | |||
<span class="help">{{.i18n.Tr "org.team_desc_helper"}}</span> | |||
</div> | |||
<div class="field"> | |||
<h4 class="text-center">{{.i18n.Tr "org.team_permission_desc"}}</h4> | |||
<label></label> | |||
<input name="permission" type="radio" value="read" {{if or .PageIsOrgTeamsNew (eq .Team.Authorize 1)}}checked{{end}}> {{.i18n.Tr "org.teams.read_access"}} | |||
<label></label> | |||
<p class="text-grey note">{{.i18n.Tr "org.teams.read_access_helper"}}</p> | |||
<label></label> | |||
<input name="permission" type="radio" value="write" {{if eq .Team.Authorize 2}}checked{{end}}> {{.i18n.Tr "org.teams.write_access"}} | |||
<label></label> | |||
<p class="text-grey note">{{.i18n.Tr "org.teams.write_access_helper"}}</p> | |||
<label></label> | |||
<input name="permission" type="radio" value="admin" {{if eq .Team.Authorize 3}}checked{{end}}> {{.i18n.Tr "org.teams.admin_access"}} | |||
<label></label> | |||
<p class="text-grey note">{{.i18n.Tr "org.teams.admin_access_helper"}}</p> | |||
</div> | |||
<hr> | |||
<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> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
{{template "ng/base/footer" .}} |
@@ -0,0 +1,42 @@ | |||
{{template "ng/base/head" .}} | |||
{{template "ng/base/header" .}} | |||
{{template "org/base/header" .}} | |||
<div class="container"> | |||
<div class="org-header-alert"> | |||
{{template "ng/base/alert" .}} | |||
</div> | |||
<div class="org-toolbar clear"> | |||
{{if .IsAdminTeam}} | |||
<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> | |||
<div class="org-team-list"> | |||
{{range .Teams}} | |||
<div class="org-team-list-item"> | |||
<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> | |||
{{end}} | |||
<a class="text-black" href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.Name}}</strong></a> | |||
</div> | |||
{{if .NumMembers}} | |||
<div class="panel-body member-avatar-group"> | |||
{{range .Members}} | |||
<a href="/{{.Name}}" title="{{.Name}}"> | |||
<img src="{{.AvatarLink}}"> | |||
</a> | |||
{{end}} | |||
</div> | |||
{{end}} | |||
<div class="panel-footer"> | |||
<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{template "ng/base/footer" .}} |
@@ -1,79 +0,0 @@ | |||
{{template "base/head" .}} | |||
{{template "base/navbar" .}} | |||
<div id="body-nav" class="org-nav org-nav-auto"> | |||
<div class="container clearfix"> | |||
<div id="org-nav-wrapper"> | |||
<ul class="nav nav-pills pull-right"> | |||
<li><a href="/org/{{.Org.Name}}/members"><i class="fa fa-users"></i>Members | |||
<span class="label label-default">{{.Org.NumMembers}}</span></a> | |||
</li> | |||
<li class="active"><a href="/org/{{.Org.Name}}/teams"><i class="fa fa-tags"></i>Teams | |||
<span class="label label-default">{{.Org.NumTeams}}</span></a> | |||
</li> | |||
</ul> | |||
<img class="pull-left org-small-logo" src="{{.Org.AvatarLink}}?s=140" alt="" width="60"/> | |||
<div id="org-nav-info"> | |||
<h2 class="org-name">{{.Org.FullName}}</h2> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div id="body" class="container"> | |||
<div id="org"> | |||
<form action="/org/{{.Org.Name}}/teams/new" method="post" id="org-teams-create" class="form-horizontal card"> | |||
{{.CsrfTokenHtml}} | |||
<h3>Create new team</h3> | |||
{{template "base/alert" .}} | |||
<div class="form-group{{if .Err_TeamName}} has-error has-feedback{{end}}"> | |||
<label class="col-md-2 control-label">Team Name<strong class="text-danger">*</strong></label> | |||
<div class="col-md-8"> | |||
<input name="name" type="text" class="form-control" placeholder="Type your team name" value="{{.name}}" required="required"> | |||
<span class="help-block">You'll use this name to mention this team in conversations.</span> | |||
</div> | |||
</div> | |||
<div class="form-group{{if .Err_Description}} has-error has-feedback{{end}}"> | |||
<label class="col-md-2 control-label">Description</label> | |||
<div class="col-md-8"> | |||
<input name="desc" type="text" class="form-control" placeholder="Type your team description (optional)" value="{{.desc}}"> | |||
</div> | |||
</div> | |||
<div class="form-group{{if .Err_Permission}} has-error has-feedback{{end}}"> | |||
<label class="col-md-2 control-label">Permission</label> | |||
<div class="col-md-8"> | |||
<div class="radio"> | |||
<label> | |||
<input type="radio" name="permission" value="read" checked=""> | |||
<strong>Read Access</strong> | |||
</label> | |||
<p>This team will be able to view and clone its repositories.</p> | |||
</div> | |||
<div class="radio"> | |||
<label> | |||
<input type="radio" name="permission" value="write"> | |||
<strong>Write Access</strong> | |||
</label> | |||
<p>This team will be able to read its repositories, as well as push to them.</p> | |||
</div> | |||
<div class="radio"> | |||
<label> | |||
<input type="radio" name="permission" value="admin"> | |||
<strong>Admin Access</strong> | |||
</label> | |||
<p>This team will be able to push/pull to its repositories, as well as add other collaborators to them.</p> | |||
</div> | |||
</div> | |||
</div> | |||
<hr/> | |||
<div class="form-group"> | |||
<label class="col-md-2"> </label> | |||
<div class="col-md-8"> | |||
<button class="btn btn-primary">Create team</button> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} |
@@ -1,58 +0,0 @@ | |||
{{template "base/head" .}} | |||
{{template "base/navbar" .}} | |||
<div id="body-nav" class="org-nav org-nav-auto"> | |||
<div class="container clearfix"> | |||
<div id="org-nav-wrapper"> | |||
<ul class="nav nav-pills pull-right"> | |||
<li><a href="/org/{{.Org.Name}}/members"><i class="fa fa-users"></i>Members | |||
<span class="label label-default">{{.Org.NumMembers}}</span></a> | |||
</li> | |||
<li class="active"><a href="/org/{{.Org.Name}}/teams"><i class="fa fa-tags"></i>Teams | |||
<span class="label label-default">{{.Org.NumTeams}}</span></a> | |||
</li> | |||
</ul> | |||
<img class="pull-left org-small-logo" src="{{.Org.AvatarLink}}?s=140" alt="" width="60"/> | |||
<div id="org-nav-info"> | |||
<h2 class="org-name">{{.Org.FullName}}</h2> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div id="body" class="container"> | |||
<div id="org"> | |||
<div id="org-teams"> | |||
<div id="org-teams-action"> | |||
<div class="col-md-12"> | |||
<a href="/org/{{.Org.Name}}/teams/new"><button class="btn btn-success"><i class="fa fa-plus-square"></i>New Team</button></a> | |||
<hr/> | |||
</div> | |||
</div> | |||
{{range .Teams}} | |||
<div class="org-team col-md-6"> | |||
<div class="panel panel-default"> | |||
<h2 class="panel-heading org-team-name"><a href="/org/{{$.Org.Name}}/teams/{{.LowerName}}"><strong>{{.Name}}</strong></a></h2> | |||
<div class="panel-body"> | |||
<p class="org-team-meta">{{.NumMembers}} members · {{.NumRepos}} repositories</p> | |||
<p class="org-team-members"> | |||
{{range .Members}} | |||
<a href="/user/{{.LowerName}}"> | |||
<img class="img-thumbnail" src="{{.AvatarLink}}?s=60" alt=""/> | |||
</a> | |||
{{end}} | |||
</p> | |||
</div> | |||
<div class="panel-footer"> | |||
{{if .IsMember $.SignedUser.Id}} | |||
<a class="pull-right btn btn-danger" href="/org/{{$.Org.Name}}/teams/{{.LowerName}}?action=leave">Leave</a> | |||
{{else}} | |||
<a class="pull-right btn btn-default" href="/org/{{$.Org.Name}}/teams/{{.LowerName}}?action=join">Join</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} |
@@ -3,7 +3,9 @@ | |||
<div id="repo-wrapper"> | |||
<form id="repo-create-form" class="form form-align panel panel-radius" action="/repo/create" method="post"> | |||
{{.CsrfTokenHtml}} | |||
<div class="panel-header"><h2>{{.i18n.Tr "new_repo"}}</h2></div> | |||
<div class="panel-header"> | |||
<h2>{{.i18n.Tr "new_repo"}}</h2> | |||
</div> | |||
<div class="panel-content"> | |||
{{template "ng/base/alert" .}} | |||
<div class="field"> | |||
@@ -1,17 +0,0 @@ | |||
{{template "base/head" .}} | |||
{{template "base/navbar" .}} | |||
<div id="body-nav"> | |||
<div class="container"> | |||
<ul class="nav nav-pills pull-right"> | |||
<li><a href="/">Feed</a></li> | |||
<li><a href="/issues">Issues</a></li> | |||
<li><a href="/pulls">Pull Requests</a></li> | |||
<li class="active"><a href="/stars">Stars</a></li> | |||
</ul> | |||
<h3>Stars</h3> | |||
</div> | |||
</div> | |||
<div id="body" class="container" data-page="user"> | |||
{{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} | |||
</div> | |||
{{template "base/footer" .}} |