@@ -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. | |||||
 |  | ||||
##### 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 服务。 | |||||
 |  | ||||
##### 当前版本:0.5.13 Beta | |||||
##### 当前版本:0.5.16 Beta | |||||
## 开发目的 | ## 开发目的 | ||||
@@ -44,7 +44,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自 | |||||
## 系统要求 | ## 系统要求 | ||||
- 最低的系统硬件要求为一个廉价的树莓派 | - 最低的系统硬件要求为一个廉价的树莓派 | ||||
- 如果用于团队项目,建议使用 4 核 CPU 及 1GB 内存 | |||||
- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存 | |||||
## 安装部署 | ## 安装部署 | ||||
@@ -75,4 +75,4 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自 | |||||
## 授权许可 | ## 授权许可 | ||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 | |||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 |
@@ -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{ | |||||
"git-upload-pack": models.WRITABLE, | |||||
"git upload-pack": models.WRITABLE, | |||||
"git-upload-archive": models.WRITABLE, | |||||
COMMANDS_READONLY = map[string]models.AccessMode{ | |||||
"git-upload-pack": models.ACCESS_MODE_WRITE, | |||||
"git upload-pack": models.ACCESS_MODE_WRITE, | |||||
"git-upload-archive": models.ACCESS_MODE_WRITE, | |||||
} | } | ||||
COMMANDS_WRITE = map[string]models.AccessType{ | |||||
"git-receive-pack": models.READABLE, | |||||
"git receive-pack": models.READABLE, | |||||
COMMANDS_WRITE = map[string]models.AccessMode{ | |||||
"git-receive-pack": models.ACCESS_MODE_READ, | |||||
"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) { | |||||
if k.IsSet("config") { | |||||
setting.CustomConf = k.String("config") | |||||
func runServ(c *cli.Context) { | |||||
if c.IsSet("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,9 +144,19 @@ func runServ(k *cli.Context) { | |||||
} | } | ||||
// Access check. | // Access check. | ||||
repo, err := models.GetRepositoryByName(repoUser.Id, repoName) | |||||
if err != nil { | |||||
if err == models.ErrRepoNotExist { | |||||
println("Gogs: given repository does not exist") | |||||
log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName) | |||||
} | |||||
println("Gogs: internal error:", err.Error()) | |||||
log.GitLogger.Fatal(2, "Fail to get repository: %v", err) | |||||
} | |||||
switch { | switch { | ||||
case isWrite: | case isWrite: | ||||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE) | |||||
has, err := models.HasAccess(user, repo, models.ACCESS_MODE_WRITE) | |||||
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 write access:", err) | log.GitLogger.Fatal(2, "Fail to check write access:", err) | ||||
@@ -154,22 +164,17 @@ func runServ(k *cli.Context) { | |||||
println("You have no right to write this repository") | 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) | 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) | |||||
if err != nil { | |||||
if err == models.ErrRepoNotExist { | |||||
println("Gogs: given repository does not exist") | |||||
log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName) | |||||
} | |||||
println("Gogs: internal error:", err.Error()) | |||||
log.GitLogger.Fatal(2, "Fail to get repository: %v", err) | |||||
} | |||||
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) | ||||
@@ -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()) | ||||
@@ -5,76 +5,190 @@ | |||||
package models | package models | ||||
import ( | import ( | ||||
"strings" | |||||
"time" | |||||
"github.com/go-xorm/xorm" | |||||
"fmt" | |||||
) | ) | ||||
type AccessType int | |||||
type AccessMode int | |||||
const ( | const ( | ||||
READABLE AccessType = iota + 1 | |||||
WRITABLE | |||||
ACCESS_MODE_NONE AccessMode = iota | |||||
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 | |||||
UserName string `xorm:"UNIQUE(s)"` | |||||
RepoName string `xorm:"UNIQUE(s)"` // <user name>/<repo name> | |||||
Mode AccessType `xorm:"UNIQUE(s)"` | |||||
Created time.Time `xorm:"CREATED"` | |||||
ID int64 `xorm:"pk autoincr"` | |||||
UserID int64 `xorm:"UNIQUE(s)"` | |||||
RepoID int64 `xorm:"UNIQUE(s)"` | |||||
Mode AccessMode | |||||
} | |||||
func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) { | |||||
mode := ACCESS_MODE_NONE | |||||
if !repo.IsPrivate { | |||||
mode = ACCESS_MODE_READ | |||||
} | |||||
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 | |||||
} | } | ||||
// AddAccess adds new access record. | |||||
func AddAccess(access *Access) error { | |||||
access.UserName = strings.ToLower(access.UserName) | |||||
access.RepoName = strings.ToLower(access.RepoName) | |||||
_, err := x.Insert(access) | |||||
return err | |||||
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the | |||||
// user does not have access. User can be nil! | |||||
func AccessLevel(u *User, repo *Repository) (AccessMode, error) { | |||||
return accessLevel(x, u, repo) | |||||
} | } | ||||
// UpdateAccess updates access information. | |||||
func UpdateAccess(access *Access) error { | |||||
access.UserName = strings.ToLower(access.UserName) | |||||
access.RepoName = strings.ToLower(access.RepoName) | |||||
_, err := x.Id(access.Id).Update(access) | |||||
return err | |||||
func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) { | |||||
mode, err := accessLevel(e, u, repo) | |||||
return testMode <= mode, err | |||||
} | } | ||||
// DeleteAccess deletes access record. | |||||
func DeleteAccess(access *Access) error { | |||||
_, err := x.Delete(access) | |||||
return err | |||||
// HasAccess returns true if someone has the request access level. User can be nil! | |||||
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) { | |||||
return hasAccess(x, u, repo, testMode) | |||||
} | } | ||||
// UpdateAccess updates access information with session for rolling back. | |||||
func UpdateAccessWithSession(sess *xorm.Session, access *Access) error { | |||||
if _, err := sess.Id(access.Id).Update(access); err != nil { | |||||
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 | |||||
} | } | ||||
return nil | |||||
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 | |||||
} | } | ||||
// HasAccess returns true if someone can read or write to given repository. | |||||
// The repoName should be in format <username>/<reponame>. | |||||
func HasAccess(uname, repoName string, mode AccessType) (bool, error) { | |||||
if len(repoName) == 0 { | |||||
return false, nil | |||||
// 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 | |||||
} | } | ||||
access := &Access{ | |||||
UserName: strings.ToLower(uname), | |||||
RepoName: strings.ToLower(repoName), | |||||
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, | |||||
}) | |||||
} | } | ||||
has, err := x.Get(access) | |||||
// 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 | |||||
} | |||||
// FIXME: should be able to have read-only access. | |||||
// Give all collaborators write access. | |||||
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error { | |||||
collaborators, err := repo.getCollaborators(e) | |||||
if err != nil { | if err != nil { | ||||
return false, err | |||||
} else if !has { | |||||
return false, nil | |||||
} else if mode > access.Mode { | |||||
return false, nil | |||||
return fmt.Errorf("getCollaborators: %v", err) | |||||
} | |||||
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 true, nil | |||||
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(u *User, repo *Repository) (err error) { | |||||
if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | |||||
OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate}); err != nil { | |||||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | |||||
return err | |||||
func newRepoAction(e Engine, u *User, repo *Repository) (err error) { | |||||
if err = notifyWatchers(e, &Action{ | |||||
ActUserId: u.Id, | |||||
ActUserName: u.Name, | |||||
ActEmail: u.Email, | |||||
OpType: CREATE_REPO, | |||||
RepoId: repo.Id, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate}); err != nil { | |||||
return fmt.Errorf("notify watchers '%d/%s'", u.Id, repo.Id) | |||||
} | } | ||||
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. | |||||
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { | |||||
// NewRepoAction adds new action for creating repository. | |||||
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, | |||||
ActUserName: u.Name, | |||||
ActEmail: u.Email, | |||||
ActUserId: actUser.Id, | |||||
ActUserName: actUser.Name, | |||||
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 { | |||||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | |||||
return err | |||||
if err = notifyWatchers(e, action); err != nil { | |||||
return fmt.Errorf("notify watchers '%d/%s'", actUser.Id, repo.Id) | |||||
} | } | ||||
// 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 { | |||||
log.Error(4, "WatchRepo", err) | |||||
if err = watchRepo(e, repo.Owner.Id, repo.Id, false); err != nil { | |||||
return fmt.Errorf("watch repository: %v", err) | |||||
} | } | ||||
} | } | ||||
log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name) | |||||
return err | |||||
log.Trace("action.TransferRepoAction: %s/%s", actUser.Name, repo.Name) | |||||
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) { | |||||
iu := &IssueUser{IssueId: iid, RepoId: rid} | |||||
us, err := GetCollaborators(repoName) | |||||
func NewIssueUserPairs(repo *Repository, issueID, orgID, posterID, assigneeID int64) (err error) { | |||||
users, err := repo.GetCollaborators() | |||||
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" | |||||
var migrations = []migration{} | |||||
// If you want to "retire" a migration, remove it from the top of the list and | |||||
// 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, migration := range migrations[v:] { | |||||
if err = migration(x); err != nil { | |||||
return err | |||||
for i, m := range migrations[v-_MIN_DB_VER:] { | |||||
log.Info("Migration: %s", m.Description()) | |||||
if err = m.Migrate(x); err != nil { | |||||
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 { | |||||
return errors.New("You are migrating from a too old gogs version") | |||||
func sessionRelease(sess *xorm.Session) { | |||||
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(Repository), new(Watch), new(Star), new(Action), new(Access), | |||||
new(User), new(PublicKey), new(Oauth2), new(AccessToken), | |||||
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" { | |||||
UseSQLite3 = true | |||||
switch DbCfg.Type { | |||||
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 { | |||||
// return err | |||||
// } | |||||
if err = migrations.Migrate(x); err != nil { | |||||
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 | ||||
} | } | ||||
@@ -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) | |||||
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() | |||||
return os.Rename(UserPath(u.LowerName), UserPath(newUserName)) | |||||
} | } | ||||
// 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(rawSql, keyId).Get(user) | |||||
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) | |||||
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(id int64) (*User, error) { | |||||
func getUserById(e Engine, 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...) | ||||
@@ -164,7 +164,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||||
}, | }, | ||||
"DiffTypeToStr": DiffTypeToStr, | "DiffTypeToStr": DiffTypeToStr, | ||||
"DiffLineTypeToStr": DiffLineTypeToStr, | "DiffLineTypeToStr": DiffLineTypeToStr, | ||||
"Sha1": Sha1, | |||||
"Sha1": Sha1, | |||||
"ShortSha": ShortSha, | "ShortSha": ShortSha, | ||||
"Md5": EncodeMd5, | "Md5": EncodeMd5, | ||||
"ActionContent2Commits": ActionContent2Commits, | "ActionContent2Commits": ActionContent2Commits, | ||||
@@ -38,29 +38,7 @@ type Context struct { | |||||
IsSigned bool | IsSigned bool | ||||
IsBasicAuth bool | IsBasicAuth bool | ||||
Repo struct { | |||||
IsOwner bool | |||||
IsTrueOwner bool | |||||
IsWatching bool | |||||
IsBranch bool | |||||
IsTag bool | |||||
IsCommit bool | |||||
IsAdmin bool // Current user is admin level. | |||||
HasAccess bool | |||||
Repository *models.Repository | |||||
Owner *models.User | |||||
Commit *git.Commit | |||||
Tag *git.Tag | |||||
GitRepo *git.Repository | |||||
BranchName string | |||||
TagName string | |||||
TreeName string | |||||
CommitId string | |||||
RepoLink string | |||||
CloneLink models.CloneLink | |||||
CommitsCount int | |||||
Mirror *models.Mirror | |||||
} | |||||
Repo RepoContext | |||||
Org struct { | Org struct { | ||||
IsOwner bool | IsOwner bool | ||||
@@ -73,6 +51,37 @@ type Context struct { | |||||
} | } | ||||
} | } | ||||
type RepoContext struct { | |||||
AccessMode models.AccessMode | |||||
IsWatching bool | |||||
IsBranch bool | |||||
IsTag bool | |||||
IsCommit bool | |||||
Repository *models.Repository | |||||
Owner *models.User | |||||
Commit *git.Commit | |||||
Tag *git.Tag | |||||
GitRepo *git.Repository | |||||
BranchName string | |||||
TagName string | |||||
TreeName string | |||||
CommitId string | |||||
RepoLink string | |||||
CloneLink models.CloneLink | |||||
CommitsCount int | |||||
Mirror *models.Mirror | |||||
} | |||||
// Return if the current user has write access for this repository | |||||
func (r RepoContext) IsOwner() bool { | |||||
return r.AccessMode >= models.ACCESS_MODE_WRITE | |||||
} | |||||
// 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. | ||||
func (ctx *Context) HasApiError() bool { | func (ctx *Context) HasApiError() bool { | ||||
hasErr, ok := ctx.Data["HasError"] | hasErr, ok := ctx.Data["HasError"] | ||||
@@ -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. | |||||
if ctx.IsSigned { | |||||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | |||||
if err != nil { | |||||
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 { | |||||
// Check if the user is the same as the repository owner. | |||||
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) { | |||||
u = ctx.User | |||||
} else { | |||||
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,66 +41,36 @@ 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). | |||||
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 { | |||||
ctx.JSON(500, &base.ApiJsonErr{"GetHighestAuthorize: " + err.Error(), base.DOC_URL}) | |||||
return | |||||
} | |||||
if auth == models.ORG_ADMIN { | |||||
ctx.Repo.IsOwner = true | |||||
ctx.Repo.IsAdmin = true | |||||
} | |||||
} | |||||
mode, err := models.AccessLevel(ctx.User, repo) | |||||
if err != nil { | |||||
ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) | |||||
return | |||||
} | } | ||||
// Check access. | |||||
if repo.IsPrivate && !ctx.Repo.IsOwner { | |||||
if ctx.User == nil { | |||||
ctx.Error(404) | |||||
return | |||||
} | |||||
ctx.Repo.AccessMode = mode | |||||
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 | |||||
} | |||||
// Check access. | |||||
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE { | |||||
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. | |||||
if ctx.IsSigned { | |||||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | |||||
if err != nil { | |||||
ctx.Handle(500, "HasAccess", err) | |||||
return | |||||
} | |||||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName) | |||||
} | |||||
if !ctx.Repo.IsTrueOwner { | |||||
// Check if the user is the same as the repository owner | |||||
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) { | |||||
u = ctx.User | |||||
} else { | |||||
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 if redirect { | |||||
ctx.Redirect(setting.AppSubUrl + "/") | |||||
return | |||||
} else { | |||||
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). | |||||
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 { | |||||
ctx.Handle(500, "GetHighestAuthorize", err) | |||||
return | |||||
} | |||||
if auth == models.ORG_ADMIN { | |||||
ctx.Repo.IsOwner = true | |||||
ctx.Repo.IsAdmin = true | |||||
} | |||||
} | |||||
mode, err := models.AccessLevel(ctx.User, repo) | |||||
if err != nil { | |||||
ctx.Handle(500, "AccessLevel", err) | |||||
return | |||||
} | } | ||||
ctx.Repo.AccessMode = mode | |||||
// Check access. | // Check access. | ||||
if repo.IsPrivate && !ctx.Repo.IsOwner { | |||||
if ctx.User == nil { | |||||
ctx.Handle(404, "HasAccess", nil) | |||||
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 | |||||
} | |||||
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE { | |||||
ctx.Handle(404, "no access right", err) | |||||
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["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner | |||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_WRITE | |||||
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", | |||||
[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}) | |||||
levelName := Cfg.Section("log."+mode).Key("LEVEL").In( | |||||
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 { | |||||
if err = collaRepos[i].GetOwner(); err != nil { | |||||
i := numOwnRepos | |||||
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) { | |||||
repos[j].Permissions.Admin = true | |||||
if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) { | |||||
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 { | |||||
has, err = models.HasAccess(authUsername, username+"/"+reponame, models.WRITABLE) | |||||
if tp == models.ACCESS_MODE_READ { | |||||
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, | |||||
ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil { | |||||
} else if err := models.NewIssueUserPairs(ctx.Repo.Repository, issue.Id, ctx.Repo.Owner.Id, | |||||
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 err != nil { | |||||
ctx.Handle(500, "IsRepositoryExist", err) | |||||
return | |||||
} else if isExist { | |||||
if models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) { | |||||
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, | |||||
Mode: models.WRITABLE}); err != nil { | |||||
ctx.Handle(500, "AddAccess", err) | |||||
if err = ctx.Repo.Repository.AddCollaborator(u); err != nil { | |||||
ctx.Handle(500, "AddCollaborator", 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 | |||||
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 { | |||||
ctx.Handle(500, "GetHighestAuthorize", err) | |||||
return | |||||
} | |||||
if auth > 0 { | |||||
needDelete = false | |||||
} | |||||
u, err := models.GetUserByName(remove) | |||||
if err != nil { | |||||
ctx.Handle(500, "GetUserByName", err) | |||||
return | |||||
} | } | ||||
if needDelete { | |||||
if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil { | |||||
ctx.Handle(500, "DeleteAccess", err) | |||||
return | |||||
} | |||||
if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil { | |||||
ctx.Handle(500, "DeleteCollaborator", err) | |||||
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)) | |||||
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.Data["Collaborators"] = users | |||||
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,10 +103,14 @@ 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, | |||||
models.READABLE); !has { | |||||
continue | |||||
// This prevents having to retrieve the repository for each action | |||||
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 | |||||
} | |||||
} | } | ||||
} | } | ||||
// FIXME: cache results? | // FIXME: cache results? | ||||
u, err := models.GetUserByName(act.ActUserName) | u, err := models.GetUserByName(act.ActUserName) | ||||
@@ -205,10 +215,14 @@ func Profile(ctx *middleware.Context) { | |||||
if !ctx.IsSigned { | if !ctx.IsSigned { | ||||
continue | continue | ||||
} | } | ||||
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, | |||||
models.READABLE); !has { | |||||
continue | |||||
// This prevents having to retrieve the repository for each action | |||||
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 | |||||
} | |||||
} | } | ||||
} | } | ||||
// FIXME: cache results? | // FIXME: cache results? | ||||
u, err := models.GetUserByName(act.ActUserName) | u, err := models.GetUserByName(act.ActUserName) | ||||
@@ -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> | ||||