Finsih add/remove repo in organization
This commit is contained in:
parent
f2c263c54f
commit
74b31566cf
19 changed files with 485 additions and 107 deletions
|
@ -141,7 +141,7 @@ func runWeb(*cli.Context) {
|
||||||
r.Get("/users/search", v1.SearchUsers)
|
r.Get("/users/search", v1.SearchUsers)
|
||||||
|
|
||||||
// Repositories.
|
// Repositories.
|
||||||
r.Get("/orgs/:org/repos/search", v1.SearchOrgRepositoreis)
|
r.Get("/repos/search", v1.SearchRepos)
|
||||||
|
|
||||||
r.Any("/*", func(ctx *middleware.Context) {
|
r.Any("/*", func(ctx *middleware.Context) {
|
||||||
ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL})
|
ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL})
|
||||||
|
@ -236,7 +236,9 @@ func runWeb(*cli.Context) {
|
||||||
|
|
||||||
r.Get("/teams", org.Teams)
|
r.Get("/teams", org.Teams)
|
||||||
r.Get("/teams/:team", org.TeamMembers)
|
r.Get("/teams/:team", org.TeamMembers)
|
||||||
|
r.Get("/teams/:team/repositories", org.TeamRepositories)
|
||||||
r.Get("/teams/:team/action/:action", org.TeamsAction)
|
r.Get("/teams/:team/action/:action", org.TeamsAction)
|
||||||
|
r.Get("/teams/:team/action/repo/:action", org.TeamsRepoAction)
|
||||||
}, middleware.OrgAssignment(true, true))
|
}, middleware.OrgAssignment(true, true))
|
||||||
|
|
||||||
m.Group("/:org", func(r *macaron.Router) {
|
m.Group("/:org", func(r *macaron.Router) {
|
||||||
|
|
|
@ -290,6 +290,9 @@ teams.delete_team_success = Given team has been successfully deleted.
|
||||||
teams.read_permission_desc = This team grants <strong>Read</strong> access: members can view and clone the team's repositories.
|
teams.read_permission_desc = This team grants <strong>Read</strong> access: members can view and clone the team's repositories.
|
||||||
teams.write_permission_desc = This team grants <strong>Write</strong> access: members can read from and push to the team's repositories.
|
teams.write_permission_desc = This team grants <strong>Write</strong> access: members can read from and push to the team's repositories.
|
||||||
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to, and add collaborators to the team's repositories.
|
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to, and add collaborators to the team's repositories.
|
||||||
|
teams.repositories = Team Repositories
|
||||||
|
teams.add_team_repository = Add Team Repository
|
||||||
|
teams.remove_repo = Remove
|
||||||
|
|
||||||
[action]
|
[action]
|
||||||
create_repo = created repository <a href="/%s">%s</a>
|
create_repo = created repository <a href="/%s">%s</a>
|
||||||
|
|
|
@ -290,6 +290,9 @@ teams.delete_team_success = 指定团队已经被成功删除!
|
||||||
teams.read_permission_desc = 该团队拥有对所属仓库的 <strong>读取</strong> 权限,团队成员可以进行查看和克隆等只读操作。
|
teams.read_permission_desc = 该团队拥有对所属仓库的 <strong>读取</strong> 权限,团队成员可以进行查看和克隆等只读操作。
|
||||||
teams.write_permission_desc = 该团队拥有对所属仓库的 <strong>读取</strong> 和 <strong>写入</strong> 的权限。
|
teams.write_permission_desc = 该团队拥有对所属仓库的 <strong>读取</strong> 和 <strong>写入</strong> 的权限。
|
||||||
teams.admin_permission_desc = 该团队拥有一定的 <strong>管理</strong> 权限,团队成员可以读取、克隆、推送以及添加其它仓库协作者。
|
teams.admin_permission_desc = 该团队拥有一定的 <strong>管理</strong> 权限,团队成员可以读取、克隆、推送以及添加其它仓库协作者。
|
||||||
|
teams.repositories = 团队仓库
|
||||||
|
teams.add_team_repository = 添加团队仓库
|
||||||
|
teams.remove_repo = 移除仓库
|
||||||
|
|
||||||
[action]
|
[action]
|
||||||
create_repo = 创建了仓库 <a href="/%s">%s</a>
|
create_repo = 创建了仓库 <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.0825 Alpha"
|
const APP_VER = "0.4.7.0826 Alpha"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
|
@ -21,10 +21,10 @@ const (
|
||||||
// Access represents the accessibility of user to repository.
|
// Access represents the accessibility of user to repository.
|
||||||
type Access struct {
|
type Access struct {
|
||||||
Id int64
|
Id int64
|
||||||
UserName string `xorm:"unique(s)"`
|
UserName string `xorm:"UNIQUE(s)"`
|
||||||
RepoName string `xorm:"unique(s)"` // <user name>/<repo name>
|
RepoName string `xorm:"UNIQUE(s)"` // <user name>/<repo name>
|
||||||
Mode AccessType `xorm:"unique(s)"`
|
Mode AccessType `xorm:"UNIQUE(s)"`
|
||||||
Created time.Time `xorm:"created"`
|
Created time.Time `xorm:"CREATED"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAccess adds new access record.
|
// AddAccess adds new access record.
|
||||||
|
|
230
models/org.go
230
models/org.go
|
@ -369,6 +369,13 @@ const (
|
||||||
ORG_ADMIN
|
ORG_ADMIN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func AuthorizeToAccessType(auth AuthorizeType) AccessType {
|
||||||
|
if auth == ORG_READABLE {
|
||||||
|
return READABLE
|
||||||
|
}
|
||||||
|
return WRITABLE
|
||||||
|
}
|
||||||
|
|
||||||
const OWNER_TEAM = "Owners"
|
const OWNER_TEAM = "Owners"
|
||||||
|
|
||||||
// Team represents a organization team.
|
// Team represents a organization team.
|
||||||
|
@ -433,6 +440,142 @@ func (t *Team) RemoveMember(uid int64) error {
|
||||||
return RemoveTeamMember(t.OrgId, t.Id, uid)
|
return RemoveTeamMember(t.OrgId, t.Id, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addAccessWithAuthorize inserts or updates access with given mode.
|
||||||
|
func addAccessWithAuthorize(sess *xorm.Session, access *Access, mode AccessType) error {
|
||||||
|
has, err := x.Get(access)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("fail to get access: %v", err)
|
||||||
|
}
|
||||||
|
access.Mode = mode
|
||||||
|
if has {
|
||||||
|
if _, err = sess.Id(access.Id).Update(access); err != nil {
|
||||||
|
return fmt.Errorf("fail to update access: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err = sess.Insert(access); err != nil {
|
||||||
|
return fmt.Errorf("fail to insert access: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRepository adds new repository to team of organization.
|
||||||
|
func (t *Team) AddRepository(repo *Repository) (err error) {
|
||||||
|
idStr := "$" + com.ToStr(repo.Id) + "|"
|
||||||
|
if repo.OwnerId != t.OrgId {
|
||||||
|
return errors.New("Repository not belong to organization")
|
||||||
|
} else if strings.Contains(t.RepoIds, idStr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repo.GetOwner(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err = t.GetMembers(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.NumRepos++
|
||||||
|
t.RepoIds += idStr
|
||||||
|
if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give access to team members.
|
||||||
|
mode := AuthorizeToAccessType(t.Authorize)
|
||||||
|
|
||||||
|
for _, u := range t.Members {
|
||||||
|
auth, err := GetHighestAuthorize(t.OrgId, u.Id, t.Id, repo.Id)
|
||||||
|
if err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
access := &Access{
|
||||||
|
UserName: u.LowerName,
|
||||||
|
RepoName: path.Join(repo.Owner.LowerName, repo.LowerName),
|
||||||
|
}
|
||||||
|
if auth == 0 {
|
||||||
|
access.Mode = mode
|
||||||
|
if _, err = sess.Insert(access); err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return fmt.Errorf("fail to insert access: %v", err)
|
||||||
|
}
|
||||||
|
} else if auth < t.Authorize {
|
||||||
|
if err = addAccessWithAuthorize(sess, access, mode); err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRepository removes repository from team of organization.
|
||||||
|
func (t *Team) RemoveRepository(repoId int64) error {
|
||||||
|
idStr := "$" + com.ToStr(repoId) + "|"
|
||||||
|
if !strings.Contains(t.RepoIds, idStr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := GetRepositoryById(repoId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repo.GetOwner(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err = t.GetMembers(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.NumRepos--
|
||||||
|
t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1)
|
||||||
|
if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove access to team members.
|
||||||
|
for _, u := range t.Members {
|
||||||
|
auth, err := GetHighestAuthorize(t.OrgId, u.Id, t.Id, repo.Id)
|
||||||
|
if err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
access := &Access{
|
||||||
|
UserName: u.LowerName,
|
||||||
|
RepoName: path.Join(repo.Owner.LowerName, repo.LowerName),
|
||||||
|
}
|
||||||
|
if auth == 0 {
|
||||||
|
if _, err = sess.Delete(access); err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return fmt.Errorf("fail to delete access: %v", err)
|
||||||
|
}
|
||||||
|
} else if auth < t.Authorize {
|
||||||
|
if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil {
|
||||||
|
sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
// NewTeam creates a record of new team.
|
// NewTeam creates a record of new team.
|
||||||
// It's caller's responsibility to assign organization ID.
|
// It's caller's responsibility to assign organization ID.
|
||||||
func NewTeam(t *Team) error {
|
func NewTeam(t *Team) error {
|
||||||
|
@ -554,16 +697,10 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mode := READABLE
|
// Update access.
|
||||||
if t.Authorize > ORG_READABLE {
|
mode := AuthorizeToAccessType(t.Authorize)
|
||||||
mode = WRITABLE
|
|
||||||
}
|
|
||||||
access := &Access{
|
|
||||||
Mode: mode,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repo := range t.Repos {
|
for _, repo := range t.Repos {
|
||||||
access.RepoName = path.Join(org.LowerName, repo.LowerName)
|
|
||||||
for _, u := range t.Members {
|
for _, u := range t.Members {
|
||||||
// ORG_WRITABLE is the highest authorize level for now.
|
// ORG_WRITABLE is the highest authorize level for now.
|
||||||
// Skip checking others if current team has this level.
|
// Skip checking others if current team has this level.
|
||||||
|
@ -578,8 +715,11 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
access.UserName = u.LowerName
|
access := &Access{
|
||||||
if _, err = sess.Update(access); err != nil {
|
UserName: u.LowerName,
|
||||||
|
RepoName: path.Join(org.LowerName, repo.LowerName),
|
||||||
|
}
|
||||||
|
if err = addAccessWithAuthorize(sess, access, mode); err != nil {
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -617,36 +757,26 @@ func DeleteTeam(t *Team) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all accesses.
|
// Delete all accesses.
|
||||||
mode := READABLE
|
|
||||||
if t.Authorize > ORG_READABLE {
|
|
||||||
mode = WRITABLE
|
|
||||||
}
|
|
||||||
access := new(Access)
|
|
||||||
|
|
||||||
for _, repo := range t.Repos {
|
for _, repo := range t.Repos {
|
||||||
access.RepoName = path.Join(org.LowerName, repo.LowerName)
|
|
||||||
for _, u := range t.Members {
|
for _, u := range t.Members {
|
||||||
access.UserName = u.LowerName
|
|
||||||
access.Mode = mode
|
|
||||||
auth, err := GetHighestAuthorize(org.Id, u.Id, t.Id, repo.Id)
|
auth, err := GetHighestAuthorize(org.Id, u.Id, t.Id, repo.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
access := &Access{
|
||||||
|
UserName: u.LowerName,
|
||||||
|
RepoName: path.Join(org.LowerName, repo.LowerName),
|
||||||
|
}
|
||||||
if auth == 0 {
|
if auth == 0 {
|
||||||
if _, err = sess.Delete(access); err != nil {
|
if _, err = sess.Delete(access); err != nil {
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return fmt.Errorf("fail to delete access: %v", err)
|
||||||
}
|
}
|
||||||
} else if auth < t.Authorize {
|
} else if auth < t.Authorize {
|
||||||
// Downgrade authorize level.
|
// Downgrade authorize level.
|
||||||
mode := READABLE
|
if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil {
|
||||||
if auth > ORG_READABLE {
|
|
||||||
mode = WRITABLE
|
|
||||||
}
|
|
||||||
access.Mode = mode
|
|
||||||
if _, err = sess.Update(access); err != nil {
|
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -779,15 +909,6 @@ func AddTeamMember(orgId, teamId, uid int64) error {
|
||||||
TeamId: teamId,
|
TeamId: teamId,
|
||||||
}
|
}
|
||||||
|
|
||||||
mode := READABLE
|
|
||||||
if t.Authorize > ORG_READABLE {
|
|
||||||
mode = WRITABLE
|
|
||||||
}
|
|
||||||
access := &Access{
|
|
||||||
UserName: u.LowerName,
|
|
||||||
Mode: mode,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.Insert(tu); err != nil {
|
if _, err = sess.Insert(tu); err != nil {
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return err
|
||||||
|
@ -797,6 +918,7 @@ func AddTeamMember(orgId, teamId, uid int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give access to team repositories.
|
// Give access to team repositories.
|
||||||
|
mode := AuthorizeToAccessType(t.Authorize)
|
||||||
for _, repo := range t.Repos {
|
for _, repo := range t.Repos {
|
||||||
auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
|
auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -804,22 +926,24 @@ func AddTeamMember(orgId, teamId, uid int64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
access.Id = 0
|
access := &Access{
|
||||||
access.RepoName = path.Join(org.LowerName, repo.LowerName)
|
UserName: u.LowerName,
|
||||||
|
RepoName: path.Join(org.LowerName, repo.LowerName),
|
||||||
|
}
|
||||||
// Equal 0 means given access doesn't exist.
|
// Equal 0 means given access doesn't exist.
|
||||||
if auth == 0 {
|
if auth == 0 {
|
||||||
|
access.Mode = mode
|
||||||
if _, err = sess.Insert(access); err != nil {
|
if _, err = sess.Insert(access); err != nil {
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return fmt.Errorf("fail to insert access: %v", err)
|
||||||
}
|
}
|
||||||
} else if auth < t.Authorize {
|
} else if auth < t.Authorize {
|
||||||
if _, err = sess.Update(access); err != nil {
|
if err = addAccessWithAuthorize(sess, access, mode); err != nil {
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("kao")
|
|
||||||
|
|
||||||
// We make sure it exists before.
|
// We make sure it exists before.
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
|
@ -889,10 +1013,6 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete access to team repositories.
|
// Delete access to team repositories.
|
||||||
access := &Access{
|
|
||||||
UserName: u.LowerName,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repo := range t.Repos {
|
for _, repo := range t.Repos {
|
||||||
auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
|
auth, err := GetHighestAuthorize(orgId, uid, teamId, repo.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -900,22 +1020,22 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
access := &Access{
|
||||||
|
UserName: u.LowerName,
|
||||||
|
RepoName: path.Join(org.LowerName, repo.LowerName),
|
||||||
|
}
|
||||||
// Delete access if this is the last team user belongs to.
|
// Delete access if this is the last team user belongs to.
|
||||||
if auth == 0 {
|
if auth == 0 {
|
||||||
access.RepoName = path.Join(org.LowerName, repo.LowerName)
|
if _, err = sess.Delete(access); err != nil {
|
||||||
_, err = sess.Delete(access)
|
sess.Rollback()
|
||||||
|
return fmt.Errorf("fail to delete access: %v", err)
|
||||||
|
}
|
||||||
} else if auth < t.Authorize {
|
} else if auth < t.Authorize {
|
||||||
// Downgrade authorize level.
|
// Downgrade authorize level.
|
||||||
mode := READABLE
|
if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil {
|
||||||
if auth > ORG_READABLE {
|
sess.Rollback()
|
||||||
mode = WRITABLE
|
return err
|
||||||
}
|
}
|
||||||
access.Mode = mode
|
|
||||||
_, err = sess.Update(access)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
sess.Rollback()
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -519,12 +519,11 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
us, err := GetTeamMembers(u.Id, t.Id)
|
if err = t.GetMembers(); err != nil {
|
||||||
if err != nil {
|
|
||||||
sess.Rollback()
|
sess.Rollback()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, u := range us {
|
for _, u := range t.Members {
|
||||||
access.Id = 0
|
access.Id = 0
|
||||||
access.UserName = u.LowerName
|
access.UserName = u.LowerName
|
||||||
if _, err = sess.Insert(access); err != nil {
|
if _, err = sess.Insert(access); err != nil {
|
||||||
|
@ -963,6 +962,37 @@ func GetCollaborators(repoName string) (us []*User, err error) {
|
||||||
return us, nil
|
return us, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchOption struct {
|
||||||
|
Keyword string
|
||||||
|
Uid int64
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRepositoryByName returns given number of repositories whose name contains keyword.
|
||||||
|
func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
|
||||||
|
// Prevent SQL inject.
|
||||||
|
opt.Keyword = strings.TrimSpace(opt.Keyword)
|
||||||
|
if len(opt.Keyword) == 0 {
|
||||||
|
return repos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.Keyword = strings.Split(opt.Keyword, " ")[0]
|
||||||
|
if len(opt.Keyword) == 0 {
|
||||||
|
return repos, nil
|
||||||
|
}
|
||||||
|
opt.Keyword = strings.ToLower(opt.Keyword)
|
||||||
|
|
||||||
|
repos = make([]*Repository, 0, opt.Limit)
|
||||||
|
|
||||||
|
// Append conditions.
|
||||||
|
sess := x.Limit(opt.Limit)
|
||||||
|
if opt.Uid > 0 {
|
||||||
|
sess.Where("owner_id=?", opt.Uid)
|
||||||
|
}
|
||||||
|
sess.And("lower_name like '%" + opt.Keyword + "%'").Find(&repos)
|
||||||
|
return repos, err
|
||||||
|
}
|
||||||
|
|
||||||
// Watch is connection request for receiving repository notifycation.
|
// Watch is connection request for receiving repository notifycation.
|
||||||
type Watch struct {
|
type Watch struct {
|
||||||
Id int64
|
Id int64
|
||||||
|
|
|
@ -521,21 +521,21 @@ func GetUserByEmail(email string) (*User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchUserByName returns given number of users whose name contains keyword.
|
// SearchUserByName returns given number of users whose name contains keyword.
|
||||||
func SearchUserByName(key string, limit int) (us []*User, err error) {
|
func SearchUserByName(opt SearchOption) (us []*User, err error) {
|
||||||
// Prevent SQL inject.
|
// Prevent SQL inject.
|
||||||
key = strings.TrimSpace(key)
|
opt.Keyword = strings.TrimSpace(opt.Keyword)
|
||||||
if len(key) == 0 {
|
if len(opt.Keyword) == 0 {
|
||||||
return us, nil
|
return us, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
key = strings.Split(key, " ")[0]
|
opt.Keyword = strings.Split(opt.Keyword, " ")[0]
|
||||||
if len(key) == 0 {
|
if len(opt.Keyword) == 0 {
|
||||||
return us, nil
|
return us, nil
|
||||||
}
|
}
|
||||||
key = strings.ToLower(key)
|
opt.Keyword = strings.ToLower(opt.Keyword)
|
||||||
|
|
||||||
us = make([]*User, 0, limit)
|
us = make([]*User, 0, opt.Limit)
|
||||||
err = x.Limit(limit).Where("type=0").And("lower_name like '%" + key + "%'").Find(&us)
|
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like '%" + opt.Keyword + "%'").Find(&us)
|
||||||
return us, err
|
return us, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1298,32 +1298,38 @@ The register and sign-in page style
|
||||||
.repo-setting-zone {
|
.repo-setting-zone {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list,
|
||||||
#team-members-list,
|
#team-members-list,
|
||||||
#repo-collab-list {
|
#repo-collab-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 10px 0 5px 0;
|
padding: 10px 0 5px 0;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list li.collab,
|
||||||
#team-members-list li.collab,
|
#team-members-list li.collab,
|
||||||
#repo-collab-list li.collab {
|
#repo-collab-list li.collab {
|
||||||
clear: both;
|
clear: both;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
padding: 0 15px 0 15px;
|
padding: 0 15px 0 15px;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list a.member,
|
||||||
#team-members-list a.member,
|
#team-members-list a.member,
|
||||||
#repo-collab-list a.member {
|
#repo-collab-list a.member {
|
||||||
color: #444;
|
color: #444;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list a.member:hover,
|
||||||
#team-members-list a.member:hover,
|
#team-members-list a.member:hover,
|
||||||
#repo-collab-list a.member:hover {
|
#repo-collab-list a.member:hover {
|
||||||
color: #4183C4;
|
color: #4183C4;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list .avatar,
|
||||||
#team-members-list .avatar,
|
#team-members-list .avatar,
|
||||||
#repo-collab-list .avatar {
|
#repo-collab-list .avatar {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list .remove-collab,
|
||||||
#team-members-list .remove-collab,
|
#team-members-list .remove-collab,
|
||||||
#repo-collab-list .remove-collab {
|
#repo-collab-list .remove-collab {
|
||||||
color: #DD4B39;
|
color: #DD4B39;
|
||||||
|
@ -1877,14 +1883,26 @@ textarea#issue-add-content {
|
||||||
#org-team-card .panel-footer {
|
#org-team-card .panel-footer {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list .panel-body .search,
|
||||||
#team-members-list .panel-body .search {
|
#team-members-list .panel-body .search {
|
||||||
padding: 4px 0 10px 10px;
|
padding: 4px 0 10px 10px;
|
||||||
border-bottom: 1px solid #dddddd;
|
border-bottom: 1px solid #dddddd;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list li.collab,
|
||||||
#team-members-list li.collab {
|
#team-members-list li.collab {
|
||||||
padding-top: 10px !important;
|
padding-top: 10px !important;
|
||||||
border-bottom: 1px solid #dddddd;
|
border-bottom: 1px solid #dddddd;
|
||||||
}
|
}
|
||||||
#team-members-list li.collab:last-child {
|
#team-repositories-list li:last-child,
|
||||||
border-bottom: 0;
|
#team-members-list li:last-child {
|
||||||
|
border-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
#team-repositories-list li a .octicon {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
#team-repositories-list li .member {
|
||||||
|
color: #428bca;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,6 +218,26 @@ var Gogs = {};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search repositories by keyword.
|
||||||
|
Gogs.searchRepos = function (val, $target, $param) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/repos/search?q=' + val + '&' + $param,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (json) {
|
||||||
|
if (json.ok && json.data.length) {
|
||||||
|
var html = '';
|
||||||
|
$.each(json.data, function (i, item) {
|
||||||
|
html += '<li><a><span class="octicon octicon-repo"></span> ' + item.repolink + '</a></li>';
|
||||||
|
});
|
||||||
|
$target.html(html);
|
||||||
|
$target.toggleShow();
|
||||||
|
} else {
|
||||||
|
$target.toggleHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
||||||
function initCore() {
|
function initCore() {
|
||||||
|
@ -358,7 +378,7 @@ function initOrgTeamCreate() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
var $form = $('#team-create-form')
|
var $form = $('#team-create-form');
|
||||||
$form.attr('action', $form.data('delete-url'));
|
$form.attr('action', $form.data('delete-url'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -383,7 +403,28 @@ function initTeamMembersList() {
|
||||||
$('#org-team-members-add').val($(this).text());
|
$('#org-team-members-add').val($(this).text());
|
||||||
$ul.toggleHide();
|
$ul.toggleHide();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTeamRepositoriesList() {
|
||||||
|
// Add team repository.
|
||||||
|
var $ul = $('#org-team-repositories-list');
|
||||||
|
$('#org-team-repositories-add').on('keyup', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
if (!$this.val()) {
|
||||||
|
$ul.toggleHide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Gogs.searchRepos($this.val(), $ul, 'uid=' + $this.data('uid'));
|
||||||
|
}).on('focus', function () {
|
||||||
|
if (!$(this).val()) {
|
||||||
|
$ul.toggleHide();
|
||||||
|
} else {
|
||||||
|
$ul.toggleShow();
|
||||||
|
}
|
||||||
|
}).next().next().find('ul').on("click", 'li', function () {
|
||||||
|
$('#org-team-repositories-add').val($(this).text());
|
||||||
|
$ul.toggleHide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@ -409,6 +450,9 @@ $(document).ready(function () {
|
||||||
if ($('#team-members-list').length) {
|
if ($('#team-members-list').length) {
|
||||||
initTeamMembersList();
|
initTeamMembersList();
|
||||||
}
|
}
|
||||||
|
if ($('#team-repositories-list').length) {
|
||||||
|
initTeamRepositoriesList();
|
||||||
|
}
|
||||||
|
|
||||||
Tabs('#dashboard-sidebar-menu');
|
Tabs('#dashboard-sidebar-menu');
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
@import "../ui/var";
|
||||||
.org-header-alert .alert {
|
.org-header-alert .alert {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
@ -197,18 +198,32 @@
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#team-repositories-list,
|
||||||
#team-members-list {
|
#team-members-list {
|
||||||
.panel-body .search {
|
.panel-body .search {
|
||||||
padding: 4px 0 10px 10px;
|
padding: 4px 0 10px 10px;
|
||||||
border-bottom: 1px solid #dddddd;
|
border-bottom: 1px solid #dddddd;
|
||||||
}
|
}
|
||||||
}
|
li {
|
||||||
#team-members-list {
|
&.collab {
|
||||||
li.collab {
|
padding-top: 10px !important;
|
||||||
padding-top: 10px !important;
|
border-bottom: 1px solid #dddddd;
|
||||||
border-bottom: 1px solid #dddddd;
|
}
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#team-repositories-list {
|
||||||
|
li {
|
||||||
|
a .octicon {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.member {
|
||||||
|
color: @linkColor;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -426,6 +426,7 @@ border-top-right-radius: .25em;
|
||||||
.repo-setting-zone {
|
.repo-setting-zone {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
}
|
}
|
||||||
|
#team-repositories-list,
|
||||||
#team-members-list,
|
#team-members-list,
|
||||||
#repo-collab-list {
|
#repo-collab-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
57
routers/api/v1/repos.go
Normal file
57
routers/api/v1/repos.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/models"
|
||||||
|
"github.com/gogits/gogs/modules/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
type repo struct {
|
||||||
|
RepoLink string `json:"repolink"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SearchRepos(ctx *middleware.Context) {
|
||||||
|
opt := models.SearchOption{
|
||||||
|
Keyword: path.Base(ctx.Query("q")),
|
||||||
|
Uid: com.StrTo(ctx.Query("uid")).MustInt64(),
|
||||||
|
Limit: com.StrTo(ctx.Query("limit")).MustInt(),
|
||||||
|
}
|
||||||
|
if opt.Limit == 0 {
|
||||||
|
opt.Limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
repos, err := models.SearchRepositoryByName(opt)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(500, map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]*repo, len(repos))
|
||||||
|
for i := range repos {
|
||||||
|
if err = repos[i].GetOwner(); err != nil {
|
||||||
|
ctx.JSON(500, map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
results[i] = &repo{
|
||||||
|
RepoLink: path.Join(repos[i].Owner.Name, repos[i].Name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Render.JSON(200, map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"data": results,
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gogits/gogs/modules/middleware"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SearchOrgRepositoreis(ctx *middleware.Context) {
|
|
||||||
|
|
||||||
}
|
|
|
@ -17,21 +17,29 @@ type user struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchUsers(ctx *middleware.Context) {
|
func SearchUsers(ctx *middleware.Context) {
|
||||||
q := ctx.Query("q")
|
opt := models.SearchOption{
|
||||||
limit, err := com.StrTo(ctx.Query("limit")).Int()
|
Keyword: ctx.Query("q"),
|
||||||
if err != nil {
|
Limit: com.StrTo(ctx.Query("limit")).MustInt(),
|
||||||
limit = 10
|
}
|
||||||
|
if opt.Limit == 0 {
|
||||||
|
opt.Limit = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
us, err := models.SearchUserByName(q, limit)
|
us, err := models.SearchUserByName(opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(500, nil)
|
ctx.JSON(500, map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
results := make([]*user, len(us))
|
results := make([]*user, len(us))
|
||||||
for i := range us {
|
for i := range us {
|
||||||
results[i] = &user{us[i].Name, us[i].AvatarLink()}
|
results[i] = &user{
|
||||||
|
UserName: us[i].Name,
|
||||||
|
AvatarLink: us[i].AvatarLink(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Render.JSON(200, map[string]interface{}{
|
ctx.Render.JSON(200, map[string]interface{}{
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package org
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
"github.com/gogits/gogs/models"
|
||||||
|
@ -15,9 +17,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TEAMS base.TplName = "org/team/teams"
|
TEAMS base.TplName = "org/team/teams"
|
||||||
TEAM_NEW base.TplName = "org/team/new"
|
TEAM_NEW base.TplName = "org/team/new"
|
||||||
TEAM_MEMBERS base.TplName = "org/team/members"
|
TEAM_MEMBERS base.TplName = "org/team/members"
|
||||||
|
TEAM_REPOSITORIES base.TplName = "org/team/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Teams(ctx *middleware.Context) {
|
func Teams(ctx *middleware.Context) {
|
||||||
|
@ -108,6 +111,38 @@ func TeamsAction(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TeamsRepoAction(ctx *middleware.Context) {
|
||||||
|
if !ctx.Org.IsOwner {
|
||||||
|
ctx.Error(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch ctx.Params(":action") {
|
||||||
|
case "add":
|
||||||
|
repoName := path.Base(ctx.Query("repo-name"))
|
||||||
|
var repo *models.Repository
|
||||||
|
repo, err = models.GetRepositoryByName(ctx.Org.Organization.Id, repoName)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "GetRepositoryByName", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ctx.Org.Team.AddRepository(repo)
|
||||||
|
case "remove":
|
||||||
|
err = ctx.Org.Team.RemoveRepository(com.StrTo(ctx.Query("repoid")).MustInt64())
|
||||||
|
}
|
||||||
|
|
||||||
|
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/" + ctx.Org.Team.LowerName + "/repositories")
|
||||||
|
}
|
||||||
|
|
||||||
func NewTeam(ctx *middleware.Context) {
|
func NewTeam(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = ctx.Org.Organization.FullName
|
ctx.Data["Title"] = ctx.Org.Organization.FullName
|
||||||
ctx.Data["PageIsOrgTeams"] = true
|
ctx.Data["PageIsOrgTeams"] = true
|
||||||
|
@ -176,6 +211,16 @@ func TeamMembers(ctx *middleware.Context) {
|
||||||
ctx.HTML(200, TEAM_MEMBERS)
|
ctx.HTML(200, TEAM_MEMBERS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TeamRepositories(ctx *middleware.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Org.Team.Name
|
||||||
|
ctx.Data["PageIsOrgTeams"] = true
|
||||||
|
if err := ctx.Org.Team.GetRepositories(); err != nil {
|
||||||
|
ctx.Handle(500, "GetRepositories", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.HTML(200, TEAM_REPOSITORIES)
|
||||||
|
}
|
||||||
|
|
||||||
func EditTeam(ctx *middleware.Context) {
|
func EditTeam(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = ctx.Org.Organization.FullName
|
ctx.Data["Title"] = ctx.Org.Organization.FullName
|
||||||
ctx.Data["PageIsOrgTeams"] = true
|
ctx.Data["PageIsOrgTeams"] = true
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.4.7.0825 Alpha
|
0.4.7.0826 Alpha
|
|
@ -0,0 +1,45 @@
|
||||||
|
{{template "ng/base/head" .}}
|
||||||
|
{{template "ng/base/header" .}}
|
||||||
|
{{template "org/base/header" .}}
|
||||||
|
<div id="setting-wrapper" class="main-wrapper">
|
||||||
|
<div id="team-members-list" class="container clear">
|
||||||
|
{{template "ng/base/alert" .}}
|
||||||
|
{{template "org/team/sidebar" .}}
|
||||||
|
<div class="grid-2-3 left">
|
||||||
|
<div class="setting-content">
|
||||||
|
<div class="panel panel-radius">
|
||||||
|
<div class="panel-header">
|
||||||
|
{{.i18n.Tr "org.teams.repositories"}}
|
||||||
|
</div>
|
||||||
|
{{$canAddRemove := and $.IsOrganizationOwner (not (eq $.Team.LowerName "owners"))}}
|
||||||
|
<ul class="panel-body setting-list" id="team-repositories-list">
|
||||||
|
{{if $canAddRemove}}
|
||||||
|
<li class="search">
|
||||||
|
<form class="form form-align" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" id="repo-collab-form">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<input class="ipt ipt-large ipt-radius" id="org-team-repositories-add" name="repo-name" autocomplete="off" data-uid="{{.Org.Id}}" required />
|
||||||
|
<button class="btn btn-blue btn-large btn-radius">{{.i18n.Tr "org.teams.add_team_repository"}}</button>
|
||||||
|
<div class="repo-user-list-block">
|
||||||
|
<ul class="menu-down-show menu-vertical menu-radius switching-list user-list" id="org-team-repositories-list"></ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
{{range .Team.Repos}}
|
||||||
|
<li class="collab">
|
||||||
|
{{if $canAddRemove}}
|
||||||
|
<a class="btn btn-small btn-red btn-radius right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.Id}}">{{$.i18n.Tr "org.teams.remove_repo"}}</a>
|
||||||
|
{{end}}
|
||||||
|
<a class="member" href="/{{$.Org.Name}}/{{.Name}}">
|
||||||
|
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
|
||||||
|
<strong>{{$.Org.Name}}/{{.Name}}</strong>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "ng/base/footer" .}}
|
|
@ -11,8 +11,8 @@
|
||||||
<p class="desc">{{if .Team.Description}}{{.Team.Description}}{{else}}{{.i18n.Tr "org.teams.no_desc"}}{{end}}</p>
|
<p class="desc">{{if .Team.Description}}{{.Team.Description}}{{else}}{{.i18n.Tr "org.teams.no_desc"}}{{end}}</p>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="team-stats">
|
<div class="team-stats">
|
||||||
<a class="text-black" href="{{.OrgLink}}/teams/{{.Team.LowerName}}"><strong>{{.Team.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
|
<a class="text-black" href="{{.OrgLink}}/teams/{{.Team.LowerName}}"><span class="octicon octicon-person"></span> <strong>{{.Team.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
|
||||||
<a class="text-black" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/repositories"><strong>{{.Team.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
|
<a class="text-black" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/repositories"><span class="octicon octicon-repo"></span> <strong>{{.Team.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
{{if eq .Team.LowerName "owners"}}
|
{{if eq .Team.LowerName "owners"}}
|
||||||
|
|
Loading…
Reference in a new issue