mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Finish team list, create new team, join/leave team page
This commit is contained in:
		| @@ -236,6 +236,7 @@ func runWeb(*cli.Context) { | |||||||
|  |  | ||||||
| 			r.Get("/teams", org.Teams) | 			r.Get("/teams", org.Teams) | ||||||
| 			r.Get("/teams/:team", org.SingleTeam) | 			r.Get("/teams/:team", org.SingleTeam) | ||||||
|  | 			r.Get("/teams/:team/action/:action", org.TeamsAction) | ||||||
| 		}, middleware.OrgAssignment(true, true)) | 		}, middleware.OrgAssignment(true, true)) | ||||||
|  |  | ||||||
| 		m.Group("/:org", func(r *macaron.Router) { | 		m.Group("/:org", func(r *macaron.Router) { | ||||||
| @@ -248,11 +249,9 @@ func runWeb(*cli.Context) { | |||||||
| 				r.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) | 				r.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) | ||||||
| 				r.Route("/delete", "GET,POST", org.SettingsDelete) | 				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) | 			r.Route("/invitations/new", "GET,POST", org.Invitation) | ||||||
| 		}, middleware.OrgAssignment(true, false, false, true)) | 		}, middleware.OrgAssignment(true, true, true)) | ||||||
| 	}, reqSignIn) | 	}, reqSignIn) | ||||||
|  |  | ||||||
| 	// Repository routers. | 	// Repository routers. | ||||||
|   | |||||||
| @@ -79,6 +79,7 @@ Retype = Re-type password | |||||||
| SSHTitle = SSH key name | SSHTitle = SSH key name | ||||||
| HttpsUrl = HTTPS URL | HttpsUrl = HTTPS URL | ||||||
| PayloadUrl = Payload URL | PayloadUrl = Payload URL | ||||||
|  | TeamName = Team name | ||||||
|  |  | ||||||
| require_error = ` cannot be empty.` | require_error = ` cannot be empty.` | ||||||
| alpha_dash_error = ` must be valid alpha or numeric or dash(-_) characters.` | 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. | username_been_taken = Username has been already taken. | ||||||
| repo_name_been_taken = Repository name has been already taken. | repo_name_been_taken = Repository name has been already taken. | ||||||
| org_name_been_taken = Organization 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. | email_been_used = E-mail address has been already used. | ||||||
| ssh_key_been_used = Public key name has been used. | ssh_key_been_used = Public key name has been used. | ||||||
| illegal_username = Your username contains illegal characters. | illegal_username = Your username contains illegal characters. | ||||||
| illegal_repo_name = Repository name contains illegal characters. | illegal_repo_name = Repository name contains illegal characters. | ||||||
| illegal_org_name = Organization 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. | username_password_incorrect = Username or password is not correct. | ||||||
| enterred_invalid_repo_name = Please make sure you entered repository name is 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_owner_name = Please make sure you entered owner name is correct. | ||||||
| enterred_invalid_password = Please make sure you entered password is correct. | enterred_invalid_password = Please make sure you entered password is correct. | ||||||
| user_not_exist = Given user does not exist. | 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 | invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s | ||||||
| auth_failed = Authentication failed: %v | auth_failed = Authentication failed: %v | ||||||
| @@ -237,6 +241,11 @@ lower_members = members | |||||||
| lower_repositories = repositories | lower_repositories = repositories | ||||||
| create_new_team = Create New Team | create_new_team = Create New Team | ||||||
| org_desc = Description | 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 = Settings | ||||||
| settings.options = Options | settings.options = Options | ||||||
| @@ -258,9 +267,19 @@ members.owner = Owner | |||||||
| members.member = Member | members.member = Member | ||||||
| members.conceal = Conceal | members.conceal = Conceal | ||||||
| members.remove = Remove | members.remove = Remove | ||||||
|  | members.leave = Leave | ||||||
| members.invite_desc = Start typing a username to invite a new member to %s: | members.invite_desc = Start typing a username to invite a new member to %s: | ||||||
| members.invite_now = Invite Now | 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] | [action] | ||||||
| create_repo = created repository <a href="/%s">%s</a> | 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> | commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a> | ||||||
|   | |||||||
| @@ -79,6 +79,7 @@ Retype = 确认密码 | |||||||
| SSHTitle = SSH 密钥名称 | SSHTitle = SSH 密钥名称 | ||||||
| HttpsUrl = HTTPS URL 地址 | HttpsUrl = HTTPS URL 地址 | ||||||
| PayloadUrl = 推送地址 | PayloadUrl = 推送地址 | ||||||
|  | TeamName = 团队名称 | ||||||
|  |  | ||||||
| require_error = 不能为空。 | require_error = 不能为空。 | ||||||
| alpha_dash_error = 必须为英文字母、阿拉伯数字或横线(-_)。 | alpha_dash_error = 必须为英文字母、阿拉伯数字或横线(-_)。 | ||||||
| @@ -94,16 +95,19 @@ password_not_match = 密码与确认密码未匹配。 | |||||||
| username_been_taken = 用户名已经被占用。 | username_been_taken = 用户名已经被占用。 | ||||||
| repo_name_been_taken = 仓库名称已经被占用。 | repo_name_been_taken = 仓库名称已经被占用。 | ||||||
| org_name_been_taken = 组织名称已经被占用。 | org_name_been_taken = 组织名称已经被占用。 | ||||||
|  | team_name_been_taken = 团队名称已经被占用。 | ||||||
| email_been_used = 邮箱地址已经被使用。 | email_been_used = 邮箱地址已经被使用。 | ||||||
| ssh_key_been_used = SSH 密钥已经被使用。 | ssh_key_been_used = SSH 密钥已经被使用。 | ||||||
| illegal_username = 您的用户名包含非法字符。 | illegal_username = 您的用户名包含非法字符。 | ||||||
| illegal_repo_name = 仓库名称包含非法字符。 | illegal_repo_name = 仓库名称包含非法字符。 | ||||||
| illegal_org_name = 组织名称包含非法字符。 | illegal_org_name = 组织名称包含非法字符。 | ||||||
|  | illegal_team_name = 团队名称包含非法字符。 | ||||||
| username_password_incorrect = 用户名或密码不正确。 | username_password_incorrect = 用户名或密码不正确。 | ||||||
| enterred_invalid_repo_name = 请检查您输入的仓库名称是正确。 | enterred_invalid_repo_name = 请检查您输入的仓库名称是正确。 | ||||||
| enterred_invalid_owner_name = 请检查您输入的新所有者用户名是否正确。 | enterred_invalid_owner_name = 请检查您输入的新所有者用户名是否正确。 | ||||||
| enterred_invalid_password = 请检查您输入的密码是否正确。 | enterred_invalid_password = 请检查您输入的密码是否正确。 | ||||||
| user_not_exist = 被操作的用户不存在! | user_not_exist = 被操作的用户不存在! | ||||||
|  | last_org_owner = 被移除用户为最后一位管理员。请添加一位新的管理员再进行移除成员操作! | ||||||
|  |  | ||||||
| invalid_ssh_key = 很抱歉,我们无法验证您输入的 SSH 密钥:%s | invalid_ssh_key = 很抱歉,我们无法验证您输入的 SSH 密钥:%s | ||||||
| auth_failed = 授权验证失败:%v | auth_failed = 授权验证失败:%v | ||||||
| @@ -237,6 +241,11 @@ lower_members = 名成员 | |||||||
| lower_repositories = 个仓库 | lower_repositories = 个仓库 | ||||||
| create_new_team = 创建新的团队 | create_new_team = 创建新的团队 | ||||||
| org_desc = 组织描述 | org_desc = 组织描述 | ||||||
|  | team_name = 团队名称 | ||||||
|  | team_desc = 团队描述 | ||||||
|  | team_name_helper = 您可以使用该名称来通知改组全体成员。 | ||||||
|  | team_desc_helper = 一句话描述这个团队是做什么的。 | ||||||
|  | team_permission_desc = 请选择该团队所具有的权限等级: | ||||||
|  |  | ||||||
| settings = 组织设置 | settings = 组织设置 | ||||||
| settings.options = 基本设置 | settings.options = 基本设置 | ||||||
| @@ -258,9 +267,19 @@ members.owner = 管理员 | |||||||
| members.member = 普通成员 | members.member = 普通成员 | ||||||
| members.conceal = 隐藏身份 | members.conceal = 隐藏身份 | ||||||
| members.remove = 移除成员 | members.remove = 移除成员 | ||||||
|  | members.leave = 离开组织 | ||||||
| members.invite_desc = 请输入被邀请到组织 %s 的用户名称: | members.invite_desc = 请输入被邀请到组织 %s 的用户名称: | ||||||
| members.invite_now = 立即邀请 | 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] | [action] | ||||||
| create_repo = 创建了仓库 <a href="/%s">%s</a> | create_repo = 创建了仓库 <a href="/%s">%s</a> | ||||||
| commit_repo = 推送了 <a href="/%s/src/%s">%s</a> 分支的代码到 <a href="/%s">%s</a> | commit_repo = 推送了 <a href="/%s/src/%s">%s</a> 分支的代码到 <a href="/%s">%s</a> | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							| @@ -17,7 +17,7 @@ import ( | |||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const APP_VER = "0.4.7.0815 Alpha" | const APP_VER = "0.4.7.0816 Alpha" | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	runtime.GOMAXPROCS(runtime.NumCPU()) | 	runtime.GOMAXPROCS(runtime.NumCPU()) | ||||||
|   | |||||||
							
								
								
									
										306
									
								
								models/org.go
									
									
									
									
									
								
							
							
						
						
									
										306
									
								
								models/org.go
									
									
									
									
									
								
							| @@ -15,6 +15,9 @@ import ( | |||||||
| var ( | var ( | ||||||
| 	ErrOrgNotExist      = errors.New("Organization does not exist") | 	ErrOrgNotExist      = errors.New("Organization does not exist") | ||||||
| 	ErrTeamAlreadyExist = errors.New("Team already 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. | // 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) | 	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. | // GetOwnerTeam returns owner team of organization. | ||||||
| func (org *User) GetOwnerTeam() (*Team, error) { | func (org *User) GetOwnerTeam() (*Team, error) { | ||||||
| 	t := &Team{ | 	return org.GetTeam(OWNER_TEAM) | ||||||
| 		OrgId: org.Id, |  | ||||||
| 		Name:  OWNER_TEAM, |  | ||||||
| 	} |  | ||||||
| 	_, err := x.Get(t) |  | ||||||
| 	return t, err |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetTeams returns all teams that belong to organization. | // GetTeams returns all teams that belong to organization. | ||||||
| @@ -179,96 +182,6 @@ func DeleteOrganization(org *User) (err error) { | |||||||
| 	return sess.Commit() | 	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 | 		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() | 	sess := x.NewSession() | ||||||
| 	defer sess.Close() | 	defer sess.Close() | ||||||
| 	if err := sess.Begin(); err != nil { | 	if err := sess.Begin(); err != nil { | ||||||
| @@ -389,6 +317,127 @@ func RemoveOrgUser(orgId, uid int64) error { | |||||||
| 	return sess.Commit() | 	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 | 	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 { | 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)"` | 	Description string `form:"desc" binding:"MaxSize(255)"` | ||||||
| 	Permission  string `form:"permission"` | 	Permission  string `form:"permission"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -71,6 +71,8 @@ type Context struct { | |||||||
| 		IsAdminTeam  bool // In owner team or team that has admin permission level. | 		IsAdminTeam  bool // In owner team or team that has admin permission level. | ||||||
| 		Organization *models.User | 		Organization *models.User | ||||||
| 		OrgLink      string | 		OrgLink      string | ||||||
|  |  | ||||||
|  | 		Team *models.Team | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,15 +41,16 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | |||||||
| 			} | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Data["Org"] = ctx.Org.Organization | 		org := ctx.Org.Organization | ||||||
|  | 		ctx.Data["Org"] = org | ||||||
|  |  | ||||||
| 		if ctx.IsSigned { | 		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 { | 			if ctx.Org.IsOwner { | ||||||
| 				ctx.Org.IsMember = true | 				ctx.Org.IsMember = true | ||||||
| 				ctx.Org.IsAdminTeam = true | 				ctx.Org.IsAdminTeam = true | ||||||
| 			} else { | 			} else { | ||||||
| 				if ctx.Org.Organization.IsOrgMember(ctx.User.Id) { | 				if org.IsOrgMember(ctx.User.Id) { | ||||||
| 					ctx.Org.IsMember = true | 					ctx.Org.IsMember = true | ||||||
| 					// TODO: ctx.Org.IsAdminTeam | 					// TODO: ctx.Org.IsAdminTeam | ||||||
| 				} | 				} | ||||||
| @@ -64,7 +65,24 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | |||||||
| 		ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam | 		ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam | ||||||
| 		ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner | 		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 | 		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; |   width: 520px; | ||||||
| } | } | ||||||
| /* repository create */ | /* repository create */ | ||||||
|  | #team-create-form, | ||||||
| #repo-migrate-form, | #repo-migrate-form, | ||||||
| #repo-create-form { | #repo-create-form { | ||||||
|   width: 800px; |   width: 800px; | ||||||
|   margin: 60px auto auto auto; |   margin: 60px auto auto auto; | ||||||
|   background: white; |   background: white; | ||||||
| } | } | ||||||
|  | #team-create-form h2, | ||||||
| #repo-migrate-form h2, | #repo-migrate-form h2, | ||||||
| #repo-create-form h2 { | #repo-create-form h2 { | ||||||
|   margin: .5em 1em; |   margin: .5em 1em; | ||||||
| } | } | ||||||
|  | #team-create-form .field, | ||||||
| #repo-migrate-form .field, | #repo-migrate-form .field, | ||||||
| #repo-create-form .field { | #repo-create-form .field { | ||||||
|   margin: 1.2em 0 2em 0; |   margin: 1.2em 0 2em 0; | ||||||
| } | } | ||||||
|  | #team-create-form .ipt, | ||||||
| #repo-migrate-form .ipt, | #repo-migrate-form .ipt, | ||||||
| #repo-create-form .ipt { | #repo-create-form .ipt { | ||||||
|   width: 540px; |   width: 540px; | ||||||
| } | } | ||||||
|  | #team-create-form textarea, | ||||||
| #repo-migrate-form textarea, | #repo-migrate-form textarea, | ||||||
| #repo-create-form textarea { | #repo-create-form textarea { | ||||||
|   height: 120px; |   height: 120px; | ||||||
| } | } | ||||||
|  | #team-create-form .avatar, | ||||||
| #repo-migrate-form .avatar, | #repo-migrate-form .avatar, | ||||||
| #repo-create-form .avatar { | #repo-create-form .avatar { | ||||||
|   vertical-align: middle; |   vertical-align: middle; | ||||||
| @@ -1201,6 +1207,7 @@ The register and sign-in page style | |||||||
|   width: 28px; |   width: 28px; | ||||||
|   height: 28px; |   height: 28px; | ||||||
| } | } | ||||||
|  | #team-create-form:hover, | ||||||
| #repo-migrate-form:hover, | #repo-migrate-form:hover, | ||||||
| #repo-create-form:hover { | #repo-create-form:hover { | ||||||
|   box-shadow: 0px 0px 6px #CCC; |   box-shadow: 0px 0px 6px #CCC; | ||||||
| @@ -1681,6 +1688,9 @@ textarea#issue-add-content { | |||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|   height: 120px; |   height: 120px; | ||||||
| } | } | ||||||
|  | .org-header-alert .alert { | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
| .org-header { | .org-header { | ||||||
|   padding: 16px 0; |   padding: 16px 0; | ||||||
|   background-color: #FFF; |   background-color: #FFF; | ||||||
| @@ -1767,10 +1777,10 @@ textarea#issue-add-content { | |||||||
| .org-sidebar .panel-footer { | .org-sidebar .panel-footer { | ||||||
|   padding: .8em 1.2em; |   padding: .8em 1.2em; | ||||||
| } | } | ||||||
| #org-member-avatar-group { | .org-sidebar .member-avatar-group { | ||||||
|   padding: 15px; |   padding: 15px; | ||||||
| } | } | ||||||
| #org-member-avatar-group img { | .org-sidebar .member-avatar-group img { | ||||||
|   width: 59px; |   width: 59px; | ||||||
|   height: 59px; |   height: 59px; | ||||||
|   border-radius: 3px; |   border-radius: 3px; | ||||||
| @@ -1801,13 +1811,14 @@ textarea#issue-add-content { | |||||||
|   margin-bottom: 0; |   margin-bottom: 0; | ||||||
|   color: #777; |   color: #777; | ||||||
| } | } | ||||||
| #org-member-toolbar { | .org-toolbar { | ||||||
|   padding: 10px 0; |   padding: 10px 0; | ||||||
|  |   border-bottom: 1px solid #eee; | ||||||
| } | } | ||||||
| #org-member-list .org-member-item { | #org-member-list .org-member-item { | ||||||
|   height: 50px; |   height: 50px; | ||||||
|   line-height: 50px; |   line-height: 50px; | ||||||
|   border-top: 1px solid #eee; |   border-bottom: 1px solid #eee; | ||||||
|   padding: 15px 20px; |   padding: 15px 20px; | ||||||
| } | } | ||||||
| #org-member-list .org-member-item .member-name { | #org-member-list .org-member-item .member-name { | ||||||
| @@ -1832,3 +1843,19 @@ textarea#issue-add-content { | |||||||
| #org-member-list-block { | #org-member-list-block { | ||||||
|   padding-top: 2px; |   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 { | .org-header { | ||||||
| 	padding: 16px 0; | 	padding: 16px 0; | ||||||
| 	background-color: #FFF; | 	background-color: #FFF; | ||||||
| @@ -91,14 +94,14 @@ | |||||||
| 	.panel-footer { | 	.panel-footer { | ||||||
| 		padding: .8em 1.2em; | 		padding: .8em 1.2em; | ||||||
| 	} | 	} | ||||||
| } | 	.member-avatar-group { | ||||||
| #org-member-avatar-group { |  | ||||||
| 		padding: 15px; | 		padding: 15px; | ||||||
| 		img { | 		img { | ||||||
| 			width: 59px; | 			width: 59px; | ||||||
| 			height: 59px; | 			height: 59px; | ||||||
| 			border-radius: 3px; | 			border-radius: 3px; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
| #org-home-team-list { | #org-home-team-list { | ||||||
| 	padding: 0 15px; | 	padding: 0 15px; | ||||||
| @@ -126,14 +129,15 @@ | |||||||
| 	margin-bottom: 0; | 	margin-bottom: 0; | ||||||
| 	color: #777; | 	color: #777; | ||||||
| } | } | ||||||
| #org-member-toolbar { | .org-toolbar { | ||||||
| 	padding: 10px 0; | 	padding: 10px 0; | ||||||
|  | 	border-bottom: 1px solid #eee; | ||||||
| } | } | ||||||
| #org-member-list { | #org-member-list { | ||||||
| 	.org-member-item { | 	.org-member-item { | ||||||
| 		height: 50px; | 		height: 50px; | ||||||
| 		line-height: 50px; | 		line-height: 50px; | ||||||
| 		border-top: 1px solid #eee; | 		border-bottom: 1px solid #eee; | ||||||
| 		padding: 15px 20px; | 		padding: 15px 20px; | ||||||
| 		.member-name { | 		.member-name { | ||||||
| 			padding-left: 15px; | 			padding-left: 15px; | ||||||
| @@ -159,3 +163,23 @@ | |||||||
| #org-member-list-block { | #org-member-list-block { | ||||||
| 	padding-top: 2px; | 	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 */ | /* repository create */ | ||||||
|  |  | ||||||
|  | #team-create-form, | ||||||
| #repo-migrate-form, | #repo-migrate-form, | ||||||
| #repo-create-form { | #repo-create-form { | ||||||
| 	width: 800px; | 	width: 800px; | ||||||
|   | |||||||
| @@ -14,13 +14,13 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	MEMBERS base.TplName = "org/members" | 	MEMBERS       base.TplName = "org/member/members" | ||||||
| 	INVITE  base.TplName = "org/invite" | 	MEMBER_INVITE base.TplName = "org/member/invite" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Members(ctx *middleware.Context) { | func Members(ctx *middleware.Context) { | ||||||
| 	org := ctx.Org.Organization | 	org := ctx.Org.Organization | ||||||
| 	ctx.Data["Title"] = org.Name | 	ctx.Data["Title"] = org.FullName | ||||||
| 	ctx.Data["PageIsOrgMembers"] = true | 	ctx.Data["PageIsOrgMembers"] = true | ||||||
|  |  | ||||||
| 	if err := org.GetMembers(); err != nil { | 	if err := org.GetMembers(); err != nil { | ||||||
| @@ -60,6 +60,18 @@ func MembersAction(ctx *middleware.Context) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		err = org.RemoveMember(uid) | 		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 { | 	if err != nil { | ||||||
| @@ -75,7 +87,7 @@ func MembersAction(ctx *middleware.Context) { | |||||||
|  |  | ||||||
| func Invitation(ctx *middleware.Context) { | func Invitation(ctx *middleware.Context) { | ||||||
| 	org := ctx.Org.Organization | 	org := ctx.Org.Organization | ||||||
| 	ctx.Data["Title"] = org.Name | 	ctx.Data["Title"] = org.FullName | ||||||
| 	ctx.Data["PageIsOrgMembers"] = true | 	ctx.Data["PageIsOrgMembers"] = true | ||||||
|  |  | ||||||
| 	if ctx.Req.Method == "POST" { | 	if ctx.Req.Method == "POST" { | ||||||
| @@ -101,5 +113,5 @@ func Invitation(ctx *middleware.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.HTML(200, INVITE) | 	ctx.HTML(200, MEMBER_INVITE) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ const ( | |||||||
|  |  | ||||||
| func Home(ctx *middleware.Context) { | func Home(ctx *middleware.Context) { | ||||||
| 	org := ctx.Org.Organization | 	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)) | 	repos, err := models.GetRepositories(org.Id, ctx.IsSigned && org.IsOrgMember(ctx.User.Id)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -13,31 +13,22 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	TEAMS    base.TplName = "org/teams" | 	TEAMS    base.TplName = "org/team/teams" | ||||||
| 	TEAM_NEW base.TplName = "org/team_new" | 	TEAM_NEW base.TplName = "org/team/new" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Teams(ctx *middleware.Context) { | 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 := org.GetTeams(); err != nil { | ||||||
| 	if err != nil { | 		ctx.Handle(500, "GetTeams", err) | ||||||
| 		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) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	for _, t := range org.Teams { | 	for _, t := range org.Teams { | ||||||
| 		if err = t.GetMembers(); err != nil { | 		if err := t.GetMembers(); err != nil { | ||||||
| 			ctx.Handle(500, "org.Home(GetMembers)", err) | 			ctx.Handle(500, "GetMembers", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -46,44 +37,39 @@ func Teams(ctx *middleware.Context) { | |||||||
| 	ctx.HTML(200, TEAMS) | 	ctx.HTML(200, TEAMS) | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewTeam(ctx *middleware.Context) { | func TeamsAction(ctx *middleware.Context) { | ||||||
| 	org, err := models.GetUserByName(ctx.Params(":org")) | 	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) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if err == models.ErrUserNotExist { | 		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err) | ||||||
| 			ctx.Handle(404, "org.NewTeam(GetUserByName)", err) | 		ctx.JSON(200, map[string]interface{}{ | ||||||
| 		} else { | 			"ok":  false, | ||||||
| 			ctx.Handle(500, "org.NewTeam(GetUserByName)", err) | 			"err": err.Error(), | ||||||
| 		} | 		}) | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ctx.Data["Org"] = org |  | ||||||
|  |  | ||||||
| 	// Check ownership of organization. |  | ||||||
| 	if !org.IsOrgOwner(ctx.User.Id) { |  | ||||||
| 		ctx.Error(403) |  | ||||||
| 		return | 		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) | 	ctx.HTML(200, TEAM_NEW) | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | ||||||
| 	org, err := models.GetUserByName(ctx.Params(":org")) | 	ctx.Data["Title"] = ctx.Org.Organization.FullName | ||||||
| 	if err != nil { | 	ctx.Data["PageIsOrgTeams"] = true | ||||||
| 		if err == models.ErrUserNotExist { | 	ctx.Data["PageIsOrgTeamsNew"] = true | ||||||
| 			ctx.Handle(404, "org.NewTeamPost(GetUserByName)", err) | 	ctx.Data["Team"] = &models.Team{} | ||||||
| 		} 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 |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if ctx.HasError() { | 	if ctx.HasError() { | ||||||
| 		ctx.HTML(200, TEAM_NEW) | 		ctx.HTML(200, TEAM_NEW) | ||||||
| @@ -104,23 +90,29 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	org := ctx.Org.Organization | ||||||
|  |  | ||||||
| 	t := &models.Team{ | 	t := &models.Team{ | ||||||
| 		OrgId:       org.Id, | 		OrgId:       org.Id, | ||||||
| 		Name:        form.TeamName, | 		Name:        form.TeamName, | ||||||
| 		Description: form.Description, | 		Description: form.Description, | ||||||
| 		Authorize:   auth, | 		Authorize:   auth, | ||||||
| 	} | 	} | ||||||
| 	if err = models.NewTeam(t); err != nil { | 	if err := models.NewTeam(t); err != nil { | ||||||
| 		if err == models.ErrTeamAlreadyExist { | 		switch err { | ||||||
|  | 		case models.ErrTeamNameIllegal: | ||||||
| 			ctx.Data["Err_TeamName"] = true | 			ctx.Data["Err_TeamName"] = true | ||||||
| 			ctx.RenderWithErr("Team name has already been used", TEAM_NEW, &form) | 			ctx.RenderWithErr(ctx.Tr("form.illegal_team_name"), TEAM_NEW, &form) | ||||||
| 		} else { | 		case models.ErrTeamAlreadyExist: | ||||||
| 			ctx.Handle(500, "org.NewTeamPost(NewTeam)", err) | 			ctx.Data["Err_TeamName"] = true | ||||||
|  | 			ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), TEAM_NEW, &form) | ||||||
|  | 		default: | ||||||
|  | 			ctx.Handle(500, "NewTeam", err) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	log.Trace("%s Team created: %s/%s", ctx.Req.RequestURI, org.Name, t.Name) | 	log.Trace("Team created: %s/%s", org.Name, t.Name) | ||||||
| 	ctx.Redirect("/org/" + org.LowerName + "/teams/" + t.LowerName) | 	ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName) | ||||||
| } | } | ||||||
|  |  | ||||||
| func EditTeam(ctx *middleware.Context) { | func EditTeam(ctx *middleware.Context) { | ||||||
|   | |||||||
| @@ -20,12 +20,11 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	SIGNIN          base.TplName = "user/signin" | 	SIGNIN          base.TplName = "user/auth/signin" | ||||||
| 	SIGNUP          base.TplName = "user/signup" | 	SIGNUP          base.TplName = "user/auth/signup" | ||||||
| 	DELETE          base.TplName = "user/delete" | 	ACTIVATE        base.TplName = "user/auth/activate" | ||||||
| 	ACTIVATE        base.TplName = "user/activate" | 	FORGOT_PASSWORD base.TplName = "user/auth/forgot_passwd" | ||||||
| 	FORGOT_PASSWORD base.TplName = "user/forgot_passwd" | 	RESET_PASSWORD  base.TplName = "user/auth/reset_passwd" | ||||||
| 	RESET_PASSWORD  base.TplName = "user/reset_passwd" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func SignIn(ctx *middleware.Context) { | 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"> | 	<div class="container clear"> | ||||||
| 		<img class="avatar-100 left" src="{{.Org.AvatarLink}}?s=140"/> | 		<img class="avatar-100 left" src="{{.Org.AvatarLink}}?s=140"/> | ||||||
| 		<div id="org-home-header-info"> | 		<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}} | 			{{if .Org.Description}}<p>{{.Org.Description}}</p>{{end}} | ||||||
|             <ul class="text-grey"> |             <ul class="text-grey"> | ||||||
|                 {{if .Org.Location}}<li><span class="octicon octicon-location"></span> <span>{{.Org.Location}}</span></li>{{end}} |                 {{if .Org.Location}}<li><span class="octicon octicon-location"></span> <span>{{.Org.Location}}</span></li>{{end}} | ||||||
| @@ -17,7 +20,7 @@ | |||||||
| <div class="container"> | <div class="container"> | ||||||
|     <div id="org-home-repo-list" class="left grid-2-3"> |     <div id="org-home-repo-list" class="left grid-2-3"> | ||||||
|         <div class="clear"> |         <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> |             <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}} |         	{{end}} | ||||||
|         </div> |         </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> | 	    			<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> | 	    			<strong>{{.i18n.Tr "org.people"}}</strong> | ||||||
| 	    		</div> | 	    		</div> | ||||||
| 	    		<div class="panel-body" id="org-member-avatar-group"> | 	    		<div class="panel-body member-avatar-group"> | ||||||
| 	    			{{range .Members}} | 	    			{{range .Members}} | ||||||
| 	    			<a href="/{{.Name}}"><img src="{{.AvatarLink}}"></a> | 	    			<a href="/{{.Name}}" title="{{.Name}}"><img src="{{.AvatarLink}}"></a> | ||||||
| 	    			{{end}} | 	    			{{end}} | ||||||
| 	    		</div> | 	    		</div> | ||||||
| 	    		{{if .IsAdminTeam}} | 	    		{{if .IsOrganizationOwner}} | ||||||
| 	    		<div class="panel-footer"> | 	    		<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> | 	    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="/org/{{.Org.LowerName}}/invitations/new">{{.i18n.Tr "org.invite_someone"}}</a> | ||||||
| 	    		</div> | 	    		</div> | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| {{template "ng/base/head" .}} | {{template "ng/base/head" .}} | ||||||
| {{template "ng/base/header" .}} | {{template "ng/base/header" .}} | ||||||
| {{template "org/header" .}} | {{template "org/base/header" .}} | ||||||
| <div class="container"> | <div class="container"> | ||||||
| 	<div class="invite-box" id="invite-box"> | 	<div class="invite-box" id="invite-box"> | ||||||
|     	{{template "ng/base/alert" .}} |     	{{template "ng/base/alert" .}} | ||||||
| @@ -1,9 +1,11 @@ | |||||||
| {{template "ng/base/head" .}} | {{template "ng/base/head" .}} | ||||||
| {{template "ng/base/header" .}} | {{template "ng/base/header" .}} | ||||||
| {{template "org/header" .}} | {{template "org/base/header" .}} | ||||||
| <div class="container"> | <div class="container"> | ||||||
|  | 	<div class="org-header-alert"> | ||||||
| 		{{template "ng/base/alert" .}} | 		{{template "ng/base/alert" .}} | ||||||
| 	<div class="clear" id="org-member-toolbar"> | 	</div> | ||||||
|  | 	<div class="org-toolbar clear"> | ||||||
| 		{{if .IsAdminTeam}} | 		{{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> |         <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}} | 		{{end}} | ||||||
| @@ -25,7 +27,11 @@ | |||||||
| 				{{end}} | 				{{end}} | ||||||
| 				</li> | 				</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> | 				<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}} | 				{{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"> | 					<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> | 						<a class="btn btn-red btn-link btn-radius" href="{{$.OrgLink}}/members/action/remove?uid={{.Id}}">{{$.i18n.Tr "org.members.remove"}}</a> | ||||||
| 					</li> | 					</li> | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| {{template "ng/base/head" .}} | {{template "ng/base/head" .}} | ||||||
| {{template "ng/base/header" .}} | {{template "ng/base/header" .}} | ||||||
|  | {{template "org/header" .}} | ||||||
| <div id="setting-wrapper" class="main-wrapper"> | <div id="setting-wrapper" class="main-wrapper"> | ||||||
|     <div id="org-setting" class="container clear"> |     <div id="org-setting" class="container clear"> | ||||||
|         {{template "org/settings/nav" .}} |         {{template "org/settings/nav" .}} | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| {{template "ng/base/head" .}} | {{template "ng/base/head" .}} | ||||||
| {{template "ng/base/header" .}} | {{template "ng/base/header" .}} | ||||||
|  | {{template "org/header" .}} | ||||||
| <div id="setting-wrapper" class="main-wrapper"> | <div id="setting-wrapper" class="main-wrapper"> | ||||||
|     <div id="org-setting" class="container clear"> |     <div id="org-setting" class="container clear"> | ||||||
| 	        {{template "org/settings/nav" .}} | 	        {{template "org/settings/nav" .}} | ||||||
|   | |||||||
							
								
								
									
										48
									
								
								templates/org/team/new.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								templates/org/team/new.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -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" .}} | ||||||
							
								
								
									
										42
									
								
								templates/org/team/teams.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								templates/org/team/teams.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -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"> | <div id="repo-wrapper"> | ||||||
|     <form id="repo-create-form" class="form form-align panel panel-radius" action="/repo/create" method="post"> |     <form id="repo-create-form" class="form form-align panel panel-radius" action="/repo/create" method="post"> | ||||||
|         {{.CsrfTokenHtml}} |         {{.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"> |         <div class="panel-content"> | ||||||
|             {{template "ng/base/alert" .}} |             {{template "ng/base/alert" .}} | ||||||
|             <div class="field"> |             <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" .}} |  | ||||||
		Reference in New Issue
	
	Block a user