Collaborator
This commit is contained in:
parent
0dfb5560cd
commit
f6c4fbeb37
10 changed files with 118 additions and 38 deletions
|
@ -36,6 +36,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o
|
||||||
- Register/delete/rename account.
|
- Register/delete/rename account.
|
||||||
- Create/migrate/mirror/delete/watch/rename/transfer public/private repository.
|
- Create/migrate/mirror/delete/watch/rename/transfer public/private repository.
|
||||||
- Repository viewer/release/issue tracker.
|
- Repository viewer/release/issue tracker.
|
||||||
|
- Add/remove repository collaborators.
|
||||||
- Gravatar and cache support.
|
- Gravatar and cache support.
|
||||||
- Mail service(register, issue).
|
- Mail service(register, issue).
|
||||||
- Administration panel.
|
- Administration panel.
|
||||||
|
|
|
@ -27,6 +27,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
|
||||||
- 注册/删除/重命名用户
|
- 注册/删除/重命名用户
|
||||||
- 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
|
- 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
|
||||||
- 仓库 浏览器/发布/缺陷追踪
|
- 仓库 浏览器/发布/缺陷追踪
|
||||||
|
- 添加/删除 仓库协作者
|
||||||
- Gravatar 以及缓存支持
|
- Gravatar 以及缓存支持
|
||||||
- 邮件服务(注册、Issue)
|
- 邮件服务(注册、Issue)
|
||||||
- 管理员面板
|
- 管理员面板
|
||||||
|
|
|
@ -42,6 +42,12 @@ func UpdateAccess(access *Access) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteAccess deletes access record.
|
||||||
|
func DeleteAccess(access *Access) error {
|
||||||
|
_, err := orm.Delete(access)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateAccess updates access information with session for rolling back.
|
// UpdateAccess updates access information with session for rolling back.
|
||||||
func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
|
func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
|
||||||
if _, err := sess.Id(access.Id).Update(access); err != nil {
|
if _, err := sess.Id(access.Id).Update(access); err != nil {
|
||||||
|
|
|
@ -712,6 +712,20 @@ func GetRepositoryCount(user *User) (int64, error) {
|
||||||
return orm.Count(&Repository{OwnerId: user.Id})
|
return orm.Count(&Repository{OwnerId: user.Id})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCollaborators returns a list of user name of repository's collaborators.
|
||||||
|
func GetCollaborators(repoName string) ([]string, error) {
|
||||||
|
accesses := make([]*Access, 0, 10)
|
||||||
|
if err := orm.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
names := make([]string, len(accesses))
|
||||||
|
for i := range accesses {
|
||||||
|
names[i] = accesses[i].UserName
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -398,6 +398,7 @@ html, body {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
border: 1px solid #CCC;
|
border: 1px solid #CCC;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#user-setting-nav > h4, #user-setting-container > h4, #user-setting-container > div > h4,
|
#user-setting-nav > h4, #user-setting-container > h4, #user-setting-container > div > h4,
|
||||||
|
|
|
@ -6,7 +6,10 @@ package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gogits/git"
|
"github.com/gogits/git"
|
||||||
|
|
||||||
"github.com/gogits/gogs/models"
|
"github.com/gogits/gogs/models"
|
||||||
"github.com/gogits/gogs/modules/base"
|
"github.com/gogits/gogs/modules/base"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"github.com/gogits/gogs/modules/log"
|
||||||
|
@ -20,13 +23,7 @@ func Setting(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["IsRepoToolbarSetting"] = true
|
ctx.Data["IsRepoToolbarSetting"] = true
|
||||||
|
ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - settings"
|
||||||
var title string
|
|
||||||
if t, ok := ctx.Data["Title"].(string); ok {
|
|
||||||
title = t
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Data["Title"] = title + " - settings"
|
|
||||||
ctx.HTML(200, "repo/setting")
|
ctx.HTML(200, "repo/setting")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,17 +125,82 @@ func SettingPost(ctx *middleware.Context) {
|
||||||
|
|
||||||
func Collaboration(ctx *middleware.Context) {
|
func Collaboration(ctx *middleware.Context) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner {
|
||||||
ctx.Handle(404, "repo.Setting", nil)
|
ctx.Error(404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["IsRepoToolbarSetting"] = true
|
repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/")
|
||||||
|
ctx.Data["IsRepoToolbarCollaboration"] = true
|
||||||
|
ctx.Data["Title"] = repoLink + " - collaboration"
|
||||||
|
|
||||||
var title string
|
// Delete collaborator.
|
||||||
if t, ok := ctx.Data["Title"].(string); ok {
|
remove := strings.ToLower(ctx.Query("remove"))
|
||||||
title = t
|
if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
|
||||||
|
if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil {
|
||||||
|
ctx.Handle(500, "repo.Collaboration(DeleteAccess)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Flash.Success("Collaborator has been removed.")
|
||||||
|
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Title"] = title + " - collaboration"
|
names, err := models.GetCollaborators(repoLink)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "repo.Collaboration(GetCollaborators)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
us := make([]*models.User, len(names))
|
||||||
|
for i, name := range names {
|
||||||
|
us[i], err = models.GetUserByName(name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "repo.Collaboration(GetUserByName)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Collaborators"] = us
|
||||||
ctx.HTML(200, "repo/collaboration")
|
ctx.HTML(200, "repo/collaboration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CollaborationPost(ctx *middleware.Context) {
|
||||||
|
if !ctx.Repo.IsOwner {
|
||||||
|
ctx.Error(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/")
|
||||||
|
name := strings.ToLower(ctx.Query("collaborator"))
|
||||||
|
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
|
||||||
|
ctx.Redirect(ctx.Req.RequestURI)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
has, err := models.HasAccess(name, repoLink, models.AU_WRITABLE)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "repo.CollaborationPost(HasAccess)", err)
|
||||||
|
return
|
||||||
|
} else if has {
|
||||||
|
ctx.Redirect(ctx.Req.RequestURI)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isExist, err := models.IsUserExist(name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "repo.CollaborationPost(IsUserExist)", err)
|
||||||
|
return
|
||||||
|
} else if !isExist {
|
||||||
|
ctx.Flash.Error("Given user does not exist.")
|
||||||
|
ctx.Redirect(ctx.Req.RequestURI)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.AddAccess(&models.Access{UserName: name, RepoName: repoLink,
|
||||||
|
Mode: models.AU_WRITABLE}); err != nil {
|
||||||
|
ctx.Handle(500, "repo.CollaborationPost(AddAccess)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Flash.Success("New collaborator has been added.")
|
||||||
|
ctx.Redirect(ctx.Req.RequestURI)
|
||||||
|
}
|
||||||
|
|
|
@ -3,14 +3,7 @@
|
||||||
{{template "repo/nav" .}}
|
{{template "repo/nav" .}}
|
||||||
{{template "repo/toolbar" .}}
|
{{template "repo/toolbar" .}}
|
||||||
<div id="body" class="container">
|
<div id="body" class="container">
|
||||||
<div id="user-setting-nav" class="col-md-2 repo-setting-nav">
|
{{template "repo/setting_nav" .}}
|
||||||
<ul class="list-group">
|
|
||||||
<li class="list-group-item"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/settings">Options</a></li>
|
|
||||||
<li class="list-group-item active"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/collaboration">Collaborators</a></li>
|
|
||||||
<!--<li class="list-group-item"><a href="#">Notifications</a></li>-->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="repo-setting-container" class="col-md-10">
|
<div id="repo-setting-container" class="col-md-10">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -19,23 +12,24 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<ul id="repo-collab-list" class="list-unstyled">
|
<ul id="repo-collab-list" class="list-unstyled">
|
||||||
<li>No Collaborators</li>
|
{{range .Collaborators}}
|
||||||
<li class="collab">
|
<li class="collab">
|
||||||
<a href="/{{.Owner.Name}}/{{.Repository.Name}}/remove_member?name=" class="remove-collab pull-right"><i class="fa fa-times"></i></a>
|
{{if not (eq .LowerName $.Owner.LowerName)}}<a href="{{$.RepoLink}}/settings/collaboration?remove={{.Name}}" class="remove-collab pull-right"><i class="fa fa-times"></i></a>{{end}}
|
||||||
<a class="member" href="#">
|
<a class="member" href="/user/{{.Name}}">
|
||||||
<img alt="无闻" class="pull-left avatar" src="https://avatars2.githubusercontent.com/u/2946214?s=140">
|
<img alt="{{.Name}}" class="pull-left avatar" src="{{.AvatarLink}}">
|
||||||
<strong class="access-member-fullname">无闻</strong><br/>
|
<strong class="access-member-fullname">{{.FullName}}</strong><br/>
|
||||||
Unknwon
|
{{.Name}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
<form action="/{{.Owner.Name}}/{{.Repository.Name}}/collaboration" method="post" class="form-horizontal" id="repo-collab-form">
|
<form action="{{.RepoLink}}/settings/collaboration" method="post" class="form-horizontal" id="repo-collab-form">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<div class="form-group" style="margin-bottom: 0">
|
<div class="form-group" style="margin-bottom: 0">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<input type="text" name="collaborator" class="form-control dropdown-toggle" id="repo-collaborator" required="required" data-toggle="dropdown"/>
|
<input type="text" name="collaborator" class="form-control dropdown-toggle" id="repo-collaborator" autocomplete="off" required="required" data-toggle="dropdown"/>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<ul class="list-unstyled"></ul>
|
<ul class="list-unstyled"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,14 +3,7 @@
|
||||||
{{template "repo/nav" .}}
|
{{template "repo/nav" .}}
|
||||||
{{template "repo/toolbar" .}}
|
{{template "repo/toolbar" .}}
|
||||||
<div id="body" class="container">
|
<div id="body" class="container">
|
||||||
<div id="user-setting-nav" class="col-md-2 repo-setting-nav">
|
{{template "repo/setting_nav" .}}
|
||||||
<ul class="list-group">
|
|
||||||
<li class="list-group-item active"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/settings">Options</a></li>
|
|
||||||
<li class="list-group-item"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/collaboration">Collaborators</a></li>
|
|
||||||
<!--<li class="list-group-item"><a href="#">Notifications</a></li>-->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="repo-setting-container" class="col-md-10">
|
<div id="repo-setting-container" class="col-md-10">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
|
7
templates/repo/setting_nav.tmpl
Normal file
7
templates/repo/setting_nav.tmpl
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div id="user-setting-nav" class="col-md-2 repo-setting-nav">
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item{{if .IsRepoToolbarSetting}} active{{end}}"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/settings">Options</a></li>
|
||||||
|
<li class="list-group-item{{if .IsRepoToolbarCollaboration}} active{{end}}"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/settings/collaboration">Collaborators</a></li>
|
||||||
|
<!--<li class="list-group-item"><a href="#">Notifications</a></li>-->
|
||||||
|
</ul>
|
||||||
|
</div>
|
3
web.go
3
web.go
|
@ -150,7 +150,8 @@ func runWeb(*cli.Context) {
|
||||||
m.Group("/:username/:reponame", func(r martini.Router) {
|
m.Group("/:username/:reponame", func(r martini.Router) {
|
||||||
r.Get("/settings", repo.Setting)
|
r.Get("/settings", repo.Setting)
|
||||||
r.Post("/settings", repo.SettingPost)
|
r.Post("/settings", repo.SettingPost)
|
||||||
r.Get("/collaboration", repo.Collaboration)
|
r.Get("/settings/collaboration", repo.Collaboration)
|
||||||
|
r.Post("/settings/collaboration", repo.CollaborationPost)
|
||||||
r.Get("/action/:action", repo.Action)
|
r.Get("/action/:action", repo.Action)
|
||||||
r.Get("/issues/new", repo.CreateIssue)
|
r.Get("/issues/new", repo.CreateIssue)
|
||||||
r.Post("/issues/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
|
r.Post("/issues/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
|
||||||
|
|
Reference in a new issue