Merge branch 'access' of github.com:gogits/gogs into dev
This commit is contained in:
commit
b0b11fd7b1
36 changed files with 1385 additions and 1320 deletions
|
@ -1,6 +1,6 @@
|
||||||
[run]
|
[run]
|
||||||
init_cmds = [
|
init_cmds = [
|
||||||
["grep", "-rn", "FIXME", "."],
|
#["grep", "-rn", "FIXME", "."],
|
||||||
["./gogs", "web"]
|
["./gogs", "web"]
|
||||||
]
|
]
|
||||||
watch_all = true
|
watch_all = true
|
||||||
|
|
|
@ -7,13 +7,13 @@ Gogs (Go Git Service) is a painless self-hosted Git service written in Go.
|
||||||
|
|
||||||
![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
|
![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
|
||||||
|
|
||||||
##### Current version: 0.5.13 Beta
|
##### Current version: 0.5.16 Beta
|
||||||
|
|
||||||
### NOTICES
|
### NOTICES
|
||||||
|
|
||||||
- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
||||||
- The demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
|
- The demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
|
||||||
- You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing a issue or making a Pull Request.
|
- You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing an issue or making a Pull Request.
|
||||||
- If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
|
- If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
|
||||||
|
|
||||||
#### Other language version
|
#### Other language version
|
||||||
|
@ -57,7 +57,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||||
## System Requirements
|
## System Requirements
|
||||||
|
|
||||||
- A cheap Raspberry Pi is powerful enough for basic functionality.
|
- A cheap Raspberry Pi is powerful enough for basic functionality.
|
||||||
- At least 4 CPU cores and 1GB RAM would be the baseline for teamwork.
|
- At least 2 CPU cores and 1GB RAM would be the baseline for teamwork.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
|
||||||
|
|
||||||
![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
|
![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
|
||||||
|
|
||||||
##### 当前版本:0.5.13 Beta
|
##### 当前版本:0.5.16 Beta
|
||||||
|
|
||||||
## 开发目的
|
## 开发目的
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||||
## 系统要求
|
## 系统要求
|
||||||
|
|
||||||
- 最低的系统硬件要求为一个廉价的树莓派
|
- 最低的系统硬件要求为一个廉价的树莓派
|
||||||
- 如果用于团队项目,建议使用 4 核 CPU 及 1GB 内存
|
- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存
|
||||||
|
|
||||||
## 安装部署
|
## 安装部署
|
||||||
|
|
||||||
|
|
59
cmd/serve.go
59
cmd/serve.go
|
@ -8,7 +8,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -43,7 +42,7 @@ func setup(logPath string) {
|
||||||
|
|
||||||
models.LoadModelsConfig()
|
models.LoadModelsConfig()
|
||||||
|
|
||||||
if models.UseSQLite3 {
|
if setting.UseSQLite3 {
|
||||||
workDir, _ := setting.WorkDir()
|
workDir, _ := setting.WorkDir()
|
||||||
os.Chdir(workDir)
|
os.Chdir(workDir)
|
||||||
}
|
}
|
||||||
|
@ -67,33 +66,33 @@ func parseCmd(cmd string) (string, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
COMMANDS_READONLY = map[string]models.AccessType{
|
COMMANDS_READONLY = map[string]models.AccessMode{
|
||||||
"git-upload-pack": models.WRITABLE,
|
"git-upload-pack": models.ACCESS_MODE_WRITE,
|
||||||
"git upload-pack": models.WRITABLE,
|
"git upload-pack": models.ACCESS_MODE_WRITE,
|
||||||
"git-upload-archive": models.WRITABLE,
|
"git-upload-archive": models.ACCESS_MODE_WRITE,
|
||||||
}
|
}
|
||||||
|
|
||||||
COMMANDS_WRITE = map[string]models.AccessType{
|
COMMANDS_WRITE = map[string]models.AccessMode{
|
||||||
"git-receive-pack": models.READABLE,
|
"git-receive-pack": models.ACCESS_MODE_READ,
|
||||||
"git receive-pack": models.READABLE,
|
"git receive-pack": models.ACCESS_MODE_READ,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func In(b string, sl map[string]models.AccessType) bool {
|
func In(b string, sl map[string]models.AccessMode) bool {
|
||||||
_, e := sl[b]
|
_, e := sl[b]
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServ(k *cli.Context) {
|
func runServ(c *cli.Context) {
|
||||||
if k.IsSet("config") {
|
if c.IsSet("config") {
|
||||||
setting.CustomConf = k.String("config")
|
setting.CustomConf = c.String("config")
|
||||||
}
|
}
|
||||||
setup("serv.log")
|
setup("serv.log")
|
||||||
|
|
||||||
if len(k.Args()) < 1 {
|
if len(c.Args()) < 1 {
|
||||||
log.GitLogger.Fatal(2, "Not enough arguments")
|
log.GitLogger.Fatal(2, "Not enough arguments")
|
||||||
}
|
}
|
||||||
keys := strings.Split(k.Args()[0], "-")
|
keys := strings.Split(c.Args()[0], "-")
|
||||||
if len(keys) != 2 {
|
if len(keys) != 2 {
|
||||||
println("Gogs: auth file format error")
|
println("Gogs: auth file format error")
|
||||||
log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2])
|
log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2])
|
||||||
|
@ -117,6 +116,7 @@ func runServ(k *cli.Context) {
|
||||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||||
if cmd == "" {
|
if cmd == "" {
|
||||||
println("Hi", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.")
|
println("Hi", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.")
|
||||||
|
println("If this is what you do not expect, please log in with password and setup Gogs under another user.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,17 +144,6 @@ func runServ(k *cli.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access check.
|
// Access check.
|
||||||
switch {
|
|
||||||
case isWrite:
|
|
||||||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE)
|
|
||||||
if err != nil {
|
|
||||||
println("Gogs: internal error:", err.Error())
|
|
||||||
log.GitLogger.Fatal(2, "Fail to check write access:", err)
|
|
||||||
} else if !has {
|
|
||||||
println("You have no right to write this repository")
|
|
||||||
log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath)
|
|
||||||
}
|
|
||||||
case isRead:
|
|
||||||
repo, err := models.GetRepositoryByName(repoUser.Id, repoName)
|
repo, err := models.GetRepositoryByName(repoUser.Id, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == models.ErrRepoNotExist {
|
if err == models.ErrRepoNotExist {
|
||||||
|
@ -165,11 +154,27 @@ func runServ(k *cli.Context) {
|
||||||
log.GitLogger.Fatal(2, "Fail to get repository: %v", err)
|
log.GitLogger.Fatal(2, "Fail to get repository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isWrite:
|
||||||
|
has, err := models.HasAccess(user, repo, models.ACCESS_MODE_WRITE)
|
||||||
|
if err != nil {
|
||||||
|
println("Gogs: internal error:", err.Error())
|
||||||
|
log.GitLogger.Fatal(2, "Fail to check write access:", err)
|
||||||
|
} else if !has {
|
||||||
|
println("You have no right to write this repository")
|
||||||
|
log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsMirror {
|
||||||
|
println("You can't write to a mirror repository")
|
||||||
|
log.GitLogger.Fatal(2, "User %s tried to write to a mirror repository %s", user.Name, repoPath)
|
||||||
|
}
|
||||||
|
case isRead:
|
||||||
if !repo.IsPrivate {
|
if !repo.IsPrivate {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE)
|
has, err := models.HasAccess(user, repo, models.ACCESS_MODE_READ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("Gogs: internal error:", err.Error())
|
println("Gogs: internal error:", err.Error())
|
||||||
log.GitLogger.Fatal(2, "Fail to check read access:", err)
|
log.GitLogger.Fatal(2, "Fail to check read access:", err)
|
||||||
|
|
|
@ -318,7 +318,7 @@ func runWeb(ctx *cli.Context) {
|
||||||
m.Get("/template/*", dev.TemplatePreview)
|
m.Get("/template/*", dev.TemplatePreview)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqTrueOwner := middleware.RequireTrueOwner()
|
reqAdmin := middleware.RequireAdmin()
|
||||||
|
|
||||||
// Organization.
|
// Organization.
|
||||||
m.Group("/org", func() {
|
m.Group("/org", func() {
|
||||||
|
@ -393,7 +393,7 @@ func runWeb(ctx *cli.Context) {
|
||||||
m.Post("/:name", repo.GitHooksEditPost)
|
m.Post("/:name", repo.GitHooksEditPost)
|
||||||
}, middleware.GitHookService())
|
}, middleware.GitHookService())
|
||||||
})
|
})
|
||||||
}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
|
}, reqSignIn, middleware.RepoAssignment(true), reqAdmin)
|
||||||
|
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
m.Get("/action/:action", repo.Action)
|
m.Get("/action/:action", repo.Action)
|
||||||
|
|
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.5.14.0222 Beta"
|
const APP_VER = "0.5.16.0228 Beta"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
216
models/access.go
216
models/access.go
|
@ -5,76 +5,190 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccessType int
|
type AccessMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
READABLE AccessType = iota + 1
|
ACCESS_MODE_NONE AccessMode = iota
|
||||||
WRITABLE
|
ACCESS_MODE_READ
|
||||||
|
ACCESS_MODE_WRITE
|
||||||
|
ACCESS_MODE_ADMIN
|
||||||
|
ACCESS_MODE_OWNER
|
||||||
)
|
)
|
||||||
|
|
||||||
// Access represents the accessibility of user to repository.
|
// Access represents the highest access level of a user to the repository. The only access type
|
||||||
|
// that is not in this table is the real owner of a repository. In case of an organization
|
||||||
|
// repository, the members of the owners team are in this table.
|
||||||
type Access struct {
|
type Access struct {
|
||||||
Id int64
|
ID int64 `xorm:"pk autoincr"`
|
||||||
UserName string `xorm:"UNIQUE(s)"`
|
UserID int64 `xorm:"UNIQUE(s)"`
|
||||||
RepoName string `xorm:"UNIQUE(s)"` // <user name>/<repo name>
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
Mode AccessType `xorm:"UNIQUE(s)"`
|
Mode AccessMode
|
||||||
Created time.Time `xorm:"CREATED"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAccess adds new access record.
|
func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
|
||||||
func AddAccess(access *Access) error {
|
mode := ACCESS_MODE_NONE
|
||||||
access.UserName = strings.ToLower(access.UserName)
|
if !repo.IsPrivate {
|
||||||
access.RepoName = strings.ToLower(access.RepoName)
|
mode = ACCESS_MODE_READ
|
||||||
_, err := x.Insert(access)
|
}
|
||||||
return err
|
|
||||||
|
if u != nil {
|
||||||
|
if u.Id == repo.OwnerId {
|
||||||
|
return ACCESS_MODE_OWNER, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &Access{UserID: u.Id, RepoID: repo.Id}
|
||||||
|
if has, err := e.Get(a); !has || err != nil {
|
||||||
|
return mode, err
|
||||||
|
}
|
||||||
|
return a.Mode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAccess updates access information.
|
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
|
||||||
func UpdateAccess(access *Access) error {
|
// user does not have access. User can be nil!
|
||||||
access.UserName = strings.ToLower(access.UserName)
|
func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
|
||||||
access.RepoName = strings.ToLower(access.RepoName)
|
return accessLevel(x, u, repo)
|
||||||
_, err := x.Id(access.Id).Update(access)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAccess deletes access record.
|
func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||||
func DeleteAccess(access *Access) error {
|
mode, err := accessLevel(e, u, repo)
|
||||||
_, err := x.Delete(access)
|
return testMode <= mode, err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAccess updates access information with session for rolling back.
|
// HasAccess returns true if someone has the request access level. User can be nil!
|
||||||
func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
|
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||||
if _, err := sess.Id(access.Id).Update(access); err != nil {
|
return hasAccess(x, u, repo, testMode)
|
||||||
sess.Rollback()
|
}
|
||||||
return err
|
|
||||||
|
// GetAccessibleRepositories finds all repositories where a user has access to,
|
||||||
|
// besides his own.
|
||||||
|
func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) {
|
||||||
|
accesses := make([]*Access, 0, 10)
|
||||||
|
if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repos := make(map[*Repository]AccessMode, len(accesses))
|
||||||
|
for _, access := range accesses {
|
||||||
|
repo, err := GetRepositoryById(access.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = repo.GetOwner(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if repo.OwnerId == u.Id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repos[repo] = access.Mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: should we generate an ordered list here? Random looks weird.
|
||||||
|
return repos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxAccessMode(modes ...AccessMode) AccessMode {
|
||||||
|
max := ACCESS_MODE_NONE
|
||||||
|
for _, mode := range modes {
|
||||||
|
if mode > max {
|
||||||
|
max = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
|
||||||
|
func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
|
||||||
|
minMode := ACCESS_MODE_READ
|
||||||
|
if !repo.IsPrivate {
|
||||||
|
minMode = ACCESS_MODE_WRITE
|
||||||
|
}
|
||||||
|
|
||||||
|
newAccesses := make([]Access, 0, len(accessMap))
|
||||||
|
for userID, mode := range accessMap {
|
||||||
|
if mode < minMode {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newAccesses = append(newAccesses, Access{
|
||||||
|
UserID: userID,
|
||||||
|
RepoID: repo.Id,
|
||||||
|
Mode: mode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old accesses and insert new ones for repository.
|
||||||
|
if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil {
|
||||||
|
return fmt.Errorf("delete old accesses: %v", err)
|
||||||
|
} else if _, err = e.Insert(newAccesses); err != nil {
|
||||||
|
return fmt.Errorf("insert new accesses: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAccess returns true if someone can read or write to given repository.
|
// FIXME: should be able to have read-only access.
|
||||||
// The repoName should be in format <username>/<reponame>.
|
// Give all collaborators write access.
|
||||||
func HasAccess(uname, repoName string, mode AccessType) (bool, error) {
|
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
|
||||||
if len(repoName) == 0 {
|
collaborators, err := repo.getCollaborators(e)
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
access := &Access{
|
|
||||||
UserName: strings.ToLower(uname),
|
|
||||||
RepoName: strings.ToLower(repoName),
|
|
||||||
}
|
|
||||||
has, err := x.Get(access)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return fmt.Errorf("getCollaborators: %v", err)
|
||||||
} else if !has {
|
|
||||||
return false, nil
|
|
||||||
} else if mode > access.Mode {
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
return true, nil
|
for _, c := range collaborators {
|
||||||
|
accessMap[c.Id] = ACCESS_MODE_WRITE
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// recalculateTeamAccesses recalculates new accesses for teams of an organization
|
||||||
|
// except the team whose ID is given. It is used to assign a team ID when
|
||||||
|
// remove repository from that team.
|
||||||
|
func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) {
|
||||||
|
accessMap := make(map[int64]AccessMode, 20)
|
||||||
|
|
||||||
|
if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
|
||||||
|
return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repo.getOwner(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if repo.Owner.IsOrganization() {
|
||||||
|
if err = repo.Owner.getTeams(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range repo.Owner.Teams {
|
||||||
|
if t.ID == ignTeamID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if t.IsOwnerTeam() {
|
||||||
|
t.Authorize = ACCESS_MODE_OWNER
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = t.getMembers(e); err != nil {
|
||||||
|
return fmt.Errorf("getMembers '%d': %v", t.ID, err)
|
||||||
|
}
|
||||||
|
for _, m := range t.Members {
|
||||||
|
accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.refreshAccesses(e, accessMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) recalculateAccesses(e Engine) error {
|
||||||
|
accessMap := make(map[int64]AccessMode, 20)
|
||||||
|
if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
|
||||||
|
return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
|
||||||
|
}
|
||||||
|
return repo.refreshAccesses(e, accessMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecalculateAccesses recalculates all accesses for repository.
|
||||||
|
func (r *Repository) RecalculateAccesses() error {
|
||||||
|
return r.recalculateAccesses(x)
|
||||||
}
|
}
|
||||||
|
|
|
@ -434,46 +434,58 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRepoAction adds new action for creating repository.
|
func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
|
||||||
func NewRepoAction(u *User, repo *Repository) (err error) {
|
if err = notifyWatchers(e, &Action{
|
||||||
if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
|
ActUserId: u.Id,
|
||||||
OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name,
|
ActUserName: u.Name,
|
||||||
|
ActEmail: u.Email,
|
||||||
|
OpType: CREATE_REPO,
|
||||||
|
RepoId: repo.Id,
|
||||||
|
RepoUserName: repo.Owner.Name,
|
||||||
|
RepoName: repo.Name,
|
||||||
IsPrivate: repo.IsPrivate}); err != nil {
|
IsPrivate: repo.IsPrivate}); err != nil {
|
||||||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
|
return fmt.Errorf("notify watchers '%d/%s'", u.Id, repo.Id)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name)
|
log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransferRepoAction adds new action for transferring repository.
|
// NewRepoAction adds new action for creating repository.
|
||||||
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
|
func NewRepoAction(u *User, repo *Repository) (err error) {
|
||||||
|
return newRepoAction(x, u, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
|
||||||
action := &Action{
|
action := &Action{
|
||||||
ActUserId: u.Id,
|
ActUserId: actUser.Id,
|
||||||
ActUserName: u.Name,
|
ActUserName: actUser.Name,
|
||||||
ActEmail: u.Email,
|
ActEmail: actUser.Email,
|
||||||
OpType: TRANSFER_REPO,
|
OpType: TRANSFER_REPO,
|
||||||
RepoId: repo.Id,
|
RepoId: repo.Id,
|
||||||
RepoUserName: newUser.Name,
|
RepoUserName: newOwner.Name,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
IsPrivate: repo.IsPrivate,
|
IsPrivate: repo.IsPrivate,
|
||||||
Content: path.Join(repo.Owner.LowerName, repo.LowerName),
|
Content: path.Join(oldOwner.LowerName, repo.LowerName),
|
||||||
}
|
}
|
||||||
if err = NotifyWatchers(action); err != nil {
|
if err = notifyWatchers(e, action); err != nil {
|
||||||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
|
return fmt.Errorf("notify watchers '%d/%s'", actUser.Id, repo.Id)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove watch for organization.
|
// Remove watch for organization.
|
||||||
if repo.Owner.IsOrganization() {
|
if repo.Owner.IsOrganization() {
|
||||||
if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil {
|
if err = watchRepo(e, repo.Owner.Id, repo.Id, false); err != nil {
|
||||||
log.Error(4, "WatchRepo", err)
|
return fmt.Errorf("watch repository: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
|
log.Trace("action.TransferRepoAction: %s/%s", actUser.Name, repo.Name)
|
||||||
return err
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferRepoAction adds new action for transferring repository.
|
||||||
|
func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
|
||||||
|
return transferRepoAction(x, actUser, oldOwner, newOwner, repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeeds returns action list of given user in given context.
|
// GetFeeds returns action list of given user in given context.
|
||||||
|
|
|
@ -282,30 +282,33 @@ type IssueUser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssueUserPairs adds new issue-user pairs for new issue of repository.
|
// NewIssueUserPairs adds new issue-user pairs for new issue of repository.
|
||||||
func NewIssueUserPairs(rid, iid, oid, pid, aid int64, repoName string) (err error) {
|
func NewIssueUserPairs(repo *Repository, issueID, orgID, posterID, assigneeID int64) (err error) {
|
||||||
iu := &IssueUser{IssueId: iid, RepoId: rid}
|
users, err := repo.GetCollaborators()
|
||||||
|
|
||||||
us, err := GetCollaborators(repoName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iu := &IssueUser{
|
||||||
|
IssueId: issueID,
|
||||||
|
RepoId: repo.Id,
|
||||||
|
}
|
||||||
|
|
||||||
isNeedAddPoster := true
|
isNeedAddPoster := true
|
||||||
for _, u := range us {
|
for _, u := range users {
|
||||||
iu.Uid = u.Id
|
iu.Uid = u.Id
|
||||||
iu.IsPoster = iu.Uid == pid
|
iu.IsPoster = iu.Uid == posterID
|
||||||
if isNeedAddPoster && iu.IsPoster {
|
if isNeedAddPoster && iu.IsPoster {
|
||||||
isNeedAddPoster = false
|
isNeedAddPoster = false
|
||||||
}
|
}
|
||||||
iu.IsAssigned = iu.Uid == aid
|
iu.IsAssigned = iu.Uid == assigneeID
|
||||||
if _, err = x.Insert(iu); err != nil {
|
if _, err = x.Insert(iu); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isNeedAddPoster {
|
if isNeedAddPoster {
|
||||||
iu.Uid = pid
|
iu.Uid = posterID
|
||||||
iu.IsPoster = true
|
iu.IsPoster = true
|
||||||
iu.IsAssigned = iu.Uid == aid
|
iu.IsAssigned = iu.Uid == assigneeID
|
||||||
if _, err = x.Insert(iu); err != nil {
|
if _, err = x.Insert(iu); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,44 @@
|
||||||
|
// Copyright 2015 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 migrations
|
package migrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/log"
|
||||||
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
type migration func(*xorm.Engine) error
|
const _MIN_DB_VER = 0
|
||||||
|
|
||||||
|
type Migration interface {
|
||||||
|
Description() string
|
||||||
|
Migrate(*xorm.Engine) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type migration struct {
|
||||||
|
description string
|
||||||
|
migrate func(*xorm.Engine) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMigration(desc string, fn func(*xorm.Engine) error) Migration {
|
||||||
|
return &migration{desc, fn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *migration) Description() string {
|
||||||
|
return m.description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *migration) Migrate(x *xorm.Engine) error {
|
||||||
|
return m.migrate(x)
|
||||||
|
}
|
||||||
|
|
||||||
// The version table. Should have only one row with id==1
|
// The version table. Should have only one row with id==1
|
||||||
type Version struct {
|
type Version struct {
|
||||||
|
@ -15,30 +47,55 @@ type Version struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
||||||
// If you want to "retire" a migration, replace it with "expiredMigration"
|
// If you want to "retire" a migration, remove it from the top of the list and
|
||||||
var migrations = []migration{}
|
// update _MIN_VER_DB accordingly
|
||||||
|
var migrations = []Migration{
|
||||||
|
NewMigration("generate collaboration from access", accessToCollaboration), // V0 -> V1
|
||||||
|
NewMigration("make authorize 4 if team is owners", ownerTeamUpdate), // V1 -> V2
|
||||||
|
NewMigration("refactor access table to use id's", accessRefactor), // V2 -> V3
|
||||||
|
NewMigration("generate team-repo from team", teamToTeamRepo), // V3 -> V4
|
||||||
|
}
|
||||||
|
|
||||||
// Migrate database to current version
|
// Migrate database to current version
|
||||||
func Migrate(x *xorm.Engine) error {
|
func Migrate(x *xorm.Engine) error {
|
||||||
if err := x.Sync(new(Version)); err != nil {
|
if err := x.Sync(new(Version)); err != nil {
|
||||||
return err
|
return fmt.Errorf("sync: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentVersion := &Version{Id: 1}
|
currentVersion := &Version{Id: 1}
|
||||||
has, err := x.Get(currentVersion)
|
has, err := x.Get(currentVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("get: %v", err)
|
||||||
} else if !has {
|
} else if !has {
|
||||||
if _, err = x.InsertOne(currentVersion); err != nil {
|
// If the user table does not exist it is a fresh installation and we
|
||||||
|
// can skip all migrations.
|
||||||
|
needsMigration, err := x.IsTableExist("user")
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if needsMigration {
|
||||||
|
isEmpty, err := x.IsTableEmpty("user")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If the user table is empty it is a fresh installation and we can
|
||||||
|
// skip all migrations.
|
||||||
|
needsMigration = !isEmpty
|
||||||
|
}
|
||||||
|
if !needsMigration {
|
||||||
|
currentVersion.Version = int64(_MIN_DB_VER + len(migrations))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = x.InsertOne(currentVersion); err != nil {
|
||||||
|
return fmt.Errorf("insert: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v := currentVersion.Version
|
v := currentVersion.Version
|
||||||
|
for i, m := range migrations[v-_MIN_DB_VER:] {
|
||||||
for i, migration := range migrations[v:] {
|
log.Info("Migration: %s", m.Description())
|
||||||
if err = migration(x); err != nil {
|
if err = m.Migrate(x); err != nil {
|
||||||
return err
|
return fmt.Errorf("do migrate: %v", err)
|
||||||
}
|
}
|
||||||
currentVersion.Version = v + int64(i) + 1
|
currentVersion.Version = v + int64(i) + 1
|
||||||
if _, err = x.Id(1).Update(currentVersion); err != nil {
|
if _, err = x.Id(1).Update(currentVersion); err != nil {
|
||||||
|
@ -48,6 +105,267 @@ func Migrate(x *xorm.Engine) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func expiredMigration(x *xorm.Engine) error {
|
func sessionRelease(sess *xorm.Session) {
|
||||||
return errors.New("You are migrating from a too old gogs version")
|
if !sess.IsCommitedOrRollbacked {
|
||||||
|
sess.Rollback()
|
||||||
|
}
|
||||||
|
sess.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessToCollaboration(x *xorm.Engine) (err error) {
|
||||||
|
type Collaboration struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||||
|
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||||
|
Created time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = x.Sync(new(Collaboration)); err != nil {
|
||||||
|
return fmt.Errorf("sync: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := x.Query("SELECT u.id AS `uid`, a.repo_name AS `repo`, a.mode AS `mode`, a.created as `created` FROM `access` a JOIN `user` u ON a.user_name=u.lower_name")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := strings.Split(time.Now().String(), " ")[2]
|
||||||
|
for _, result := range results {
|
||||||
|
mode := com.StrTo(result["mode"]).MustInt64()
|
||||||
|
// Collaborators must have write access.
|
||||||
|
if mode < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := com.StrTo(result["uid"]).MustInt64()
|
||||||
|
repoRefName := string(result["repo"])
|
||||||
|
|
||||||
|
var created time.Time
|
||||||
|
switch {
|
||||||
|
case setting.UseSQLite3:
|
||||||
|
created, _ = time.Parse(time.RFC3339, string(result["created"]))
|
||||||
|
case setting.UseMySQL:
|
||||||
|
created, _ = time.Parse("2006-01-02 15:04:05-0700", string(result["created"])+offset)
|
||||||
|
case setting.UsePostgreSQL:
|
||||||
|
created, _ = time.Parse("2006-01-02T15:04:05Z-0700", string(result["created"])+offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find owner of repository
|
||||||
|
parts := strings.SplitN(repoRefName, "/", 2)
|
||||||
|
ownerName := parts[0]
|
||||||
|
repoName := parts[1]
|
||||||
|
|
||||||
|
results, err := sess.Query("SELECT u.id as `uid`, ou.uid as `memberid` FROM `user` u LEFT JOIN org_user ou ON ou.org_id=u.id WHERE u.lower_name=?", ownerName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(results) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ownerID := com.StrTo(results[0]["uid"]).MustInt64()
|
||||||
|
if ownerID == userID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if user is member of owning organization
|
||||||
|
isMember := false
|
||||||
|
for _, member := range results {
|
||||||
|
memberID := com.StrTo(member["memberid"]).MustInt64()
|
||||||
|
// We can skip all cases that a user is member of the owning organization
|
||||||
|
if memberID == userID {
|
||||||
|
isMember = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isMember {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err = sess.Query("SELECT id FROM `repository` WHERE owner_id=? AND lower_name=?", ownerID, repoName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(results) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
collaboration := &Collaboration{
|
||||||
|
UserID: userID,
|
||||||
|
RepoID: com.StrTo(results[0]["id"]).MustInt64(),
|
||||||
|
}
|
||||||
|
has, err := sess.Get(collaboration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if has {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
collaboration.Created = created
|
||||||
|
if _, err = sess.InsertOne(collaboration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ownerTeamUpdate(x *xorm.Engine) (err error) {
|
||||||
|
if _, err := x.Exec("UPDATE `team` SET authorize=4 WHERE lower_name=?", "owners"); err != nil {
|
||||||
|
return fmt.Errorf("update owner team table: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessRefactor(x *xorm.Engine) (err error) {
|
||||||
|
type (
|
||||||
|
AccessMode int
|
||||||
|
Access struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
UserID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
Mode AccessMode
|
||||||
|
}
|
||||||
|
UserRepo struct {
|
||||||
|
UserID int64
|
||||||
|
RepoID int64
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// We consiously don't start a session yet as we make only reads for now, no writes
|
||||||
|
|
||||||
|
accessMap := make(map[UserRepo]AccessMode, 50)
|
||||||
|
|
||||||
|
results, err := x.Query("SELECT r.id AS `repo_id`, r.is_private AS `is_private`, r.owner_id AS `owner_id`, u.type AS `owner_type` FROM `repository` r LEFT JOIN `user` u ON r.owner_id=u.id")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("select repositories: %v", err)
|
||||||
|
}
|
||||||
|
for _, repo := range results {
|
||||||
|
repoID := com.StrTo(repo["repo_id"]).MustInt64()
|
||||||
|
isPrivate := com.StrTo(repo["is_private"]).MustInt() > 0
|
||||||
|
ownerID := com.StrTo(repo["owner_id"]).MustInt64()
|
||||||
|
ownerIsOrganization := com.StrTo(repo["owner_type"]).MustInt() > 0
|
||||||
|
|
||||||
|
results, err := x.Query("SELECT `user_id` FROM `collaboration` WHERE repo_id=?", repoID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("select collaborators: %v", err)
|
||||||
|
}
|
||||||
|
for _, user := range results {
|
||||||
|
userID := com.StrTo(user["user_id"]).MustInt64()
|
||||||
|
accessMap[UserRepo{userID, repoID}] = 2 // WRITE ACCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ownerIsOrganization {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The minimum level to add a new access record,
|
||||||
|
// because public repository has implicit open access.
|
||||||
|
minAccessLevel := AccessMode(0)
|
||||||
|
if !isPrivate {
|
||||||
|
minAccessLevel = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
repoString := "$" + string(repo["repo_id"]) + "|"
|
||||||
|
|
||||||
|
results, err = x.Query("SELECT `id`,`authorize`,`repo_ids` FROM `team` WHERE org_id=? AND authorize>? ORDER BY `authorize` ASC", ownerID, int(minAccessLevel))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("select teams from org: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, team := range results {
|
||||||
|
if !strings.Contains(string(team["repo_ids"]), repoString) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
teamID := com.StrTo(team["id"]).MustInt64()
|
||||||
|
mode := AccessMode(com.StrTo(team["authorize"]).MustInt())
|
||||||
|
|
||||||
|
results, err := x.Query("SELECT `uid` FROM `team_user` WHERE team_id=?", teamID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("select users from team: %v", err)
|
||||||
|
}
|
||||||
|
for _, user := range results {
|
||||||
|
userID := com.StrTo(user["user_id"]).MustInt64()
|
||||||
|
accessMap[UserRepo{userID, repoID}] = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop table can't be in a session (at least not in sqlite)
|
||||||
|
if _, err = x.Exec("DROP TABLE `access`"); err != nil {
|
||||||
|
return fmt.Errorf("drop access table: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we start writing so we make a session
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = sess.Sync2(new(Access)); err != nil {
|
||||||
|
return fmt.Errorf("sync: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
accesses := make([]*Access, 0, len(accessMap))
|
||||||
|
for ur, mode := range accessMap {
|
||||||
|
accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = sess.Insert(accesses); err != nil {
|
||||||
|
return fmt.Errorf("insert accesses: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func teamToTeamRepo(x *xorm.Engine) error {
|
||||||
|
type TeamRepo struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
OrgID int64 `xorm:"INDEX"`
|
||||||
|
TeamID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
teamRepos := make([]*TeamRepo, 0, 50)
|
||||||
|
|
||||||
|
results, err := x.Query("SELECT `id`,`org_id`,`repo_ids` FROM `team`")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("select teams: %v", err)
|
||||||
|
}
|
||||||
|
for _, team := range results {
|
||||||
|
orgID := com.StrTo(team["org_id"]).MustInt64()
|
||||||
|
teamID := com.StrTo(team["id"]).MustInt64()
|
||||||
|
|
||||||
|
for _, idStr := range strings.Split(string(team["repo_ids"]), "|") {
|
||||||
|
repoID := com.StrTo(strings.TrimPrefix(idStr, "$")).MustInt64()
|
||||||
|
if repoID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
teamRepos = append(teamRepos, &TeamRepo{
|
||||||
|
OrgID: orgID,
|
||||||
|
TeamID: teamID,
|
||||||
|
RepoID: repoID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = sess.Sync2(new(TeamRepo)); err != nil {
|
||||||
|
return fmt.Errorf("sync: %v", err)
|
||||||
|
} else if _, err = sess.Insert(teamRepos); err != nil {
|
||||||
|
return fmt.Errorf("insert team-repos: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/go-xorm/core"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
// "github.com/gogits/gogs/models/migrations"
|
"github.com/gogits/gogs/models/migrations"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,12 +24,22 @@ import (
|
||||||
type Engine interface {
|
type Engine interface {
|
||||||
Delete(interface{}) (int64, error)
|
Delete(interface{}) (int64, error)
|
||||||
Exec(string, ...interface{}) (sql.Result, error)
|
Exec(string, ...interface{}) (sql.Result, error)
|
||||||
|
Find(interface{}, ...interface{}) error
|
||||||
Get(interface{}) (bool, error)
|
Get(interface{}) (bool, error)
|
||||||
Insert(...interface{}) (int64, error)
|
Insert(...interface{}) (int64, error)
|
||||||
|
InsertOne(interface{}) (int64, error)
|
||||||
Id(interface{}) *xorm.Session
|
Id(interface{}) *xorm.Session
|
||||||
|
Sql(string, ...interface{}) *xorm.Session
|
||||||
Where(string, ...interface{}) *xorm.Session
|
Where(string, ...interface{}) *xorm.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sessionRelease(sess *xorm.Session) {
|
||||||
|
if !sess.IsCommitedOrRollbacked {
|
||||||
|
sess.Rollback()
|
||||||
|
}
|
||||||
|
sess.Close()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
x *xorm.Engine
|
||||||
tables []interface{}
|
tables []interface{}
|
||||||
|
@ -39,24 +50,30 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
EnableSQLite3 bool
|
EnableSQLite3 bool
|
||||||
UseSQLite3 bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tables = append(tables,
|
tables = append(tables,
|
||||||
new(User), new(PublicKey), new(Follow), new(Oauth2), new(AccessToken),
|
new(User), new(PublicKey), new(Oauth2), new(AccessToken),
|
||||||
new(Repository), new(Watch), new(Star), new(Action), new(Access),
|
new(Repository), new(Collaboration), new(Access),
|
||||||
|
new(Watch), new(Star), new(Follow), new(Action),
|
||||||
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
|
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
|
||||||
new(Mirror), new(Release), new(LoginSource), new(Webhook),
|
new(Mirror), new(Release), new(LoginSource), new(Webhook),
|
||||||
new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
|
new(UpdateTask), new(HookTask),
|
||||||
|
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
||||||
new(Notice), new(EmailAddress))
|
new(Notice), new(EmailAddress))
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadModelsConfig() {
|
func LoadModelsConfig() {
|
||||||
sec := setting.Cfg.Section("database")
|
sec := setting.Cfg.Section("database")
|
||||||
DbCfg.Type = sec.Key("DB_TYPE").String()
|
DbCfg.Type = sec.Key("DB_TYPE").String()
|
||||||
if DbCfg.Type == "sqlite3" {
|
switch DbCfg.Type {
|
||||||
UseSQLite3 = true
|
case "sqlite3":
|
||||||
|
setting.UseSQLite3 = true
|
||||||
|
case "mysql":
|
||||||
|
setting.UseMySQL = true
|
||||||
|
case "postgres":
|
||||||
|
setting.UsePostgreSQL = true
|
||||||
}
|
}
|
||||||
DbCfg.Host = sec.Key("HOST").String()
|
DbCfg.Host = sec.Key("HOST").String()
|
||||||
DbCfg.Name = sec.Key("NAME").String()
|
DbCfg.Name = sec.Key("NAME").String()
|
||||||
|
@ -103,6 +120,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
|
||||||
return fmt.Errorf("connect to database: %v", err)
|
return fmt.Errorf("connect to database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x.SetMapper(core.GonicMapper{})
|
||||||
return x.Sync(tables...)
|
return x.Sync(tables...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +130,8 @@ func SetEngine() (err error) {
|
||||||
return fmt.Errorf("connect to database: %v", err)
|
return fmt.Errorf("connect to database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x.SetMapper(core.GonicMapper{})
|
||||||
|
|
||||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||||
// so use log file to instead print to stdout.
|
// so use log file to instead print to stdout.
|
||||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
||||||
|
@ -136,13 +156,14 @@ func NewEngine() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if err = migrations.Migrate(x); err != nil {
|
if err = migrations.Migrate(x); err != nil {
|
||||||
// return err
|
return fmt.Errorf("migrate: %v", err)
|
||||||
// }
|
}
|
||||||
|
|
||||||
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
|
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
|
||||||
return fmt.Errorf("sync database struct error: %v\n", err)
|
return fmt.Errorf("sync database struct error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
641
models/org.go
641
models/org.go
File diff suppressed because it is too large
Load diff
752
models/repo.go
752
models/repo.go
File diff suppressed because it is too large
Load diff
|
@ -231,7 +231,7 @@ func (u *User) GetOrganizations() error {
|
||||||
|
|
||||||
u.Orgs = make([]*User, len(ous))
|
u.Orgs = make([]*User, len(ous))
|
||||||
for i, ou := range ous {
|
for i, ou := range ous {
|
||||||
u.Orgs[i], err = GetUserById(ou.OrgId)
|
u.Orgs[i], err = GetUserById(ou.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -398,63 +398,7 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
||||||
return ErrUserNameIllegal
|
return ErrUserNameIllegal
|
||||||
}
|
}
|
||||||
|
|
||||||
newUserName = strings.ToLower(newUserName)
|
return os.Rename(UserPath(u.LowerName), UserPath(newUserName))
|
||||||
if u.LowerName == newUserName {
|
|
||||||
// User only change letter cases.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update accesses of user.
|
|
||||||
accesses := make([]Access, 0, 10)
|
|
||||||
if err = x.Find(&accesses, &Access{UserName: u.LowerName}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range accesses {
|
|
||||||
accesses[i].UserName = newUserName
|
|
||||||
if strings.HasPrefix(accesses[i].RepoName, u.LowerName+"/") {
|
|
||||||
accesses[i].RepoName = strings.Replace(accesses[i].RepoName, u.LowerName, newUserName, 1)
|
|
||||||
}
|
|
||||||
if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repos, err := GetRepositories(u.Id, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := range repos {
|
|
||||||
accesses = make([]Access, 0, 10)
|
|
||||||
// Update accesses of user repository.
|
|
||||||
if err = x.Find(&accesses, &Access{RepoName: u.LowerName + "/" + repos[i].LowerName}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := range accesses {
|
|
||||||
// if the access is not the user's access (already updated above)
|
|
||||||
if accesses[j].UserName != u.LowerName {
|
|
||||||
accesses[j].RepoName = newUserName + "/" + repos[i].LowerName
|
|
||||||
if err = UpdateAccessWithSession(sess, &accesses[j]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change user directory name.
|
|
||||||
if err = os.Rename(UserPath(u.LowerName), UserPath(newUserName)); err != nil {
|
|
||||||
sess.Rollback()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser updates user's information.
|
// UpdateUser updates user's information.
|
||||||
|
@ -527,7 +471,7 @@ func DeleteUser(u *User) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Delete all accesses.
|
// Delete all accesses.
|
||||||
if _, err = x.Delete(&Access{UserName: u.LowerName}); err != nil {
|
if _, err = x.Delete(&Access{UserID: u.Id}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Delete all alternative email addresses
|
// Delete all alternative email addresses
|
||||||
|
@ -570,8 +514,7 @@ func UserPath(userName string) string {
|
||||||
|
|
||||||
func GetUserByKeyId(keyId int64) (*User, error) {
|
func GetUserByKeyId(keyId int64) (*User, error) {
|
||||||
user := new(User)
|
user := new(User)
|
||||||
rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?"
|
has, err := x.Sql("SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?", keyId).Get(user)
|
||||||
has, err := x.Sql(rawSql, keyId).Get(user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
|
@ -580,10 +523,9 @@ func GetUserByKeyId(keyId int64) (*User, error) {
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserById returns the user object by given ID if exists.
|
func getUserById(e Engine, id int64) (*User, error) {
|
||||||
func GetUserById(id int64) (*User, error) {
|
|
||||||
u := new(User)
|
u := new(User)
|
||||||
has, err := x.Id(id).Get(u)
|
has, err := e.Id(id).Get(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
|
@ -592,6 +534,11 @@ func GetUserById(id int64) (*User, error) {
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserById returns the user object by given ID if exists.
|
||||||
|
func GetUserById(id int64) (*User, error) {
|
||||||
|
return getUserById(x, id)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserByName returns user by given name.
|
// GetUserByName returns user by given name.
|
||||||
func GetUserByName(name string) (*User, error) {
|
func GetUserByName(name string) (*User, error) {
|
||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
|
@ -913,7 +860,7 @@ func UpdateMentions(userNames []string, issueId int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, orgUser := range orgUsers {
|
for _, orgUser := range orgUsers {
|
||||||
tempIds = append(tempIds, orgUser.Id)
|
tempIds = append(tempIds, orgUser.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
ids = append(ids, tempIds...)
|
ids = append(ids, tempIds...)
|
||||||
|
|
|
@ -38,15 +38,25 @@ type Context struct {
|
||||||
IsSigned bool
|
IsSigned bool
|
||||||
IsBasicAuth bool
|
IsBasicAuth bool
|
||||||
|
|
||||||
Repo struct {
|
Repo RepoContext
|
||||||
|
|
||||||
|
Org struct {
|
||||||
IsOwner bool
|
IsOwner bool
|
||||||
IsTrueOwner bool
|
IsMember bool
|
||||||
|
IsAdminTeam bool // In owner team or team that has admin permission level.
|
||||||
|
Organization *models.User
|
||||||
|
OrgLink string
|
||||||
|
|
||||||
|
Team *models.Team
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepoContext struct {
|
||||||
|
AccessMode models.AccessMode
|
||||||
IsWatching bool
|
IsWatching bool
|
||||||
IsBranch bool
|
IsBranch bool
|
||||||
IsTag bool
|
IsTag bool
|
||||||
IsCommit bool
|
IsCommit bool
|
||||||
IsAdmin bool // Current user is admin level.
|
|
||||||
HasAccess bool
|
|
||||||
Repository *models.Repository
|
Repository *models.Repository
|
||||||
Owner *models.User
|
Owner *models.User
|
||||||
Commit *git.Commit
|
Commit *git.Commit
|
||||||
|
@ -60,17 +70,16 @@ type Context struct {
|
||||||
CloneLink models.CloneLink
|
CloneLink models.CloneLink
|
||||||
CommitsCount int
|
CommitsCount int
|
||||||
Mirror *models.Mirror
|
Mirror *models.Mirror
|
||||||
}
|
}
|
||||||
|
|
||||||
Org struct {
|
// Return if the current user has write access for this repository
|
||||||
IsOwner bool
|
func (r RepoContext) IsOwner() bool {
|
||||||
IsMember bool
|
return r.AccessMode >= models.ACCESS_MODE_WRITE
|
||||||
IsAdminTeam bool // In owner team or team that has admin permission level.
|
}
|
||||||
Organization *models.User
|
|
||||||
OrgLink string
|
|
||||||
|
|
||||||
Team *models.Team
|
// Return if the current user has read access for this repository
|
||||||
}
|
func (r RepoContext) HasAccess() bool {
|
||||||
|
return r.AccessMode >= models.ACCESS_MODE_READ
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasError returns true if error occurs in form validation.
|
// HasError returns true if error occurs in form validation.
|
||||||
|
|
|
@ -87,7 +87,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["Team"] = ctx.Org.Team
|
ctx.Data["Team"] = ctx.Org.Team
|
||||||
ctx.Org.IsAdminTeam = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize == models.ORG_ADMIN
|
ctx.Org.IsAdminTeam = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize >= models.ACCESS_MODE_ADMIN
|
||||||
}
|
}
|
||||||
ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam
|
ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam
|
||||||
if requireAdminTeam && !ctx.Org.IsAdminTeam {
|
if requireAdminTeam && !ctx.Org.IsAdminTeam {
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -29,17 +28,10 @@ func ApiRepoAssignment() macaron.Handler {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collaborators who have write access can be seen as owners.
|
// Check if the user is the same as the repository owner.
|
||||||
if ctx.IsSigned {
|
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
|
||||||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE)
|
u = ctx.User
|
||||||
if err != nil {
|
} else {
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.Repo.IsTrueOwner {
|
|
||||||
u, err = models.GetUserByName(userName)
|
u, err = models.GetUserByName(userName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == models.ErrUserNotExist {
|
if err == models.ErrUserNotExist {
|
||||||
|
@ -49,67 +41,37 @@ func ApiRepoAssignment() macaron.Handler {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
u = ctx.User
|
|
||||||
}
|
}
|
||||||
ctx.Repo.Owner = u
|
ctx.Repo.Owner = u
|
||||||
|
|
||||||
// Organization owner team members are true owners as well.
|
|
||||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
|
||||||
ctx.Repo.IsTrueOwner = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get repository.
|
// Get repository.
|
||||||
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == models.ErrRepoNotExist {
|
if err == models.ErrRepoNotExist {
|
||||||
ctx.Error(404)
|
ctx.Error(404)
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL})
|
ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL})
|
||||||
|
}
|
||||||
return
|
return
|
||||||
} else if err = repo.GetOwner(); err != nil {
|
} else if err = repo.GetOwner(); err != nil {
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
|
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mirror repository owner(mirror repository doesn't have access).
|
mode, err := models.AccessLevel(ctx.User, repo)
|
||||||
if ctx.IsSigned && !ctx.Repo.IsOwner {
|
|
||||||
if repo.OwnerId == ctx.User.Id {
|
|
||||||
ctx.Repo.IsOwner = true
|
|
||||||
}
|
|
||||||
// Check if current user has admin permission to repository.
|
|
||||||
if u.IsOrganization() {
|
|
||||||
auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, repo.Id, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"GetHighestAuthorize: " + err.Error(), base.DOC_URL})
|
ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if auth == models.ORG_ADMIN {
|
|
||||||
ctx.Repo.IsOwner = true
|
ctx.Repo.AccessMode = mode
|
||||||
ctx.Repo.IsAdmin = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check access.
|
// Check access.
|
||||||
if repo.IsPrivate && !ctx.Repo.IsOwner {
|
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
|
||||||
if ctx.User == nil {
|
|
||||||
ctx.Error(404)
|
ctx.Error(404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE)
|
|
||||||
if err != nil {
|
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL})
|
|
||||||
return
|
|
||||||
} else if !hasAccess {
|
|
||||||
ctx.Error(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.Repo.HasAccess = true
|
|
||||||
|
|
||||||
ctx.Repo.Repository = repo
|
ctx.Repo.Repository = repo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,101 +204,49 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||||
refName = ctx.Params(":path")
|
refName = ctx.Params(":path")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collaborators who have write access can be seen as owners.
|
// Check if the user is the same as the repository owner
|
||||||
if ctx.IsSigned {
|
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
|
||||||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE)
|
u = ctx.User
|
||||||
if err != nil {
|
} else {
|
||||||
ctx.Handle(500, "HasAccess", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.Repo.IsTrueOwner {
|
|
||||||
u, err = models.GetUserByName(userName)
|
u, err = models.GetUserByName(userName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == models.ErrUserNotExist {
|
if err == models.ErrUserNotExist {
|
||||||
ctx.Handle(404, "GetUserByName", err)
|
ctx.Handle(404, "GetUserByName", err)
|
||||||
} else if redirect {
|
|
||||||
log.Error(4, "GetUserByName", err)
|
|
||||||
ctx.Redirect(setting.AppSubUrl + "/")
|
|
||||||
} else {
|
} else {
|
||||||
ctx.Handle(500, "GetUserByName", err)
|
ctx.Handle(500, "GetUserByName", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
u = ctx.User
|
|
||||||
}
|
|
||||||
|
|
||||||
if u == nil {
|
|
||||||
if redirect {
|
|
||||||
ctx.Redirect(setting.AppSubUrl + "/")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ctx.Repo.Owner = u
|
ctx.Repo.Owner = u
|
||||||
|
|
||||||
// Organization owner team members are true owners as well.
|
|
||||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
|
||||||
ctx.Repo.IsTrueOwner = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get repository.
|
// Get repository.
|
||||||
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == models.ErrRepoNotExist {
|
if err == models.ErrRepoNotExist {
|
||||||
ctx.Handle(404, "GetRepositoryByName", err)
|
ctx.Handle(404, "GetRepositoryByName", err)
|
||||||
return
|
} else {
|
||||||
} else if redirect {
|
|
||||||
ctx.Redirect(setting.AppSubUrl + "/")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Handle(500, "GetRepositoryByName", err)
|
ctx.Handle(500, "GetRepositoryByName", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
} else if err = repo.GetOwner(); err != nil {
|
} else if err = repo.GetOwner(); err != nil {
|
||||||
ctx.Handle(500, "GetOwner", err)
|
ctx.Handle(500, "GetOwner", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mirror repository owner(mirror repository doesn't have access).
|
mode, err := models.AccessLevel(ctx.User, repo)
|
||||||
if ctx.IsSigned && !ctx.Repo.IsOwner {
|
|
||||||
if repo.OwnerId == ctx.User.Id {
|
|
||||||
ctx.Repo.IsOwner = true
|
|
||||||
}
|
|
||||||
// Check if current user has admin permission to repository.
|
|
||||||
if u.IsOrganization() {
|
|
||||||
auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, repo.Id, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetHighestAuthorize", err)
|
ctx.Handle(500, "AccessLevel", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if auth == models.ORG_ADMIN {
|
ctx.Repo.AccessMode = mode
|
||||||
ctx.Repo.IsOwner = true
|
|
||||||
ctx.Repo.IsAdmin = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check access.
|
// Check access.
|
||||||
if repo.IsPrivate && !ctx.Repo.IsOwner {
|
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
|
||||||
if ctx.User == nil {
|
ctx.Handle(404, "no access right", err)
|
||||||
ctx.Handle(404, "HasAccess", nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Handle(500, "HasAccess", err)
|
|
||||||
return
|
|
||||||
} else if !hasAccess {
|
|
||||||
ctx.Handle(404, "HasAccess", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.Repo.HasAccess = true
|
|
||||||
ctx.Data["HasAccess"] = true
|
ctx.Data["HasAccess"] = true
|
||||||
|
|
||||||
if repo.IsMirror {
|
if repo.IsMirror {
|
||||||
|
@ -383,8 +293,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||||
ctx.Data["Title"] = u.Name + "/" + repo.Name
|
ctx.Data["Title"] = u.Name + "/" + repo.Name
|
||||||
ctx.Data["Repository"] = repo
|
ctx.Data["Repository"] = repo
|
||||||
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
|
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
|
||||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
|
ctx.Data["IsRepositoryOwner"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_WRITE
|
||||||
ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
|
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_ADMIN
|
||||||
|
|
||||||
ctx.Data["DisableSSH"] = setting.DisableSSH
|
ctx.Data["DisableSSH"] = setting.DisableSSH
|
||||||
ctx.Repo.CloneLink, err = repo.CloneLink()
|
ctx.Repo.CloneLink, err = repo.CloneLink()
|
||||||
|
@ -438,9 +348,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequireTrueOwner() macaron.Handler {
|
func RequireAdmin() macaron.Handler {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
|
if ctx.Repo.AccessMode < models.ACCESS_MODE_ADMIN {
|
||||||
if !ctx.IsSigned {
|
if !ctx.IsSigned {
|
||||||
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
|
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
|
||||||
ctx.Redirect(setting.AppSubUrl + "/user/login")
|
ctx.Redirect(setting.AppSubUrl + "/user/login")
|
||||||
|
|
|
@ -67,6 +67,11 @@ var (
|
||||||
CookieRememberName string
|
CookieRememberName string
|
||||||
ReverseProxyAuthUser string
|
ReverseProxyAuthUser string
|
||||||
|
|
||||||
|
// Database settings.
|
||||||
|
UseSQLite3 bool
|
||||||
|
UseMySQL bool
|
||||||
|
UsePostgreSQL bool
|
||||||
|
|
||||||
// Webhook settings.
|
// Webhook settings.
|
||||||
Webhook struct {
|
Webhook struct {
|
||||||
TaskInterval int
|
TaskInterval int
|
||||||
|
@ -267,10 +272,6 @@ func NewConfigContext() {
|
||||||
"StampNano": time.StampNano,
|
"StampNano": time.StampNano,
|
||||||
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
|
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
|
||||||
|
|
||||||
if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil {
|
|
||||||
log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
RunUser = Cfg.Section("").Key("RUN_USER").String()
|
RunUser = Cfg.Section("").Key("RUN_USER").String()
|
||||||
curUser := os.Getenv("USER")
|
curUser := os.Getenv("USER")
|
||||||
if len(curUser) == 0 {
|
if len(curUser) == 0 {
|
||||||
|
@ -293,9 +294,6 @@ func NewConfigContext() {
|
||||||
} else {
|
} else {
|
||||||
RepoRootPath = filepath.Clean(RepoRootPath)
|
RepoRootPath = filepath.Clean(RepoRootPath)
|
||||||
}
|
}
|
||||||
if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil {
|
|
||||||
log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err)
|
|
||||||
}
|
|
||||||
ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
|
ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
|
||||||
|
|
||||||
sec = Cfg.Section("picture")
|
sec = Cfg.Section("picture")
|
||||||
|
@ -304,7 +302,6 @@ func NewConfigContext() {
|
||||||
if !filepath.IsAbs(AvatarUploadPath) {
|
if !filepath.IsAbs(AvatarUploadPath) {
|
||||||
AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
|
AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
|
||||||
}
|
}
|
||||||
os.MkdirAll(AvatarUploadPath, os.ModePerm)
|
|
||||||
switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") {
|
switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") {
|
||||||
case "duoshuo":
|
case "duoshuo":
|
||||||
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
||||||
|
@ -369,9 +366,11 @@ func newLogService() {
|
||||||
log.Fatal(4, "Unknown log mode: %s", mode)
|
log.Fatal(4, "Unknown log mode: %s", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
|
||||||
// Log level.
|
// Log level.
|
||||||
levelName := Cfg.Section("log."+mode).Key("LEVEL").In("Trace",
|
levelName := Cfg.Section("log."+mode).Key("LEVEL").In(
|
||||||
[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
|
Cfg.Section("log").Key("LEVEL").In("Trace", validLevels),
|
||||||
|
validLevels)
|
||||||
level, ok := logLevels[levelName]
|
level, ok := logLevels[levelName]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatal(4, "Unknown log level: %s", levelName)
|
log.Fatal(4, "Unknown log level: %s", levelName)
|
||||||
|
|
|
@ -238,28 +238,31 @@ func ListMyRepos(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
numOwnRepos := len(ownRepos)
|
numOwnRepos := len(ownRepos)
|
||||||
|
|
||||||
collaRepos, err := models.GetCollaborativeRepos(ctx.User.Name)
|
accessibleRepos, err := ctx.User.GetAccessibleRepositories()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"GetCollaborativeRepos: " + err.Error(), base.DOC_URL})
|
ctx.JSON(500, &base.ApiJsonErr{"GetAccessibleRepositories: " + err.Error(), base.DOC_URL})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repos := make([]*api.Repository, numOwnRepos+len(collaRepos))
|
repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
|
||||||
for i := range ownRepos {
|
for i := range ownRepos {
|
||||||
repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
|
repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
|
||||||
}
|
}
|
||||||
for i := range collaRepos {
|
i := numOwnRepos
|
||||||
if err = collaRepos[i].GetOwner(); err != nil {
|
|
||||||
|
for repo, access := range accessibleRepos {
|
||||||
|
if err = repo.GetOwner(); err != nil {
|
||||||
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
|
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
j := i + numOwnRepos
|
|
||||||
repos[j] = ToApiRepository(collaRepos[i].Owner, collaRepos[i].Repository, api.Permission{false, collaRepos[i].CanPush, true})
|
repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.ACCESS_MODE_WRITE, true})
|
||||||
|
|
||||||
// FIXME: cache result to reduce DB query?
|
// FIXME: cache result to reduce DB query?
|
||||||
if collaRepos[i].Owner.IsOrganization() && collaRepos[i].Owner.IsOwnedBy(ctx.User.Id) {
|
if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) {
|
||||||
repos[j].Permissions.Admin = true
|
repos[i].Permissions.Admin = true
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, &repos)
|
ctx.JSON(200, &repos)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetRepoRawFile(ctx *middleware.Context) {
|
func GetRepoRawFile(ctx *middleware.Context) {
|
||||||
if ctx.Repo.Repository.IsPrivate && !ctx.Repo.HasAccess {
|
if !ctx.Repo.HasAccess() {
|
||||||
ctx.Error(404)
|
ctx.Error(404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,7 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
|
||||||
cfg.Section("session").Key("PROVIDER").SetValue("file")
|
cfg.Section("session").Key("PROVIDER").SetValue("file")
|
||||||
|
|
||||||
cfg.Section("log").Key("MODE").SetValue("file")
|
cfg.Section("log").Key("MODE").SetValue("file")
|
||||||
|
cfg.Section("log").Key("LEVEL").SetValue("Info")
|
||||||
|
|
||||||
cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
|
cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
|
||||||
cfg.Section("security").Key("SECRET_KEY").SetValue(base.GetRandomString(15))
|
cfg.Section("security").Key("SECRET_KEY").SetValue(base.GetRandomString(15))
|
||||||
|
|
|
@ -165,14 +165,14 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate permission level.
|
// Validate permission level.
|
||||||
var auth models.AuthorizeType
|
var auth models.AccessMode
|
||||||
switch form.Permission {
|
switch form.Permission {
|
||||||
case "read":
|
case "read":
|
||||||
auth = models.ORG_READABLE
|
auth = models.ACCESS_MODE_READ
|
||||||
case "write":
|
case "write":
|
||||||
auth = models.ORG_WRITABLE
|
auth = models.ACCESS_MODE_WRITE
|
||||||
case "admin":
|
case "admin":
|
||||||
auth = models.ORG_ADMIN
|
auth = models.ACCESS_MODE_ADMIN
|
||||||
default:
|
default:
|
||||||
ctx.Error(401)
|
ctx.Error(401)
|
||||||
return
|
return
|
||||||
|
@ -181,7 +181,7 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
|
||||||
org := ctx.Org.Organization
|
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,
|
||||||
|
@ -246,14 +246,14 @@ func EditTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
|
||||||
isAuthChanged := false
|
isAuthChanged := false
|
||||||
if !t.IsOwnerTeam() {
|
if !t.IsOwnerTeam() {
|
||||||
// Validate permission level.
|
// Validate permission level.
|
||||||
var auth models.AuthorizeType
|
var auth models.AccessMode
|
||||||
switch form.Permission {
|
switch form.Permission {
|
||||||
case "read":
|
case "read":
|
||||||
auth = models.ORG_READABLE
|
auth = models.ACCESS_MODE_READ
|
||||||
case "write":
|
case "write":
|
||||||
auth = models.ORG_WRITABLE
|
auth = models.ACCESS_MODE_WRITE
|
||||||
case "admin":
|
case "admin":
|
||||||
auth = models.ORG_ADMIN
|
auth = models.ACCESS_MODE_ADMIN
|
||||||
default:
|
default:
|
||||||
ctx.Error(401)
|
ctx.Error(401)
|
||||||
return
|
return
|
||||||
|
|
|
@ -131,18 +131,18 @@ func Http(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isPublicPull {
|
if !isPublicPull {
|
||||||
var tp = models.WRITABLE
|
var tp = models.ACCESS_MODE_WRITE
|
||||||
if isPull {
|
if isPull {
|
||||||
tp = models.READABLE
|
tp = models.ACCESS_MODE_READ
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err := models.HasAccess(authUsername, username+"/"+reponame, tp)
|
has, err := models.HasAccess(authUser, repo, tp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(401, "no basic auth and digit auth", nil)
|
ctx.Handle(401, "no basic auth and digit auth", nil)
|
||||||
return
|
return
|
||||||
} else if !has {
|
} else if !has {
|
||||||
if tp == models.READABLE {
|
if tp == models.ACCESS_MODE_READ {
|
||||||
has, err = models.HasAccess(authUsername, username+"/"+reponame, models.WRITABLE)
|
has, err = models.HasAccess(authUser, repo, models.ACCESS_MODE_WRITE)
|
||||||
if err != nil || !has {
|
if err != nil || !has {
|
||||||
ctx.Handle(401, "no basic auth and digit auth", nil)
|
ctx.Handle(401, "no basic auth and digit auth", nil)
|
||||||
return
|
return
|
||||||
|
@ -152,6 +152,11 @@ func Http(ctx *middleware.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isPull && repo.IsMirror {
|
||||||
|
ctx.Handle(401, "can't push to mirror", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,7 @@ func CreateIssue(ctx *middleware.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
|
us, err := ctx.Repo.Repository.GetCollaborators()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
|
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
|
||||||
return
|
return
|
||||||
|
@ -218,7 +218,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
|
_, err = ctx.Repo.Repository.GetCollaborators()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
send(500, nil, err)
|
send(500, nil, err)
|
||||||
return
|
return
|
||||||
|
@ -230,7 +230,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only collaborators can assign.
|
// Only collaborators can assign.
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
form.AssigneeId = 0
|
form.AssigneeId = 0
|
||||||
}
|
}
|
||||||
issue := &models.Issue{
|
issue := &models.Issue{
|
||||||
|
@ -246,8 +246,8 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
|
||||||
if err := models.NewIssue(issue); err != nil {
|
if err := models.NewIssue(issue); err != nil {
|
||||||
send(500, nil, err)
|
send(500, nil, err)
|
||||||
return
|
return
|
||||||
} else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, ctx.Repo.Owner.Id,
|
} else if err := models.NewIssueUserPairs(ctx.Repo.Repository, issue.Id, ctx.Repo.Owner.Id,
|
||||||
ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil {
|
ctx.User.Id, form.AssigneeId); err != nil {
|
||||||
send(500, nil, err)
|
send(500, nil, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -384,7 +384,7 @@ func ViewIssue(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all collaborators.
|
// Get all collaborators.
|
||||||
ctx.Data["Collaborators"], err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
|
ctx.Data["Collaborators"], err = ctx.Repo.Repository.GetCollaborators()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
|
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
|
||||||
return
|
return
|
||||||
|
@ -434,7 +434,7 @@ func ViewIssue(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = issue.Name
|
ctx.Data["Title"] = issue.Name
|
||||||
ctx.Data["Issue"] = issue
|
ctx.Data["Issue"] = issue
|
||||||
ctx.Data["Comments"] = comments
|
ctx.Data["Comments"] = comments
|
||||||
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
|
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
|
||||||
ctx.Data["IsRepoToolbarIssues"] = true
|
ctx.Data["IsRepoToolbarIssues"] = true
|
||||||
ctx.Data["IsRepoToolbarIssuesList"] = false
|
ctx.Data["IsRepoToolbarIssuesList"] = false
|
||||||
ctx.HTML(200, ISSUE_VIEW)
|
ctx.HTML(200, ISSUE_VIEW)
|
||||||
|
@ -457,7 +457,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner {
|
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() {
|
||||||
ctx.Error(403)
|
ctx.Error(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -484,7 +484,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateIssueLabel(ctx *middleware.Context) {
|
func UpdateIssueLabel(ctx *middleware.Context) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Error(403)
|
ctx.Error(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -561,7 +561,7 @@ func UpdateIssueLabel(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateIssueMilestone(ctx *middleware.Context) {
|
func UpdateIssueMilestone(ctx *middleware.Context) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Error(403)
|
ctx.Error(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -607,7 +607,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateAssignee(ctx *middleware.Context) {
|
func UpdateAssignee(ctx *middleware.Context) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Error(403)
|
ctx.Error(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -753,7 +753,7 @@ func Comment(ctx *middleware.Context) {
|
||||||
|
|
||||||
// Check if issue owner changes the status of issue.
|
// Check if issue owner changes the status of issue.
|
||||||
var newStatus string
|
var newStatus string
|
||||||
if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id {
|
if ctx.Repo.IsOwner() || issue.PosterId == ctx.User.Id {
|
||||||
newStatus = ctx.Query("change_status")
|
newStatus = ctx.Query("change_status")
|
||||||
}
|
}
|
||||||
if len(newStatus) > 0 {
|
if len(newStatus) > 0 {
|
||||||
|
|
|
@ -41,7 +41,7 @@ func Releases(ctx *middleware.Context) {
|
||||||
tags := make([]*models.Release, len(rawTags))
|
tags := make([]*models.Release, len(rawTags))
|
||||||
for i, rawTag := range rawTags {
|
for i, rawTag := range rawTags {
|
||||||
for j, rel := range rels {
|
for j, rel := range rels {
|
||||||
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner) {
|
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rel.TagName == rawTag {
|
if rel.TagName == rawTag {
|
||||||
|
@ -140,7 +140,7 @@ func Releases(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRelease(ctx *middleware.Context) {
|
func NewRelease(ctx *middleware.Context) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Handle(403, "release.ReleasesNew", nil)
|
ctx.Handle(403, "release.ReleasesNew", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ func NewRelease(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Handle(403, "release.ReleasesNew", nil)
|
ctx.Handle(403, "release.ReleasesNew", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditRelease(ctx *middleware.Context) {
|
func EditRelease(ctx *middleware.Context) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Handle(403, "release.ReleasesEdit", nil)
|
ctx.Handle(403, "release.ReleasesEdit", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ func EditRelease(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
|
func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Handle(403, "release.EditReleasePost", nil)
|
ctx.Handle(403, "release.EditReleasePost", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -349,7 +349,7 @@ func Action(ctx *middleware.Context) {
|
||||||
case "unstar":
|
case "unstar":
|
||||||
err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
|
err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
|
||||||
case "desc":
|
case "desc":
|
||||||
if !ctx.Repo.IsOwner {
|
if !ctx.Repo.IsOwner() {
|
||||||
ctx.Error(404)
|
ctx.Error(404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -54,15 +53,11 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
|
||||||
newRepoName := form.RepoName
|
newRepoName := form.RepoName
|
||||||
// Check if repository name has been changed.
|
// Check if repository name has been changed.
|
||||||
if ctx.Repo.Repository.Name != newRepoName {
|
if ctx.Repo.Repository.Name != newRepoName {
|
||||||
isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName)
|
if models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) {
|
||||||
if err != nil {
|
|
||||||
ctx.Handle(500, "IsRepositoryExist", err)
|
|
||||||
return
|
|
||||||
} else if isExist {
|
|
||||||
ctx.Data["Err_RepoName"] = true
|
ctx.Data["Err_RepoName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, nil)
|
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, nil)
|
||||||
return
|
return
|
||||||
} else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
|
} else if err := models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
|
||||||
if err == models.ErrRepoNameIllegal {
|
if err == models.ErrRepoNameIllegal {
|
||||||
ctx.Data["Err_RepoName"] = true
|
ctx.Data["Err_RepoName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), SETTINGS_OPTIONS, nil)
|
ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), SETTINGS_OPTIONS, nil)
|
||||||
|
@ -169,22 +164,12 @@ func SettingsCollaboration(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
ctx.Data["PageIsSettingsCollaboration"] = true
|
ctx.Data["PageIsSettingsCollaboration"] = true
|
||||||
|
|
||||||
repoLink := path.Join(ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName)
|
|
||||||
|
|
||||||
if ctx.Req.Method == "POST" {
|
if ctx.Req.Method == "POST" {
|
||||||
name := strings.ToLower(ctx.Query("collaborator"))
|
name := strings.ToLower(ctx.Query("collaborator"))
|
||||||
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
|
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
|
||||||
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
|
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
has, err := models.HasAccess(name, repoLink, models.WRITABLE)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Handle(500, "HasAccess", err)
|
|
||||||
return
|
|
||||||
} else if has {
|
|
||||||
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := models.GetUserByName(name)
|
u, err := models.GetUserByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -204,9 +189,8 @@ func SettingsCollaboration(ctx *middleware.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink,
|
if err = ctx.Repo.Repository.AddCollaborator(u); err != nil {
|
||||||
Mode: models.WRITABLE}); err != nil {
|
ctx.Handle(500, "AddCollaborator", err)
|
||||||
ctx.Handle(500, "AddAccess", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,50 +209,27 @@ func SettingsCollaboration(ctx *middleware.Context) {
|
||||||
// Delete collaborator.
|
// Delete collaborator.
|
||||||
remove := strings.ToLower(ctx.Query("remove"))
|
remove := strings.ToLower(ctx.Query("remove"))
|
||||||
if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
|
if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
|
||||||
needDelete := true
|
u, err := models.GetUserByName(remove)
|
||||||
if ctx.User.IsOrganization() {
|
|
||||||
// Check if user belongs to a team that has access to this repository.
|
|
||||||
auth, err := models.GetHighestAuthorize(ctx.Repo.Owner.Id, ctx.User.Id, ctx.Repo.Repository.Id, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetHighestAuthorize", err)
|
ctx.Handle(500, "GetUserByName", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if auth > 0 {
|
if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil {
|
||||||
needDelete = false
|
ctx.Handle(500, "DeleteCollaborator", err)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if needDelete {
|
|
||||||
if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil {
|
|
||||||
ctx.Handle(500, "DeleteAccess", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
|
ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
names, err := models.GetCollaboratorNames(repoLink)
|
users, err := ctx.Repo.Repository.GetCollaborators()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetCollaborators", err)
|
ctx.Handle(500, "GetCollaborators", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
collaborators := make([]*models.User, 0, len(names))
|
ctx.Data["Collaborators"] = users
|
||||||
for _, name := range names {
|
|
||||||
u, err := models.GetUserByName(name)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Handle(500, "GetUserByName", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Does not show organization members.
|
|
||||||
if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
collaborators = append(collaborators, u)
|
|
||||||
}
|
|
||||||
ctx.Data["Collaborators"] = collaborators
|
|
||||||
ctx.HTML(200, COLLABORATION)
|
ctx.HTML(200, COLLABORATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,13 +49,19 @@ func Dashboard(ctx *middleware.Context) {
|
||||||
} else {
|
} else {
|
||||||
// Normal user.
|
// Normal user.
|
||||||
ctxUser = ctx.User
|
ctxUser = ctx.User
|
||||||
collaborates, err := models.GetCollaborativeRepos(ctxUser.Name)
|
collaborates, err := ctx.User.GetAccessibleRepositories()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetCollaborativeRepos", err)
|
ctx.Handle(500, "GetAccessibleRepositories", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["CollaborateCount"] = len(collaborates)
|
|
||||||
ctx.Data["CollaborativeRepos"] = collaborates
|
repositories := make([]*models.Repository, 0, len(collaborates))
|
||||||
|
for repo := range collaborates {
|
||||||
|
repositories = append(repositories, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["CollaborateCount"] = len(repositories)
|
||||||
|
ctx.Data["CollaborativeRepos"] = repositories
|
||||||
}
|
}
|
||||||
ctx.Data["ContextUser"] = ctxUser
|
ctx.Data["ContextUser"] = ctxUser
|
||||||
|
|
||||||
|
@ -97,11 +103,15 @@ func Dashboard(ctx *middleware.Context) {
|
||||||
feeds := make([]*models.Action, 0, len(actions))
|
feeds := make([]*models.Action, 0, len(actions))
|
||||||
for _, act := range actions {
|
for _, act := range actions {
|
||||||
if act.IsPrivate {
|
if act.IsPrivate {
|
||||||
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
|
// This prevents having to retrieve the repository for each action
|
||||||
models.READABLE); !has {
|
repo := &models.Repository{Id: act.RepoId, IsPrivate: true}
|
||||||
|
if act.RepoUserName != ctx.User.LowerName {
|
||||||
|
if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// FIXME: cache results?
|
// FIXME: cache results?
|
||||||
u, err := models.GetUserByName(act.ActUserName)
|
u, err := models.GetUserByName(act.ActUserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -205,11 +215,15 @@ func Profile(ctx *middleware.Context) {
|
||||||
if !ctx.IsSigned {
|
if !ctx.IsSigned {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
|
// This prevents having to retrieve the repository for each action
|
||||||
models.READABLE); !has {
|
repo := &models.Repository{Id: act.RepoId, IsPrivate: true}
|
||||||
|
if act.RepoUserName != ctx.User.LowerName {
|
||||||
|
if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// FIXME: cache results?
|
// FIXME: cache results?
|
||||||
u, err := models.GetUserByName(act.ActUserName)
|
u, err := models.GetUserByName(act.ActUserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.5.14.0222 Beta
|
0.5.16.0228 Beta
|
|
@ -1,7 +1,7 @@
|
||||||
</div>
|
</div>
|
||||||
<footer id="footer">
|
<footer id="footer">
|
||||||
<div class="container clear">
|
<div class="container clear">
|
||||||
<p class="left" id="footer-rights">© 2015 GoGits · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> ·
|
<p class="left" id="footer-rights">© 2015 Gogs · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> ·
|
||||||
{{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong></p>
|
{{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong></p>
|
||||||
|
|
||||||
<div class="right" id="footer-links">
|
<div class="right" id="footer-links">
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="org-repo-list">
|
<div id="org-repo-list">
|
||||||
{{range .Repos}}
|
{{range .Repos}}
|
||||||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser.Name)}}
|
{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}}
|
||||||
<div class="org-repo-item">
|
<div class="org-repo-item">
|
||||||
<ul class="org-repo-status right">
|
<ul class="org-repo-status right">
|
||||||
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
|
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li id="repo-header-fork">
|
<li id="repo-header-fork">
|
||||||
<a id="repo-header-fork-btn" {{if or (not $.IsRepositoryTrueOwner) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}>
|
<a id="repo-header-fork-btn" {{if or (not $.IsRepositoryAdmin) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}>
|
||||||
<button class="btn btn-gray text-bold btn-radius">
|
<button class="btn btn-gray text-bold btn-radius">
|
||||||
<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
|
<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
|
||||||
<span class="num">{{.NumForks}}</span>
|
<span class="num">{{.NumForks}}</span>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<!-- <li>
|
<!-- <li>
|
||||||
<a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a>
|
<a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a>
|
||||||
</li> -->
|
</li> -->
|
||||||
{{if .IsRepositoryTrueOwner}}
|
{{if .IsRepositoryAdmin}}
|
||||||
<li class="border-bottom"></li>
|
<li class="border-bottom"></li>
|
||||||
<li>
|
<li>
|
||||||
<a class="radius" href="{{.RepoLink}}/settings"><i class="octicon octicon-tools"></i>{{.i18n.Tr "repo.settings"}}</a>
|
<a class="radius" href="{{.RepoLink}}/settings"><i class="octicon octicon-tools"></i>{{.i18n.Tr "repo.settings"}}</a>
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
<li><a href="#">Pulse</a></li>
|
<li><a href="#">Pulse</a></li>
|
||||||
<li><a href="#">Network</a></li>
|
<li><a href="#">Network</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li> -->{{end}}{{if .IsRepositoryTrueOwner}}
|
</li> -->{{end}}{{if .IsRepositoryAdmin}}
|
||||||
<li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a>
|
<li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a>
|
||||||
</li>{{end}}
|
</li>{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
<div class="tab-pane active">
|
<div class="tab-pane active">
|
||||||
<div id="org-repo-list">
|
<div id="org-repo-list">
|
||||||
{{range .Repos}}
|
{{range .Repos}}
|
||||||
{{if or (not .IsPrivate) (.HasAccess $.SignedUserName)}}
|
{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}}
|
||||||
<div class="org-repo-item">
|
<div class="org-repo-item">
|
||||||
<ul class="org-repo-status right">
|
<ul class="org-repo-status right">
|
||||||
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
|
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
|
||||||
|
|
Reference in a new issue