mirror of https://github.com/go-gitea/gitea.git
Merge branch 'access' of github.com:gogits/gogs into dev
commit
b0b11fd7b1
|
@ -1,6 +1,6 @@
|
|||
[run]
|
||||
init_cmds = [
|
||||
["grep", "-rn", "FIXME", "."],
|
||||
#["grep", "-rn", "FIXME", "."],
|
||||
["./gogs", "web"]
|
||||
]
|
||||
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)
|
||||
|
||||
##### Current version: 0.5.13 Beta
|
||||
##### Current version: 0.5.16 Beta
|
||||
|
||||
### 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.
|
||||
- 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!
|
||||
|
||||
#### Other language version
|
||||
|
@ -57,7 +57,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
|||
## System Requirements
|
||||
|
||||
- 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
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
|
|||
|
||||
![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 内存
|
||||
|
||||
## 安装部署
|
||||
|
||||
|
@ -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) 文件中。
|
||||
|
|
59
cmd/serve.go
59
cmd/serve.go
|
@ -8,7 +8,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -43,7 +42,7 @@ func setup(logPath string) {
|
|||
|
||||
models.LoadModelsConfig()
|
||||
|
||||
if models.UseSQLite3 {
|
||||
if setting.UseSQLite3 {
|
||||
workDir, _ := setting.WorkDir()
|
||||
os.Chdir(workDir)
|
||||
}
|
||||
|
@ -67,33 +66,33 @@ func parseCmd(cmd string) (string, string) {
|
|||
}
|
||||
|
||||
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]
|
||||
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")
|
||||
|
||||
if len(k.Args()) < 1 {
|
||||
if len(c.Args()) < 1 {
|
||||
log.GitLogger.Fatal(2, "Not enough arguments")
|
||||
}
|
||||
keys := strings.Split(k.Args()[0], "-")
|
||||
keys := strings.Split(c.Args()[0], "-")
|
||||
if len(keys) != 2 {
|
||||
println("Gogs: auth file format error")
|
||||
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")
|
||||
if cmd == "" {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -144,9 +144,19 @@ func runServ(k *cli.Context) {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
println("Gogs: internal error:", err.Error())
|
||||
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")
|
||||
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 {
|
||||
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 {
|
||||
println("Gogs: internal error:", err.Error())
|
||||
log.GitLogger.Fatal(2, "Fail to check read access:", err)
|
||||
|
|
|
@ -318,7 +318,7 @@ func runWeb(ctx *cli.Context) {
|
|||
m.Get("/template/*", dev.TemplatePreview)
|
||||
}
|
||||
|
||||
reqTrueOwner := middleware.RequireTrueOwner()
|
||||
reqAdmin := middleware.RequireAdmin()
|
||||
|
||||
// Organization.
|
||||
m.Group("/org", func() {
|
||||
|
@ -393,7 +393,7 @@ func runWeb(ctx *cli.Context) {
|
|||
m.Post("/:name", repo.GitHooksEditPost)
|
||||
}, middleware.GitHookService())
|
||||
})
|
||||
}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
|
||||
}, reqSignIn, middleware.RepoAssignment(true), reqAdmin)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/action/:action", repo.Action)
|
||||
|
|
2
gogs.go
2
gogs.go
|
@ -17,7 +17,7 @@ import (
|
|||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const APP_VER = "0.5.14.0222 Beta"
|
||||
const APP_VER = "0.5.16.0228 Beta"
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
|
216
models/access.go
216
models/access.go
|
@ -5,76 +5,190 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type AccessType int
|
||||
type AccessMode int
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
// DeleteAccess deletes access record.
|
||||
func DeleteAccess(access *Access) error {
|
||||
_, err := x.Delete(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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
access := &Access{
|
||||
UserName: strings.ToLower(uname),
|
||||
RepoName: strings.ToLower(repoName),
|
||||
}
|
||||
has, err := x.Get(access)
|
||||
// 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 {
|
||||
return false, err
|
||||
} else if !has {
|
||||
return false, nil
|
||||
} else if mode > access.Mode {
|
||||
return false, nil
|
||||
return fmt.Errorf("getCollaborators: %v", err)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
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{
|
||||
ActUserId: u.Id,
|
||||
ActUserName: u.Name,
|
||||
ActEmail: u.Email,
|
||||
ActUserId: actUser.Id,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
OpType: TRANSFER_REPO,
|
||||
RepoId: repo.Id,
|
||||
RepoUserName: newUser.Name,
|
||||
RepoUserName: newOwner.Name,
|
||||
RepoName: repo.Name,
|
||||
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.
|
||||
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.
|
||||
|
|
|
@ -282,30 +282,33 @@ type IssueUser struct {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
iu := &IssueUser{
|
||||
IssueId: issueID,
|
||||
RepoId: repo.Id,
|
||||
}
|
||||
|
||||
isNeedAddPoster := true
|
||||
for _, u := range us {
|
||||
for _, u := range users {
|
||||
iu.Uid = u.Id
|
||||
iu.IsPoster = iu.Uid == pid
|
||||
iu.IsPoster = iu.Uid == posterID
|
||||
if isNeedAddPoster && iu.IsPoster {
|
||||
isNeedAddPoster = false
|
||||
}
|
||||
iu.IsAssigned = iu.Uid == aid
|
||||
iu.IsAssigned = iu.Uid == assigneeID
|
||||
if _, err = x.Insert(iu); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if isNeedAddPoster {
|
||||
iu.Uid = pid
|
||||
iu.Uid = posterID
|
||||
iu.IsPoster = true
|
||||
iu.IsAssigned = iu.Uid == aid
|
||||
iu.IsAssigned = iu.Uid == assigneeID
|
||||
if _, err = x.Insert(iu); err != nil {
|
||||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"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
|
||||
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.
|
||||
// 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
|
||||
func Migrate(x *xorm.Engine) error {
|
||||
if err := x.Sync(new(Version)); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("sync: %v", err)
|
||||
}
|
||||
|
||||
currentVersion := &Version{Id: 1}
|
||||
has, err := x.Get(currentVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("get: %v", err)
|
||||
} 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
|
||||
}
|
||||
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
|
||||
|
||||
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
|
||||
if _, err = x.Id(1).Update(currentVersion); err != nil {
|
||||
|
@ -48,6 +105,267 @@ func Migrate(x *xorm.Engine) error {
|
|||
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"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
// "github.com/gogits/gogs/models/migrations"
|
||||
"github.com/gogits/gogs/models/migrations"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -23,12 +24,22 @@ import (
|
|||
type Engine interface {
|
||||
Delete(interface{}) (int64, error)
|
||||
Exec(string, ...interface{}) (sql.Result, error)
|
||||
Find(interface{}, ...interface{}) error
|
||||
Get(interface{}) (bool, error)
|
||||
Insert(...interface{}) (int64, error)
|
||||
InsertOne(interface{}) (int64, error)
|
||||
Id(interface{}) *xorm.Session
|
||||
Sql(string, ...interface{}) *xorm.Session
|
||||
Where(string, ...interface{}) *xorm.Session
|
||||
}
|
||||
|
||||
func sessionRelease(sess *xorm.Session) {
|
||||
if !sess.IsCommitedOrRollbacked {
|
||||
sess.Rollback()
|
||||
}
|
||||
sess.Close()
|
||||
}
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
|
@ -39,24 +50,30 @@ var (
|
|||
}
|
||||
|
||||
EnableSQLite3 bool
|
||||
UseSQLite3 bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
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(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))
|
||||
}
|
||||
|
||||
func LoadModelsConfig() {
|
||||
sec := setting.Cfg.Section("database")
|
||||
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.Name = sec.Key("NAME").String()
|
||||
|
@ -103,6 +120,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
|
|||
return fmt.Errorf("connect to database: %v", err)
|
||||
}
|
||||
|
||||
x.SetMapper(core.GonicMapper{})
|
||||
return x.Sync(tables...)
|
||||
}
|
||||
|
||||
|
@ -112,6 +130,8 @@ func SetEngine() (err error) {
|
|||
return fmt.Errorf("connect to database: %v", err)
|
||||
}
|
||||
|
||||
x.SetMapper(core.GonicMapper{})
|
||||
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
||||
|
@ -136,13 +156,14 @@ func NewEngine() (err error) {
|
|||
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 {
|
||||
return fmt.Errorf("sync database struct error: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
653
models/org.go
653
models/org.go
File diff suppressed because it is too large
Load Diff
778
models/repo.go
778
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))
|
||||
for i, ou := range ous {
|
||||
u.Orgs[i], err = GetUserById(ou.OrgId)
|
||||
u.Orgs[i], err = GetUserById(ou.OrgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -398,63 +398,7 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
|||
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.
|
||||
|
@ -527,7 +471,7 @@ func DeleteUser(u *User) error {
|
|||
return err
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// Delete all alternative email addresses
|
||||
|
@ -570,8 +514,7 @@ func UserPath(userName string) string {
|
|||
|
||||
func GetUserByKeyId(keyId int64) (*User, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
|
@ -580,10 +523,9 @@ func GetUserByKeyId(keyId int64) (*User, error) {
|
|||
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)
|
||||
has, err := x.Id(id).Get(u)
|
||||
has, err := e.Id(id).Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
|
@ -592,6 +534,11 @@ func GetUserById(id int64) (*User, error) {
|
|||
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.
|
||||
func GetUserByName(name string) (*User, error) {
|
||||
if len(name) == 0 {
|
||||
|
@ -913,7 +860,7 @@ func UpdateMentions(userNames []string, issueId int64) error {
|
|||
}
|
||||
|
||||
for _, orgUser := range orgUsers {
|
||||
tempIds = append(tempIds, orgUser.Id)
|
||||
tempIds = append(tempIds, orgUser.ID)
|
||||
}
|
||||
|
||||
ids = append(ids, tempIds...)
|
||||
|
|
|
@ -164,7 +164,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
|||
},
|
||||
"DiffTypeToStr": DiffTypeToStr,
|
||||
"DiffLineTypeToStr": DiffLineTypeToStr,
|
||||
"Sha1": Sha1,
|
||||
"Sha1": Sha1,
|
||||
"ShortSha": ShortSha,
|
||||
"Md5": EncodeMd5,
|
||||
"ActionContent2Commits": ActionContent2Commits,
|
||||
|
|
|
@ -38,29 +38,7 @@ type Context struct {
|
|||
IsSigned 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 {
|
||||
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.
|
||||
func (ctx *Context) HasApiError() bool {
|
||||
hasErr, ok := ctx.Data["HasError"]
|
||||
|
|
|
@ -87,7 +87,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
|||
return
|
||||
}
|
||||
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
|
||||
if requireAdminTeam && !ctx.Org.IsAdminTeam {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -29,17 +28,10 @@ func ApiRepoAssignment() macaron.Handler {
|
|||
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)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
|
@ -49,66 +41,36 @@ func ApiRepoAssignment() macaron.Handler {
|
|||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
u = ctx.User
|
||||
}
|
||||
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.
|
||||
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
||||
if err != nil {
|
||||
if err == models.ErrRepoNotExist {
|
||||
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
|
||||
} else if err = repo.GetOwner(); err != nil {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
|
||||
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
|
||||
}
|
||||
|
||||
ctx.Repo.AccessMode = mode
|
||||
|
||||
// Check access.
|
||||
if repo.IsPrivate && !ctx.Repo.IsOwner {
|
||||
if ctx.User == nil {
|
||||
ctx.Error(404)
|
||||
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
|
||||
}
|
||||
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
ctx.Repo.HasAccess = true
|
||||
|
||||
ctx.Repo.Repository = repo
|
||||
}
|
||||
|
@ -242,101 +204,49 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
|||
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)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
ctx.Handle(404, "GetUserByName", err)
|
||||
} else if redirect {
|
||||
log.Error(4, "GetUserByName", err)
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
} else {
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
}
|
||||
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
|
||||
|
||||
// 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.
|
||||
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
||||
if err != nil {
|
||||
if err == models.ErrRepoNotExist {
|
||||
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
|
||||
} else if err = repo.GetOwner(); err != nil {
|
||||
ctx.Handle(500, "GetOwner", err)
|
||||
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.
|
||||
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
|
||||
|
||||
if repo.IsMirror {
|
||||
|
@ -383,8 +293,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
|||
ctx.Data["Title"] = u.Name + "/" + repo.Name
|
||||
ctx.Data["Repository"] = repo
|
||||
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.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) {
|
||||
if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
|
||||
if ctx.Repo.AccessMode < models.ACCESS_MODE_ADMIN {
|
||||
if !ctx.IsSigned {
|
||||
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/login")
|
||||
|
|
|
@ -67,6 +67,11 @@ var (
|
|||
CookieRememberName string
|
||||
ReverseProxyAuthUser string
|
||||
|
||||
// Database settings.
|
||||
UseSQLite3 bool
|
||||
UseMySQL bool
|
||||
UsePostgreSQL bool
|
||||
|
||||
// Webhook settings.
|
||||
Webhook struct {
|
||||
TaskInterval int
|
||||
|
@ -267,10 +272,6 @@ func NewConfigContext() {
|
|||
"StampNano": time.StampNano,
|
||||
}[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()
|
||||
curUser := os.Getenv("USER")
|
||||
if len(curUser) == 0 {
|
||||
|
@ -293,9 +294,6 @@ func NewConfigContext() {
|
|||
} else {
|
||||
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")
|
||||
|
||||
sec = Cfg.Section("picture")
|
||||
|
@ -304,7 +302,6 @@ func NewConfigContext() {
|
|||
if !filepath.IsAbs(AvatarUploadPath) {
|
||||
AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
|
||||
}
|
||||
os.MkdirAll(AvatarUploadPath, os.ModePerm)
|
||||
switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") {
|
||||
case "duoshuo":
|
||||
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
||||
|
@ -369,9 +366,11 @@ func newLogService() {
|
|||
log.Fatal(4, "Unknown log mode: %s", mode)
|
||||
}
|
||||
|
||||
validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
|
||||
// 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]
|
||||
if !ok {
|
||||
log.Fatal(4, "Unknown log level: %s", levelName)
|
||||
|
|
|
@ -238,28 +238,31 @@ func ListMyRepos(ctx *middleware.Context) {
|
|||
}
|
||||
numOwnRepos := len(ownRepos)
|
||||
|
||||
collaRepos, err := models.GetCollaborativeRepos(ctx.User.Name)
|
||||
accessibleRepos, err := ctx.User.GetAccessibleRepositories()
|
||||
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
|
||||
}
|
||||
|
||||
repos := make([]*api.Repository, numOwnRepos+len(collaRepos))
|
||||
repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
|
||||
for i := range ownRepos {
|
||||
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})
|
||||
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?
|
||||
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)
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func GetRepoRawFile(ctx *middleware.Context) {
|
||||
if ctx.Repo.Repository.IsPrivate && !ctx.Repo.HasAccess {
|
||||
if !ctx.Repo.HasAccess() {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
|
|||
cfg.Section("session").Key("PROVIDER").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("SECRET_KEY").SetValue(base.GetRandomString(15))
|
||||
|
|
|
@ -165,14 +165,14 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
|
|||
}
|
||||
|
||||
// Validate permission level.
|
||||
var auth models.AuthorizeType
|
||||
var auth models.AccessMode
|
||||
switch form.Permission {
|
||||
case "read":
|
||||
auth = models.ORG_READABLE
|
||||
auth = models.ACCESS_MODE_READ
|
||||
case "write":
|
||||
auth = models.ORG_WRITABLE
|
||||
auth = models.ACCESS_MODE_WRITE
|
||||
case "admin":
|
||||
auth = models.ORG_ADMIN
|
||||
auth = models.ACCESS_MODE_ADMIN
|
||||
default:
|
||||
ctx.Error(401)
|
||||
return
|
||||
|
@ -181,7 +181,7 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
|
|||
org := ctx.Org.Organization
|
||||
|
||||
t := &models.Team{
|
||||
OrgId: org.Id,
|
||||
OrgID: org.Id,
|
||||
Name: form.TeamName,
|
||||
Description: form.Description,
|
||||
Authorize: auth,
|
||||
|
@ -246,14 +246,14 @@ func EditTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
|
|||
isAuthChanged := false
|
||||
if !t.IsOwnerTeam() {
|
||||
// Validate permission level.
|
||||
var auth models.AuthorizeType
|
||||
var auth models.AccessMode
|
||||
switch form.Permission {
|
||||
case "read":
|
||||
auth = models.ORG_READABLE
|
||||
auth = models.ACCESS_MODE_READ
|
||||
case "write":
|
||||
auth = models.ORG_WRITABLE
|
||||
auth = models.ACCESS_MODE_WRITE
|
||||
case "admin":
|
||||
auth = models.ORG_ADMIN
|
||||
auth = models.ACCESS_MODE_ADMIN
|
||||
default:
|
||||
ctx.Error(401)
|
||||
return
|
||||
|
|
|
@ -131,18 +131,18 @@ func Http(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
if !isPublicPull {
|
||||
var tp = models.WRITABLE
|
||||
var tp = models.ACCESS_MODE_WRITE
|
||||
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 {
|
||||
ctx.Handle(401, "no basic auth and digit auth", nil)
|
||||
return
|
||||
} 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 {
|
||||
ctx.Handle(401, "no basic auth and digit auth", nil)
|
||||
return
|
||||
|
@ -152,6 +152,11 @@ func Http(ctx *middleware.Context) {
|
|||
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
|
||||
}
|
||||
|
||||
us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
|
||||
us, err := ctx.Repo.Repository.GetCollaborators()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
|
||||
return
|
||||
|
@ -218,7 +218,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
|
||||
_, err = ctx.Repo.Repository.GetCollaborators()
|
||||
if err != nil {
|
||||
send(500, nil, err)
|
||||
return
|
||||
|
@ -230,7 +230,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
|
|||
}
|
||||
|
||||
// Only collaborators can assign.
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
form.AssigneeId = 0
|
||||
}
|
||||
issue := &models.Issue{
|
||||
|
@ -246,8 +246,8 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
|
|||
if err := models.NewIssue(issue); err != nil {
|
||||
send(500, nil, err)
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ func ViewIssue(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
|
||||
return
|
||||
|
@ -434,7 +434,7 @@ func ViewIssue(ctx *middleware.Context) {
|
|||
ctx.Data["Title"] = issue.Name
|
||||
ctx.Data["Issue"] = issue
|
||||
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["IsRepoToolbarIssuesList"] = false
|
||||
ctx.HTML(200, ISSUE_VIEW)
|
||||
|
@ -457,7 +457,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner {
|
||||
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
|
@ -484,7 +484,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
|
|||
}
|
||||
|
||||
func UpdateIssueLabel(ctx *middleware.Context) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
|
@ -561,7 +561,7 @@ func UpdateIssueLabel(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
func UpdateIssueMilestone(ctx *middleware.Context) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
|
@ -607,7 +607,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
func UpdateAssignee(ctx *middleware.Context) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
|
@ -753,7 +753,7 @@ func Comment(ctx *middleware.Context) {
|
|||
|
||||
// Check if issue owner changes the status of issue.
|
||||
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")
|
||||
}
|
||||
if len(newStatus) > 0 {
|
||||
|
|
|
@ -41,7 +41,7 @@ func Releases(ctx *middleware.Context) {
|
|||
tags := make([]*models.Release, len(rawTags))
|
||||
for i, rawTag := range rawTags {
|
||||
for j, rel := range rels {
|
||||
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner) {
|
||||
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner()) {
|
||||
continue
|
||||
}
|
||||
if rel.TagName == rawTag {
|
||||
|
@ -140,7 +140,7 @@ func Releases(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
func NewRelease(ctx *middleware.Context) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Handle(403, "release.ReleasesNew", nil)
|
||||
return
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ func NewRelease(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Handle(403, "release.ReleasesNew", nil)
|
||||
return
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
|||
}
|
||||
|
||||
func EditRelease(ctx *middleware.Context) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Handle(403, "release.ReleasesEdit", nil)
|
||||
return
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ func EditRelease(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Handle(403, "release.EditReleasePost", nil)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -349,7 +349,7 @@ func Action(ctx *middleware.Context) {
|
|||
case "unstar":
|
||||
err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
|
||||
case "desc":
|
||||
if !ctx.Repo.IsOwner {
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -54,15 +53,11 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
|
|||
newRepoName := form.RepoName
|
||||
// Check if repository name has been changed.
|
||||
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.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, nil)
|
||||
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 {
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
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["PageIsSettingsCollaboration"] = true
|
||||
|
||||
repoLink := path.Join(ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName)
|
||||
|
||||
if ctx.Req.Method == "POST" {
|
||||
name := strings.ToLower(ctx.Query("collaborator"))
|
||||
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
|
||||
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -204,9 +189,8 @@ func SettingsCollaboration(ctx *middleware.Context) {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -225,50 +209,27 @@ func SettingsCollaboration(ctx *middleware.Context) {
|
|||
// Delete collaborator.
|
||||
remove := strings.ToLower(ctx.Query("remove"))
|
||||
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.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
||||
return
|
||||
}
|
||||
|
||||
names, err := models.GetCollaboratorNames(repoLink)
|
||||
users, err := ctx.Repo.Repository.GetCollaborators()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCollaborators", err)
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,13 +49,19 @@ func Dashboard(ctx *middleware.Context) {
|
|||
} else {
|
||||
// Normal user.
|
||||
ctxUser = ctx.User
|
||||
collaborates, err := models.GetCollaborativeRepos(ctxUser.Name)
|
||||
collaborates, err := ctx.User.GetAccessibleRepositories()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCollaborativeRepos", err)
|
||||
ctx.Handle(500, "GetAccessibleRepositories", err)
|
||||
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
|
||||
|
||||
|
@ -97,10 +103,14 @@ func Dashboard(ctx *middleware.Context) {
|
|||
feeds := make([]*models.Action, 0, len(actions))
|
||||
for _, act := range actions {
|
||||
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?
|
||||
u, err := models.GetUserByName(act.ActUserName)
|
||||
|
@ -205,10 +215,14 @@ func Profile(ctx *middleware.Context) {
|
|||
if !ctx.IsSigned {
|
||||
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?
|
||||
u, err := models.GetUserByName(act.ActUserName)
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.5.14.0222 Beta
|
||||
0.5.16.0228 Beta
|
|
@ -1,7 +1,7 @@
|
|||
</div>
|
||||
<footer id="footer">
|
||||
<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>
|
||||
|
||||
<div class="right" id="footer-links">
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
<div id="org-repo-list">
|
||||
{{range .Repos}}
|
||||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser.Name)}}
|
||||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}}
|
||||
<div class="org-repo-item">
|
||||
<ul class="org-repo-status right">
|
||||
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
</a>
|
||||
</li>
|
||||
<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">
|
||||
<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
|
||||
<span class="num">{{.NumForks}}</span>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<!-- <li>
|
||||
<a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a>
|
||||
</li> -->
|
||||
{{if .IsRepositoryTrueOwner}}
|
||||
{{if .IsRepositoryAdmin}}
|
||||
<li class="border-bottom"></li>
|
||||
<li>
|
||||
<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="#">Network</a></li>
|
||||
</ul>
|
||||
</li> -->{{end}}{{if .IsRepositoryTrueOwner}}
|
||||
</li> -->{{end}}{{if .IsRepositoryAdmin}}
|
||||
<li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a>
|
||||
</li>{{end}}
|
||||
</ul>
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<div class="tab-pane active">
|
||||
<div id="org-repo-list">
|
||||
{{range .Repos}}
|
||||
{{if or (not .IsPrivate) (.HasAccess $.SignedUserName)}}
|
||||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}}
|
||||
<div class="org-repo-item">
|
||||
<ul class="org-repo-status right">
|
||||
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
|
||||
|
|
Loading…
Reference in New Issue