Merge branch 'master' of https://github.com/go-gitea/gitea
This commit is contained in:
commit
ed4a47dcc8
6
Makefile
6
Makefile
|
|
@ -183,15 +183,15 @@ test-pgsql: integrations.test generate-ini
|
|||
|
||||
.PHONY: bench-sqlite
|
||||
bench-sqlite: integrations.sqlite.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mysql
|
||||
bench-mysql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.test -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-pgsql
|
||||
bench-pgsql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.test -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
|
||||
.PHONY: integration-test-coverage
|
||||
|
|
|
|||
23
cmd/web.go
23
cmd/web.go
|
|
@ -51,6 +51,26 @@ and it takes care of all the other things for you`,
|
|||
},
|
||||
}
|
||||
|
||||
func runHTTPRedirector() {
|
||||
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
|
||||
dest := strings.TrimSuffix(setting.AppURL, "/")
|
||||
log.Info("Redirecting: %s to %s", source, dest)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
target := dest + r.URL.Path
|
||||
if len(r.URL.RawQuery) > 0 {
|
||||
target += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
var err = runHTTP(source, context2.ClearHandler(handler))
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Failed to start port redirection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
|
|
@ -124,6 +144,9 @@ func runWeb(ctx *cli.Context) error {
|
|||
case setting.HTTP:
|
||||
err = runHTTP(listenAddr, context2.ClearHandler(m))
|
||||
case setting.HTTPS:
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
}
|
||||
err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
case setting.FCGI:
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ SCRIPT_TYPE = bash
|
|||
ANSI_CHARSET =
|
||||
; Force every new repository to be private
|
||||
FORCE_PRIVATE = false
|
||||
; Default private when create a new repository, could be: last, private, public. Default is last which means last user repo visiblity.
|
||||
DEFAULT_PRIVATE = last
|
||||
; Global maximum creation limit of repository per user, -1 means no limit
|
||||
MAX_CREATION_LIMIT = -1
|
||||
; Mirror sync queue length, increase if mirror syncing starts hanging
|
||||
|
|
@ -107,6 +109,12 @@ ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
|||
; Listen address. Either a IPv4/IPv6 address or the path to a unix socket.
|
||||
HTTP_ADDR = 0.0.0.0
|
||||
HTTP_PORT = 3000
|
||||
; If REDIRECT_OTHER_PORT is true, and PROTOCOL is set to https an http server
|
||||
; will be started on PORT_TO_REDIRECT and redirect request to the main
|
||||
; ROOT_URL. Defaults are false for REDIRECT_OTHER_PORT and 80 for
|
||||
; PORT_TO_REDIRECT.
|
||||
REDIRECT_OTHER_PORT = false
|
||||
PORT_TO_REDIRECT = 80
|
||||
; Permission for unix socket
|
||||
UNIX_SOCKET_PERMISSION = 666
|
||||
; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
- `SCRIPT_TYPE`: The script type your server supports, usually this is `bash`, but some customers report that they only have `sh`.
|
||||
- `ANSI_CHARSET`: The default charset for an unrecognized charset.
|
||||
- `FORCE_PRIVATE`: Force every new repository to be private.
|
||||
- `DEFAULT_PRIVATE`: Default private when create a new repository, could be: `last`, `private` and `public`. Default is last which means last user repo visiblity.
|
||||
- `MAX_CREATION_LIMIT`: Global maximum creation limit of repositories per user, `-1` means no limit.
|
||||
- `PULL_REQUEST_QUEUE_LENGTH`:exclamation:: Length of pull request patch test queue, make it as large as possible.
|
||||
- `MIRROR_QUEUE_LENGTH`: Patch test queue length, increase if pull request patch testing starts hanging. Defaults to 1000.
|
||||
|
|
@ -86,6 +87,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
- `LFS_START_SERVER`: Enables git-lfs support. `true` or `false`, default is `false`.
|
||||
- `LFS_CONTENT_PATH`: Where your lfs files put on, default is `data/lfs`.
|
||||
- `LFS_JWT_SECRET`: LFS authentication secret, changed this to yourself.
|
||||
- `REDIRECT_OTHER_PORT`: If true and `PROTOCOL` is https, redirects http requests on another port to `ROOT_URL`, default is `false`.
|
||||
- `PORT_TO_REDIRECT`: Port used when `REDIRECT_OTHER_PORT` is true, default is `80`.
|
||||
|
||||
## Database (`database`)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ menu:
|
|||
- `SCRIPT_TYPE`: 服务器支持的Shell类型,通常是 `bash`,但有些服务器也有可能是 `sh`。
|
||||
- `ANSI_CHARSET`: 默认字符编码。
|
||||
- `FORCE_PRIVATE`: 强制所有git工程必须私有。
|
||||
- `DEFAULT_PRIVATE`: 默认创建的git工程为私有。 可以是`last`, `private` 或 `public`。默认值是 `last`表示用户最后创建的Repo的选择。
|
||||
- `MAX_CREATION_LIMIT`: 全局最大每个用户创建的git工程数目, `-1` 表示没限制。
|
||||
- `PULL_REQUEST_QUEUE_LENGTH`: 小心:合并请求测试队列的长度,尽量放大。
|
||||
|
||||
|
|
|
|||
113
integrations/benchmarks_test.go
Normal file
113
integrations/benchmarks_test.go
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2017 The Gitea 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 integrations
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
api "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func BenchmarkRepo(b *testing.B) {
|
||||
samples := []struct {
|
||||
url string
|
||||
name string
|
||||
skipShort bool
|
||||
}{
|
||||
{url: "https://github.com/go-gitea/gitea.git", name: "gitea"},
|
||||
{url: "https://github.com/ethantkoenig/manyfiles.git", name: "manyfiles"},
|
||||
{url: "https://github.com/moby/moby.git", name: "moby", skipShort: true},
|
||||
{url: "https://github.com/golang/go.git", name: "go", skipShort: true},
|
||||
{url: "https://github.com/torvalds/linux.git", name: "linux", skipShort: true},
|
||||
}
|
||||
prepareTestEnv(b)
|
||||
session := loginUser(b, "user2")
|
||||
b.ResetTimer()
|
||||
|
||||
for _, s := range samples {
|
||||
b.Run(s.name, func(b *testing.B) {
|
||||
if testing.Short() && s.skipShort {
|
||||
b.Skip("skipping test in short mode.")
|
||||
}
|
||||
b.Run("Migrate", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testRepoMigrate(b, session, s.url, s.name)
|
||||
}
|
||||
})
|
||||
b.Run("Access", func(b *testing.B) {
|
||||
var branches []*api.Branch
|
||||
b.Run("APIBranchList", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := NewRequestf(b, "GET", "/api/v1/repos/%s/%s/branches", "user2", s.name)
|
||||
resp := session.MakeRequest(b, req, http.StatusOK)
|
||||
b.StopTimer()
|
||||
if len(branches) == 0 {
|
||||
DecodeJSON(b, resp, &branches) //Store for next phase
|
||||
}
|
||||
b.StartTimer()
|
||||
}
|
||||
})
|
||||
branchCount := len(branches)
|
||||
b.Run("WebViewCommit", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := NewRequestf(b, "GET", "/%s/%s/commit/%s", "user2", s.name, branches[i%branchCount].Commit.ID)
|
||||
session.MakeRequest(b, req, http.StatusOK)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//StringWithCharset random string (from https://www.calhoun.io/creating-random-strings-in-go/)
|
||||
func StringWithCharset(length int, charset string) string {
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func BenchmarkRepoBranchCommit(b *testing.B) {
|
||||
samples := []int64{1, 3, 15, 16}
|
||||
prepareTestEnv(b)
|
||||
b.ResetTimer()
|
||||
|
||||
for _, repoID := range samples {
|
||||
b.StopTimer()
|
||||
repo := models.AssertExistsAndLoadBean(b, &models.Repository{ID: repoID}).(*models.Repository)
|
||||
b.StartTimer()
|
||||
b.Run(repo.Name, func(b *testing.B) {
|
||||
owner := models.AssertExistsAndLoadBean(b, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||
session := loginUser(b, owner.LoginName)
|
||||
b.ResetTimer()
|
||||
b.Run("Create", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
branchName := StringWithCharset(5+rand.Intn(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
b.StartTimer()
|
||||
testCreateBranch(b, session, owner.LoginName, repo.Name, "branch/master", branchName, http.StatusFound)
|
||||
}
|
||||
})
|
||||
b.Run("Access", func(b *testing.B) {
|
||||
var branches []*api.Branch
|
||||
req := NewRequestf(b, "GET", "/api/v1/%s/branches", repo.FullName())
|
||||
resp := session.MakeRequest(b, req, http.StatusOK)
|
||||
DecodeJSON(b, resp, &branches)
|
||||
branchCount := len(branches)
|
||||
b.ResetTimer() //We measure from here
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := NewRequestf(b, "GET", "/%s/%s/commits/%s", owner.Name, repo.Name, branches[i%branchCount])
|
||||
session.MakeRequest(b, req, http.StatusOK)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//TODO list commits /repos/{owner}/{repo}/commits
|
||||
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string {
|
||||
func testCreateBranch(t testing.TB, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string {
|
||||
var csrf string
|
||||
if expectedStatus == http.StatusNotFound {
|
||||
csrf = GetCSRF(t, session, path.Join(user, repo, "src/branch/master"))
|
||||
|
|
|
|||
|
|
@ -40,29 +40,3 @@ func TestRepoMigrate(t *testing.T) {
|
|||
session := loginUser(t, "user2")
|
||||
testRepoMigrate(t, session, "https://github.com/go-gitea/git.git", "git")
|
||||
}
|
||||
|
||||
func BenchmarkRepoMigrate(b *testing.B) {
|
||||
samples := []struct {
|
||||
url string
|
||||
name string
|
||||
}{
|
||||
{url: "https://github.com/go-gitea/gitea.git", name: "gitea"},
|
||||
{url: "https://github.com/ethantkoenig/manyfiles.git", name: "manyfiles"},
|
||||
{url: "https://github.com/moby/moby.git", name: "moby"},
|
||||
{url: "https://github.com/golang/go.git", name: "go"},
|
||||
{url: "https://github.com/torvalds/linux.git", name: "linux"},
|
||||
}
|
||||
|
||||
prepareTestEnv(b)
|
||||
session := loginUser(b, "user2")
|
||||
b.ResetTimer()
|
||||
|
||||
for _, s := range samples {
|
||||
b.Run(s.name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testRepoMigrate(b, session, s.url, s.name)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,19 +135,28 @@ func DeleteAttachment(a *Attachment, remove bool) error {
|
|||
|
||||
// DeleteAttachments deletes the given attachments and optionally the associated files.
|
||||
func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
|
||||
for i, a := range attachments {
|
||||
if remove {
|
||||
if len(attachments) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var ids = make([]int64, 0, len(attachments))
|
||||
for _, a := range attachments {
|
||||
ids = append(ids, a.ID)
|
||||
}
|
||||
|
||||
cnt, err := x.In("id", ids).NoAutoCondition().Delete(attachments[0])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if remove {
|
||||
for i, a := range attachments {
|
||||
if err := os.Remove(a.LocalPath()); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := x.Delete(a); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
return len(attachments), nil
|
||||
return int(cnt), nil
|
||||
}
|
||||
|
||||
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@
|
|||
content: content for the fourth issue
|
||||
is_closed: true
|
||||
is_pull: false
|
||||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
|
||||
-
|
||||
id: 5
|
||||
|
|
@ -57,6 +59,9 @@
|
|||
content: content for the fifth issue
|
||||
is_closed: true
|
||||
is_pull: false
|
||||
created_unix: 946684840
|
||||
updated_unix: 978307200
|
||||
|
||||
-
|
||||
id: 6
|
||||
repo_id: 3
|
||||
|
|
@ -68,5 +73,5 @@
|
|||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 946684800
|
||||
created_unix: 946684850
|
||||
updated_unix: 978307200
|
||||
|
|
|
|||
122
models/issue.go
122
models/issue.go
|
|
@ -10,14 +10,15 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
api "code.gitea.io/sdk/gitea"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
api "code.gitea.io/sdk/gitea"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/builder"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
// Issue represents an issue or pull request of repository.
|
||||
|
|
@ -1022,12 +1023,11 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
|
|||
|
||||
// IssuesOptions represents options of an issue.
|
||||
type IssuesOptions struct {
|
||||
RepoID int64
|
||||
RepoIDs []int64 // include all repos if empty
|
||||
AssigneeID int64
|
||||
PosterID int64
|
||||
MentionedID int64
|
||||
MilestoneID int64
|
||||
RepoIDs []int64
|
||||
Page int
|
||||
PageSize int
|
||||
IsClosed util.OptionalBool
|
||||
|
|
@ -1073,9 +1073,7 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
|
|||
sess.In("issue.id", opts.IssueIDs)
|
||||
}
|
||||
|
||||
if opts.RepoID > 0 {
|
||||
sess.And("issue.repo_id=?", opts.RepoID)
|
||||
} else if len(opts.RepoIDs) > 0 {
|
||||
if len(opts.RepoIDs) > 0 {
|
||||
// In case repository IDs are provided but actually no repository has issue.
|
||||
sess.In("issue.repo_id", opts.RepoIDs)
|
||||
}
|
||||
|
|
@ -1339,58 +1337,92 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
|
|||
return stats, err
|
||||
}
|
||||
|
||||
// UserIssueStatsOptions contains parameters accepted by GetUserIssueStats.
|
||||
type UserIssueStatsOptions struct {
|
||||
UserID int64
|
||||
RepoID int64
|
||||
UserRepoIDs []int64
|
||||
FilterMode int
|
||||
IsPull bool
|
||||
IsClosed bool
|
||||
}
|
||||
|
||||
// GetUserIssueStats returns issue statistic information for dashboard by given conditions.
|
||||
func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPull bool) *IssueStats {
|
||||
func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
||||
var err error
|
||||
stats := &IssueStats{}
|
||||
|
||||
countSession := func(isClosed, isPull bool, repoID int64, repoIDs []int64) *xorm.Session {
|
||||
sess := x.
|
||||
Where("issue.is_closed = ?", isClosed).
|
||||
And("issue.is_pull = ?", isPull)
|
||||
|
||||
if repoID > 0 {
|
||||
sess.And("repo_id = ?", repoID)
|
||||
} else if len(repoIDs) > 0 {
|
||||
sess.In("repo_id", repoIDs)
|
||||
}
|
||||
|
||||
return sess
|
||||
cond := builder.NewCond()
|
||||
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull})
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID})
|
||||
}
|
||||
|
||||
stats.AssignCount, _ = countSession(false, isPull, repoID, nil).
|
||||
And("assignee_id = ?", uid).
|
||||
Count(new(Issue))
|
||||
|
||||
stats.CreateCount, _ = countSession(false, isPull, repoID, nil).
|
||||
And("poster_id = ?", uid).
|
||||
Count(new(Issue))
|
||||
|
||||
stats.YourRepositoriesCount, _ = countSession(false, isPull, repoID, repoIDs).
|
||||
Count(new(Issue))
|
||||
|
||||
switch filterMode {
|
||||
switch opts.FilterMode {
|
||||
case FilterModeAll:
|
||||
stats.OpenCount, _ = countSession(false, isPull, repoID, repoIDs).
|
||||
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
|
||||
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
|
||||
Count(new(Issue))
|
||||
stats.ClosedCount, _ = countSession(true, isPull, repoID, repoIDs).
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
|
||||
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
|
||||
Count(new(Issue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case FilterModeAssign:
|
||||
stats.OpenCount, _ = countSession(false, isPull, repoID, nil).
|
||||
And("assignee_id = ?", uid).
|
||||
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
|
||||
And("assignee_id = ?", opts.UserID).
|
||||
Count(new(Issue))
|
||||
stats.ClosedCount, _ = countSession(true, isPull, repoID, nil).
|
||||
And("assignee_id = ?", uid).
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
|
||||
And("assignee_id = ?", opts.UserID).
|
||||
Count(new(Issue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case FilterModeCreate:
|
||||
stats.OpenCount, _ = countSession(false, isPull, repoID, nil).
|
||||
And("poster_id = ?", uid).
|
||||
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
|
||||
And("poster_id = ?", opts.UserID).
|
||||
Count(new(Issue))
|
||||
stats.ClosedCount, _ = countSession(true, isPull, repoID, nil).
|
||||
And("poster_id = ?", uid).
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
|
||||
And("poster_id = ?", opts.UserID).
|
||||
Count(new(Issue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return stats
|
||||
cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
|
||||
stats.AssignCount, err = x.Where(cond).
|
||||
And("assignee_id = ?", opts.UserID).
|
||||
Count(new(Issue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats.CreateCount, err = x.Where(cond).
|
||||
And("poster_id = ?", opts.UserID).
|
||||
Count(new(Issue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats.YourRepositoriesCount, err = x.Where(cond).
|
||||
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
|
||||
Count(new(Issue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetRepoIssueStats returns number of open and closed repository issues by given filter mode.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func populateIssueIndexer() error {
|
|||
}
|
||||
for _, repo := range repos {
|
||||
issues, err := Issues(&IssuesOptions{
|
||||
RepoID: repo.ID,
|
||||
RepoIDs: []int64{repo.ID},
|
||||
IsClosed: util.OptionalBoolNone,
|
||||
IsPull: util.OptionalBoolNone,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -168,3 +168,114 @@ func TestUpdateIssueCols(t *testing.T) {
|
|||
assert.EqualValues(t, prevContent, updatedIssue.Content)
|
||||
AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
|
||||
}
|
||||
|
||||
func TestIssues(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
for _, test := range []struct {
|
||||
Opts IssuesOptions
|
||||
ExpectedIssueIDs []int64
|
||||
}{
|
||||
{
|
||||
IssuesOptions{
|
||||
AssigneeID: 1,
|
||||
SortType: "oldest",
|
||||
},
|
||||
[]int64{1, 6},
|
||||
},
|
||||
{
|
||||
IssuesOptions{
|
||||
RepoIDs: []int64{1, 3},
|
||||
SortType: "oldest",
|
||||
Page: 1,
|
||||
PageSize: 4,
|
||||
},
|
||||
[]int64{1, 2, 3, 5},
|
||||
},
|
||||
{
|
||||
IssuesOptions{
|
||||
Labels: "1,2",
|
||||
Page: 1,
|
||||
PageSize: 4,
|
||||
},
|
||||
[]int64{5, 2, 1},
|
||||
},
|
||||
} {
|
||||
issues, err := Issues(&test.Opts)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
|
||||
for i, issue := range issues {
|
||||
assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserIssueStats(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
for _, test := range []struct {
|
||||
Opts UserIssueStatsOptions
|
||||
ExpectedIssueStats IssueStats
|
||||
}{
|
||||
{
|
||||
UserIssueStatsOptions{
|
||||
UserID: 1,
|
||||
RepoID: 1,
|
||||
FilterMode: FilterModeAll,
|
||||
},
|
||||
IssueStats{
|
||||
YourRepositoriesCount: 0,
|
||||
AssignCount: 1,
|
||||
CreateCount: 1,
|
||||
OpenCount: 0,
|
||||
ClosedCount: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
UserIssueStatsOptions{
|
||||
UserID: 1,
|
||||
FilterMode: FilterModeAssign,
|
||||
},
|
||||
IssueStats{
|
||||
YourRepositoriesCount: 0,
|
||||
AssignCount: 2,
|
||||
CreateCount: 2,
|
||||
OpenCount: 2,
|
||||
ClosedCount: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
UserIssueStatsOptions{
|
||||
UserID: 1,
|
||||
FilterMode: FilterModeCreate,
|
||||
},
|
||||
IssueStats{
|
||||
YourRepositoriesCount: 0,
|
||||
AssignCount: 2,
|
||||
CreateCount: 2,
|
||||
OpenCount: 2,
|
||||
ClosedCount: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
UserIssueStatsOptions{
|
||||
UserID: 2,
|
||||
UserRepoIDs: []int64{1, 2},
|
||||
FilterMode: FilterModeAll,
|
||||
IsClosed: true,
|
||||
},
|
||||
IssueStats{
|
||||
YourRepositoriesCount: 2,
|
||||
AssignCount: 0,
|
||||
CreateCount: 2,
|
||||
OpenCount: 1,
|
||||
ClosedCount: 2,
|
||||
},
|
||||
},
|
||||
} {
|
||||
stats, err := GetUserIssueStats(test.Opts)
|
||||
if !assert.NoError(t, err) {
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, test.ExpectedIssueStats, *stats)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ var (
|
|||
)
|
||||
|
||||
// IsOwnedBy returns true if given user is in the owner team.
|
||||
func (org *User) IsOwnedBy(uid int64) bool {
|
||||
func (org *User) IsOwnedBy(uid int64) (bool, error) {
|
||||
return IsOrganizationOwner(org.ID, uid)
|
||||
}
|
||||
|
||||
// IsOrgMember returns true if given user is member of organization.
|
||||
func (org *User) IsOrgMember(uid int64) bool {
|
||||
return org.IsOrganization() && IsOrganizationMember(org.ID, uid)
|
||||
func (org *User) IsOrgMember(uid int64) (bool, error) {
|
||||
return IsOrganizationMember(org.ID, uid)
|
||||
}
|
||||
|
||||
func (org *User) getTeam(e Engine, name string) (*Team, error) {
|
||||
|
|
@ -285,32 +285,32 @@ type OrgUser struct {
|
|||
}
|
||||
|
||||
// IsOrganizationOwner returns true if given user is in the owner team.
|
||||
func IsOrganizationOwner(orgID, uid int64) bool {
|
||||
has, _ := x.
|
||||
func IsOrganizationOwner(orgID, uid int64) (bool, error) {
|
||||
return x.
|
||||
Where("is_owner=?", true).
|
||||
And("uid=?", uid).
|
||||
And("org_id=?", orgID).
|
||||
Get(new(OrgUser))
|
||||
return has
|
||||
Table("org_user").
|
||||
Exist()
|
||||
}
|
||||
|
||||
// IsOrganizationMember returns true if given user is member of organization.
|
||||
func IsOrganizationMember(orgID, uid int64) bool {
|
||||
has, _ := x.
|
||||
func IsOrganizationMember(orgID, uid int64) (bool, error) {
|
||||
return x.
|
||||
Where("uid=?", uid).
|
||||
And("org_id=?", orgID).
|
||||
Get(new(OrgUser))
|
||||
return has
|
||||
Table("org_user").
|
||||
Exist()
|
||||
}
|
||||
|
||||
// IsPublicMembership returns true if given user public his/her membership.
|
||||
func IsPublicMembership(orgID, uid int64) bool {
|
||||
has, _ := x.
|
||||
func IsPublicMembership(orgID, uid int64) (bool, error) {
|
||||
return x.
|
||||
Where("uid=?", uid).
|
||||
And("org_id=?", orgID).
|
||||
And("is_public=?", true).
|
||||
Get(new(OrgUser))
|
||||
return has
|
||||
Table("org_user").
|
||||
Exist()
|
||||
}
|
||||
|
||||
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
|
||||
|
|
@ -401,8 +401,9 @@ func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
|
|||
|
||||
// AddOrgUser adds new user to given organization.
|
||||
func AddOrgUser(orgID, uid int64) error {
|
||||
if IsOrganizationMember(orgID, uid) {
|
||||
return nil
|
||||
isAlreadyMember, err := IsOrganizationMember(orgID, uid)
|
||||
if err != nil || isAlreadyMember {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
|
|
@ -447,7 +448,9 @@ func RemoveOrgUser(orgID, userID int64) error {
|
|||
}
|
||||
|
||||
// Check if the user to delete is the last member in owner team.
|
||||
if IsOrganizationOwner(orgID, userID) {
|
||||
if isOwner, err := IsOrganizationOwner(orgID, userID); err != nil {
|
||||
return err
|
||||
} else if isOwner {
|
||||
t, err := org.GetOwnerTeam()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
const ownerTeamName = "Owners"
|
||||
|
|
@ -47,7 +49,12 @@ func (t *Team) IsOwnerTeam() bool {
|
|||
|
||||
// IsMember returns true if given user is a member of team.
|
||||
func (t *Team) IsMember(userID int64) bool {
|
||||
return IsTeamMember(t.OrgID, t.ID, userID)
|
||||
isMember, err := IsTeamMember(t.OrgID, t.ID, userID)
|
||||
if err != nil {
|
||||
log.Error(4, "IsMember: %v", err)
|
||||
return false
|
||||
}
|
||||
return isMember
|
||||
}
|
||||
|
||||
func (t *Team) getRepositories(e Engine) error {
|
||||
|
|
@ -413,17 +420,17 @@ type TeamUser struct {
|
|||
UID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
func isTeamMember(e Engine, orgID, teamID, userID int64) bool {
|
||||
has, _ := e.
|
||||
func isTeamMember(e Engine, orgID, teamID, userID int64) (bool, error) {
|
||||
return e.
|
||||
Where("org_id=?", orgID).
|
||||
And("team_id=?", teamID).
|
||||
And("uid=?", userID).
|
||||
Get(new(TeamUser))
|
||||
return has
|
||||
Table("team_user").
|
||||
Exist()
|
||||
}
|
||||
|
||||
// IsTeamMember returns true if given user is a member of team.
|
||||
func IsTeamMember(orgID, teamID, userID int64) bool {
|
||||
func IsTeamMember(orgID, teamID, userID int64) (bool, error) {
|
||||
return isTeamMember(x, orgID, teamID, userID)
|
||||
}
|
||||
|
||||
|
|
@ -471,8 +478,9 @@ func GetUserTeams(orgID, userID int64) ([]*Team, error) {
|
|||
// AddTeamMember adds new membership of given team to given organization,
|
||||
// the user will have membership to given organization automatically when needed.
|
||||
func AddTeamMember(team *Team, userID int64) error {
|
||||
if IsTeamMember(team.OrgID, team.ID, userID) {
|
||||
return nil
|
||||
isAlreadyMember, err := IsTeamMember(team.OrgID, team.ID, userID)
|
||||
if err != nil || isAlreadyMember {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddOrgUser(team.OrgID, userID); err != nil {
|
||||
|
|
@ -529,8 +537,9 @@ func AddTeamMember(team *Team, userID int64) error {
|
|||
}
|
||||
|
||||
func removeTeamMember(e Engine, team *Team, userID int64) error {
|
||||
if !isTeamMember(e, team.OrgID, team.ID, userID) {
|
||||
return nil
|
||||
isMember, err := isTeamMember(e, team.OrgID, team.ID, userID)
|
||||
if err != nil || !isMember {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the user to delete is the last member in owner team.
|
||||
|
|
@ -566,7 +575,7 @@ func removeTeamMember(e Engine, team *Team, userID int64) error {
|
|||
|
||||
// This must exist.
|
||||
ou := new(OrgUser)
|
||||
_, err := e.
|
||||
_, err = e.
|
||||
Where("uid = ?", userID).
|
||||
And("org_id = ?", team.OrgID).
|
||||
Get(ou)
|
||||
|
|
|
|||
|
|
@ -250,16 +250,21 @@ func TestDeleteTeam(t *testing.T) {
|
|||
|
||||
func TestIsTeamMember(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
test := func(orgID, teamID, userID int64, expected bool) {
|
||||
isMember, err := IsTeamMember(orgID, teamID, userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, isMember)
|
||||
}
|
||||
|
||||
assert.True(t, IsTeamMember(3, 1, 2))
|
||||
assert.False(t, IsTeamMember(3, 1, 4))
|
||||
assert.False(t, IsTeamMember(3, 1, NonexistentID))
|
||||
test(3, 1, 2, true)
|
||||
test(3, 1, 4, false)
|
||||
test(3, 1, NonexistentID, false)
|
||||
|
||||
assert.True(t, IsTeamMember(3, 2, 2))
|
||||
assert.True(t, IsTeamMember(3, 2, 4))
|
||||
test(3, 2, 2, true)
|
||||
test(3, 2, 4, true)
|
||||
|
||||
assert.False(t, IsTeamMember(3, NonexistentID, NonexistentID))
|
||||
assert.False(t, IsTeamMember(NonexistentID, NonexistentID, NonexistentID))
|
||||
test(3, NonexistentID, NonexistentID, false)
|
||||
test(NonexistentID, NonexistentID, NonexistentID, false)
|
||||
}
|
||||
|
||||
func TestGetTeamMembers(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -12,28 +12,44 @@ import (
|
|||
|
||||
func TestUser_IsOwnedBy(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
|
||||
assert.True(t, org.IsOwnedBy(2))
|
||||
assert.False(t, org.IsOwnedBy(1))
|
||||
assert.False(t, org.IsOwnedBy(3))
|
||||
assert.False(t, org.IsOwnedBy(4))
|
||||
|
||||
nonOrg := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
assert.False(t, nonOrg.IsOwnedBy(2))
|
||||
assert.False(t, nonOrg.IsOwnedBy(3))
|
||||
for _, testCase := range []struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
ExpectedOwner bool
|
||||
}{
|
||||
{3, 2, true},
|
||||
{3, 1, false},
|
||||
{3, 3, false},
|
||||
{3, 4, false},
|
||||
{2, 2, false}, // user2 is not an organization
|
||||
{2, 3, false},
|
||||
} {
|
||||
org := AssertExistsAndLoadBean(t, &User{ID: testCase.OrgID}).(*User)
|
||||
isOwner, err := org.IsOwnedBy(testCase.UserID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCase.ExpectedOwner, isOwner)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUser_IsOrgMember(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
|
||||
assert.True(t, org.IsOrgMember(2))
|
||||
assert.True(t, org.IsOrgMember(4))
|
||||
assert.False(t, org.IsOrgMember(1))
|
||||
assert.False(t, org.IsOrgMember(3))
|
||||
|
||||
nonOrg := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
assert.False(t, nonOrg.IsOrgMember(2))
|
||||
assert.False(t, nonOrg.IsOrgMember(3))
|
||||
for _, testCase := range []struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
ExpectedMember bool
|
||||
}{
|
||||
{3, 2, true},
|
||||
{3, 4, true},
|
||||
{3, 1, false},
|
||||
{3, 3, false},
|
||||
{2, 2, false}, // user2 is not an organization
|
||||
{2, 3, false},
|
||||
} {
|
||||
org := AssertExistsAndLoadBean(t, &User{ID: testCase.OrgID}).(*User)
|
||||
isMember, err := org.IsOrgMember(testCase.UserID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCase.ExpectedMember, isMember)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUser_GetTeam(t *testing.T) {
|
||||
|
|
@ -257,31 +273,46 @@ func TestDeleteOrganization(t *testing.T) {
|
|||
|
||||
func TestIsOrganizationOwner(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
assert.True(t, IsOrganizationOwner(3, 2))
|
||||
assert.False(t, IsOrganizationOwner(3, 3))
|
||||
assert.True(t, IsOrganizationOwner(6, 5))
|
||||
assert.False(t, IsOrganizationOwner(6, 4))
|
||||
assert.False(t, IsOrganizationOwner(NonexistentID, NonexistentID))
|
||||
test := func(orgID, userID int64, expected bool) {
|
||||
isOwner, err := IsOrganizationOwner(orgID, userID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, expected, isOwner)
|
||||
}
|
||||
test(3, 2, true)
|
||||
test(3, 3, false)
|
||||
test(6, 5, true)
|
||||
test(6, 4, false)
|
||||
test(NonexistentID, NonexistentID, false)
|
||||
}
|
||||
|
||||
func TestIsOrganizationMember(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
assert.True(t, IsOrganizationMember(3, 2))
|
||||
assert.False(t, IsOrganizationMember(3, 3))
|
||||
assert.True(t, IsOrganizationMember(3, 4))
|
||||
assert.True(t, IsOrganizationMember(6, 5))
|
||||
assert.False(t, IsOrganizationMember(6, 4))
|
||||
assert.False(t, IsOrganizationMember(NonexistentID, NonexistentID))
|
||||
test := func(orgID, userID int64, expected bool) {
|
||||
isMember, err := IsOrganizationMember(orgID, userID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, expected, isMember)
|
||||
}
|
||||
test(3, 2, true)
|
||||
test(3, 3, false)
|
||||
test(3, 4, true)
|
||||
test(6, 5, true)
|
||||
test(6, 4, false)
|
||||
test(NonexistentID, NonexistentID, false)
|
||||
}
|
||||
|
||||
func TestIsPublicMembership(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
assert.True(t, IsPublicMembership(3, 2))
|
||||
assert.False(t, IsPublicMembership(3, 3))
|
||||
assert.False(t, IsPublicMembership(3, 4))
|
||||
assert.True(t, IsPublicMembership(6, 5))
|
||||
assert.False(t, IsPublicMembership(6, 4))
|
||||
assert.False(t, IsPublicMembership(NonexistentID, NonexistentID))
|
||||
test := func(orgID, userID int64, expected bool) {
|
||||
isMember, err := IsPublicMembership(orgID, userID)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, expected, isMember)
|
||||
}
|
||||
test(3, 2, true)
|
||||
test(3, 3, false)
|
||||
test(3, 4, false)
|
||||
test(6, 5, true)
|
||||
test(6, 4, false)
|
||||
test(NonexistentID, NonexistentID, false)
|
||||
}
|
||||
|
||||
func TestGetOrgsByUserID(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -1497,30 +1497,22 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
|||
// Dummy object.
|
||||
collaboration := &Collaboration{RepoID: repo.ID}
|
||||
for _, c := range collaborators {
|
||||
collaboration.UserID = c.ID
|
||||
if c.ID == newOwner.ID || newOwner.IsOrgMember(c.ID) {
|
||||
if _, err = sess.Delete(collaboration); err != nil {
|
||||
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
|
||||
if c.ID != newOwner.ID {
|
||||
isMember, err := newOwner.IsOrgMember(c.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("IsOrgMember: %v", err)
|
||||
} else if !isMember {
|
||||
continue
|
||||
}
|
||||
}
|
||||
collaboration.UserID = c.ID
|
||||
if _, err = sess.Delete(collaboration); err != nil {
|
||||
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old team-repository relations.
|
||||
if owner.IsOrganization() {
|
||||
if err = owner.getTeams(sess); err != nil {
|
||||
return fmt.Errorf("getTeams: %v", err)
|
||||
}
|
||||
for _, t := range owner.Teams {
|
||||
if !t.hasRepository(sess, repo.ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
t.NumRepos--
|
||||
if _, err := sess.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
||||
return fmt.Errorf("decrease team repository count '%d': %v", t.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = owner.removeOrgRepo(sess, repo.ID); err != nil {
|
||||
return fmt.Errorf("removeOrgRepo: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,3 +153,26 @@ func TestRepoLocalCopyPath(t *testing.T) {
|
|||
setting.Repository.Local.LocalCopyPath = tempPath
|
||||
assert.Equal(t, expected, repo.LocalCopyPath())
|
||||
}
|
||||
|
||||
func TestTransferOwnership(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
||||
repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User)
|
||||
assert.NoError(t, TransferOwnership(doer, "user2", repo))
|
||||
|
||||
transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
||||
assert.EqualValues(t, 2, transferredRepo.OwnerID)
|
||||
|
||||
assert.False(t, com.IsExist(RepoPath("user3", "repo3")))
|
||||
assert.True(t, com.IsExist(RepoPath("user2", "repo3")))
|
||||
AssertExistsAndLoadBean(t, &Action{
|
||||
OpType: ActionTransferRepo,
|
||||
ActUserID: 2,
|
||||
RepoID: 3,
|
||||
Content: "user3/repo3",
|
||||
})
|
||||
|
||||
CheckConsistencyFor(t, &Repository{}, &User{}, &Team{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error)
|
|||
}
|
||||
|
||||
// BeanExists for testing, check if a bean exists
|
||||
func BeanExists(t *testing.T, bean interface{}, conditions ...interface{}) bool {
|
||||
func BeanExists(t testing.TB, bean interface{}, conditions ...interface{}) bool {
|
||||
exists, err := loadBeanIfExists(bean, conditions...)
|
||||
assert.NoError(t, err)
|
||||
return exists
|
||||
|
|
@ -123,7 +123,7 @@ func BeanExists(t *testing.T, bean interface{}, conditions ...interface{}) bool
|
|||
|
||||
// AssertExistsAndLoadBean assert that a bean exists and load it from the test
|
||||
// database
|
||||
func AssertExistsAndLoadBean(t *testing.T, bean interface{}, conditions ...interface{}) interface{} {
|
||||
func AssertExistsAndLoadBean(t testing.TB, bean interface{}, conditions ...interface{}) interface{} {
|
||||
exists, err := loadBeanIfExists(bean, conditions...)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exists,
|
||||
|
|
@ -133,7 +133,7 @@ func AssertExistsAndLoadBean(t *testing.T, bean interface{}, conditions ...inter
|
|||
}
|
||||
|
||||
// GetCount get the count of a bean
|
||||
func GetCount(t *testing.T, bean interface{}, conditions ...interface{}) int {
|
||||
func GetCount(t testing.TB, bean interface{}, conditions ...interface{}) int {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
whereConditions(sess, conditions)
|
||||
|
|
@ -143,7 +143,7 @@ func GetCount(t *testing.T, bean interface{}, conditions ...interface{}) int {
|
|||
}
|
||||
|
||||
// AssertNotExistsBean assert that a bean does not exist in the test database
|
||||
func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface{}) {
|
||||
func AssertNotExistsBean(t testing.TB, bean interface{}, conditions ...interface{}) {
|
||||
exists, err := loadBeanIfExists(bean, conditions...)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
|
|
@ -158,18 +158,18 @@ func AssertExistsIf(t *testing.T, expected bool, bean interface{}, conditions ..
|
|||
}
|
||||
|
||||
// AssertSuccessfulInsert assert that beans is successfully inserted
|
||||
func AssertSuccessfulInsert(t *testing.T, beans ...interface{}) {
|
||||
func AssertSuccessfulInsert(t testing.TB, beans ...interface{}) {
|
||||
_, err := x.Insert(beans...)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// AssertCount assert the count of a bean
|
||||
func AssertCount(t *testing.T, bean interface{}, expected interface{}) {
|
||||
func AssertCount(t testing.TB, bean interface{}, expected interface{}) {
|
||||
assert.EqualValues(t, expected, GetCount(t, bean))
|
||||
}
|
||||
|
||||
// AssertInt64InRange assert value is in range [low, high]
|
||||
func AssertInt64InRange(t *testing.T, low, high, value int64) {
|
||||
func AssertInt64InRange(t testing.TB, low, high, value int64) {
|
||||
assert.True(t, value >= low && value <= high,
|
||||
"Expected value in range [%d, %d], found %d", low, high, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -487,12 +487,22 @@ func (u *User) IsOrganization() bool {
|
|||
|
||||
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
||||
func (u *User) IsUserOrgOwner(orgID int64) bool {
|
||||
return IsOrganizationOwner(orgID, u.ID)
|
||||
isOwner, err := IsOrganizationOwner(orgID, u.ID)
|
||||
if err != nil {
|
||||
log.Error(4, "IsOrganizationOwner: %v", err)
|
||||
return false
|
||||
}
|
||||
return isOwner
|
||||
}
|
||||
|
||||
// IsPublicMember returns true if user public his/her membership in given organization.
|
||||
func (u *User) IsPublicMember(orgID int64) bool {
|
||||
return IsPublicMembership(orgID, u.ID)
|
||||
isMember, err := IsPublicMembership(orgID, u.ID)
|
||||
if err != nil {
|
||||
log.Error(4, "IsPublicMembership: %v", err)
|
||||
return false
|
||||
}
|
||||
return isMember
|
||||
}
|
||||
|
||||
func (u *User) getOrganizationCount(e Engine) (int64, error) {
|
||||
|
|
|
|||
|
|
@ -73,14 +73,21 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
|||
ctx.Org.IsTeamMember = true
|
||||
ctx.Org.IsTeamAdmin = true
|
||||
} else if ctx.IsSigned {
|
||||
ctx.Org.IsOwner = org.IsOwnedBy(ctx.User.ID)
|
||||
ctx.Org.IsOwner, err = org.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOwnedBy", err)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Org.IsOwner {
|
||||
ctx.Org.IsMember = true
|
||||
ctx.Org.IsTeamMember = true
|
||||
ctx.Org.IsTeamAdmin = true
|
||||
} else {
|
||||
if org.IsOrgMember(ctx.User.ID) {
|
||||
ctx.Org.IsMember = true
|
||||
ctx.Org.IsMember, err = org.IsOrgMember(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOrgMember", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,13 @@ type MarkupParser struct {
|
|||
IsInputFile bool
|
||||
}
|
||||
|
||||
// enumerates all the policy repository creating
|
||||
const (
|
||||
RepoCreatingLastUserVisibility = "last"
|
||||
RepoCreatingPrivate = "private"
|
||||
RepoCreatingPublic = "public"
|
||||
)
|
||||
|
||||
// settings
|
||||
var (
|
||||
// AppVer settings
|
||||
|
|
@ -89,6 +96,8 @@ var (
|
|||
HTTPAddr string
|
||||
HTTPPort string
|
||||
LocalURL string
|
||||
RedirectOtherPort bool
|
||||
PortToRedirect string
|
||||
OfflineMode bool
|
||||
DisableRouterLog bool
|
||||
CertFile string
|
||||
|
|
@ -180,6 +189,7 @@ var (
|
|||
Repository = struct {
|
||||
AnsiCharset string
|
||||
ForcePrivate bool
|
||||
DefaultPrivate string
|
||||
MaxCreationLimit int
|
||||
MirrorQueueLength int
|
||||
PullRequestQueueLength int
|
||||
|
|
@ -209,6 +219,7 @@ var (
|
|||
}{
|
||||
AnsiCharset: "",
|
||||
ForcePrivate: false,
|
||||
DefaultPrivate: RepoCreatingLastUserVisibility,
|
||||
MaxCreationLimit: -1,
|
||||
MirrorQueueLength: 1000,
|
||||
PullRequestQueueLength: 1000,
|
||||
|
|
@ -732,6 +743,8 @@ func NewContext() {
|
|||
defaultLocalURL += ":" + HTTPPort + "/"
|
||||
}
|
||||
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
|
||||
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
||||
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
||||
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
|
||||
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(AppWorkPath)
|
||||
|
|
|
|||
|
|
@ -489,6 +489,8 @@ mirror_last_synced=Zuletzt synchronisiert
|
|||
watchers=Beobachter
|
||||
stargazers=In Favoriten von
|
||||
forks=Forks
|
||||
pick_reaction=Wähle eine Reaktion
|
||||
reactions_more=und %d weitere
|
||||
|
||||
form.reach_limit_of_creation=Du hast bereits dein Limit von %d Repositories erreicht.
|
||||
form.name_reserved=Der Repository-Name '%s' ist reserviert.
|
||||
|
|
@ -539,6 +541,7 @@ pulls=Pull-Requests
|
|||
labels=Label
|
||||
milestones=Meilensteine
|
||||
commits=Commits
|
||||
commit=Committen
|
||||
releases=Releases
|
||||
file_raw=Originalformat
|
||||
file_history=Verlauf
|
||||
|
|
@ -1544,6 +1547,7 @@ no_read=Du hast momentan keine gelesenen Benachrichtigungen.
|
|||
pin=Benachrichtigung pinnen
|
||||
mark_as_read=Als gelesen markieren
|
||||
mark_as_unread=Als ungelesen markieren
|
||||
mark_all_as_read=Alle als gelesen markieren
|
||||
|
||||
[gpg]
|
||||
error.extract_sign=Die Signatur konnte nicht extrahiert werden
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ sign_in=Bejelentkezés
|
|||
sign_in_with=Bejelentkezés a következővel
|
||||
sign_out=Kijelentkezés
|
||||
sign_up=Regisztráció
|
||||
link_account=Fiók Összekötés
|
||||
link_account=Fiók kapcsolása
|
||||
link_account_signin_or_signup=Bejelentkezés meglévő felhasználóval, hogy a meglévő fiókodhoz kösd, vagy új fiók létrehozása
|
||||
register=Regisztráció
|
||||
website=Webhely
|
||||
|
|
@ -49,22 +49,22 @@ your_settings=Beállításaid
|
|||
|
||||
all=Összes
|
||||
sources=Saját
|
||||
mirrors=Tükör
|
||||
mirrors=Tükrök
|
||||
collaborative=Közreműködő
|
||||
forks=Másolat
|
||||
forks=Másolatok
|
||||
|
||||
activities=Tevékenységek
|
||||
pull_requests=Egyesítési Kérések
|
||||
issues=Hibajegyek
|
||||
|
||||
cancel=Mégsem
|
||||
cancel=Mégse
|
||||
|
||||
[install]
|
||||
install=Telepítés
|
||||
title=Kezdeti konfiguráció
|
||||
docker_helper=Hogyha a Gitea-t Docker-en belül futtatod, akkor kérlek olvasd el az <a target="_blank" rel="noopener" href="%s">irányelveket</a> mielőtt bármit megváltoztatnál ezen az oldalon.
|
||||
requite_db_desc=Giteához szükséges MySQL, PostgreSQL, SQLite3 vagy TiDB.
|
||||
db_title=Adatbázis beállításai
|
||||
docker_helper=Ha Docker-en belül futtatja a Gitea-t, kérjük olvassa el az <a target="_blank" rel="noopener" href="%s">irányelveket</a> mielőtt bármit megváltoztatna ezen az oldalon.
|
||||
requite_db_desc=A Giteához MySQL, PostgreSQL, SQLite3 vagy TiDB adatbázis szükséges.
|
||||
db_title=Adatbázis beállítások
|
||||
db_type=Adatbázis típusa
|
||||
host=Kiszolgáló
|
||||
user=Felhasználó
|
||||
|
|
@ -103,7 +103,7 @@ optional_title=További beállítások
|
|||
email_title=E-mail szolgáltatás beállításai
|
||||
smtp_host=SMTP kiszolgáló
|
||||
smtp_from=Feladó
|
||||
smtp_from_helper=E-Mail feladójának a címe az RFC 5322 formátumban. Lehet megadni csak egy e-mail címet, vagy "Név" <emailcím@example.com> formátumban.
|
||||
smtp_from_helper=Az e-mail feladójának a címe, RFC 5322 formátumban, amely lehet csak az e-mail cím, vagy a "Név" <nev@palda.hu> formátum is.
|
||||
mailer_user=Feladó Felhasználója
|
||||
mailer_password=Feladó Jelszava
|
||||
register_confirm=Regisztráció megerősítésének engedélyezése
|
||||
|
|
@ -140,7 +140,7 @@ run_user_not_match=A futtató felhasználó nem az aktuális felhasználó: %s -
|
|||
save_config_failed=Hiba történt a konfiguráció mentése közben: %v
|
||||
invalid_admin_setting=Rendszergazda fiók beállítása helytelen: %v
|
||||
install_success=Üdvözlünk! Köszönjük, hogy a Gitea-t választotta és jó szórakozást kívánunk a használatához!
|
||||
invalid_log_root_path=Napó gyökérkönyvtára helytelen: %v
|
||||
invalid_log_root_path=Napló gyökérkönyvtára helytelen: %v
|
||||
default_keep_email_private=Email cím ne látszódjon" alapértelmezett értéke
|
||||
default_keep_email_private_popup=Ez az alapértelmezett értéke a felhasználó e-mail cím láthatóságának. Ha értéke igaz, akkor minden új felhasználó e-mail címe rejtve marad, amíg a felhasználó nem módosítja a beállítását.
|
||||
default_allow_create_organization=Felhasználó létrehozhat Szervezeteket" jogosultság alapértelmezett értéke
|
||||
|
|
|
|||
|
|
@ -177,7 +177,10 @@ func reqOrgMembership() macaron.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
if !models.IsOrganizationMember(orgID, ctx.User.ID) {
|
||||
if isMember, err := models.IsOrganizationMember(orgID, ctx.User.ID); err != nil {
|
||||
ctx.Error(500, "IsOrganizationMember", err)
|
||||
return
|
||||
} else if !isMember {
|
||||
if ctx.Org.Organization != nil {
|
||||
ctx.Error(403, "", "Must be an organization member")
|
||||
} else {
|
||||
|
|
@ -200,7 +203,10 @@ func reqOrgOwnership() macaron.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
if !models.IsOrganizationOwner(orgID, ctx.User.ID) {
|
||||
isOwner, err := models.IsOrganizationOwner(orgID, ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOrganizationOwner", err)
|
||||
} else if !isOwner {
|
||||
if ctx.Org.Organization != nil {
|
||||
ctx.Error(403, "", "Must be an organization owner")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,15 @@ func ListMembers(ctx *context.APIContext) {
|
|||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/UserList"
|
||||
publicOnly := ctx.User == nil || !ctx.Org.Organization.IsOrgMember(ctx.User.ID)
|
||||
publicOnly := true
|
||||
if ctx.User != nil {
|
||||
isMember, err := ctx.Org.Organization.IsOrgMember(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOrgMember", err)
|
||||
return
|
||||
}
|
||||
publicOnly = !isMember
|
||||
}
|
||||
listMembers(ctx, publicOnly)
|
||||
}
|
||||
|
||||
|
|
@ -119,19 +127,30 @@ func IsMember(ctx *context.APIContext) {
|
|||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if ctx.User != nil && ctx.Org.Organization.IsOrgMember(ctx.User.ID) {
|
||||
if ctx.Org.Organization.IsOrgMember(userToCheck.ID) {
|
||||
ctx.Status(204)
|
||||
} else {
|
||||
if ctx.User != nil {
|
||||
userIsMember, err := ctx.Org.Organization.IsOrgMember(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOrgMember", err)
|
||||
return
|
||||
} else if userIsMember {
|
||||
userToCheckIsMember, err := ctx.Org.Organization.IsOrgMember(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOrgMember", err)
|
||||
} else if userToCheckIsMember {
|
||||
ctx.Status(204)
|
||||
} else {
|
||||
ctx.Status(404)
|
||||
}
|
||||
return
|
||||
} else if ctx.User.ID == userToCheck.ID {
|
||||
ctx.Status(404)
|
||||
return
|
||||
}
|
||||
} else if ctx.User != nil && ctx.User.ID == userToCheck.ID {
|
||||
ctx.Status(404)
|
||||
} else {
|
||||
redirectURL := fmt.Sprintf("%sapi/v1/orgs/%s/public_members/%s",
|
||||
setting.AppURL, ctx.Org.Organization.Name, userToCheck.Name)
|
||||
ctx.Redirect(redirectURL, 302)
|
||||
}
|
||||
|
||||
redirectURL := fmt.Sprintf("%sapi/v1/orgs/%s/public_members/%s",
|
||||
setting.AppURL, ctx.Org.Organization.Name, userToCheck.Name)
|
||||
ctx.Redirect(redirectURL, 302)
|
||||
}
|
||||
|
||||
// IsPublicMember check if a user is a public member of an organization
|
||||
|
|
|
|||
|
|
@ -176,7 +176,11 @@ func GetTeamMembers(ctx *context.APIContext) {
|
|||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/UserList"
|
||||
if !models.IsOrganizationMember(ctx.Org.Team.OrgID, ctx.User.ID) {
|
||||
isMember, err := models.IsOrganizationMember(ctx.Org.Team.OrgID, ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOrganizationMember", err)
|
||||
return
|
||||
} else if !isMember {
|
||||
ctx.Status(404)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,11 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if !org.IsOrgMember(ctx.User.ID) {
|
||||
isMember, err := org.IsOrgMember(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOrgMember", err)
|
||||
return
|
||||
} else if !isMember {
|
||||
ctx.Status(403)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func ListIssues(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
issues, err := models.Issues(&models.IssuesOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
RepoIDs: []int64{ctx.Repo.Repository.ID},
|
||||
Page: ctx.QueryInt("page"),
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
IsClosed: isClosed,
|
||||
|
|
|
|||
|
|
@ -108,8 +108,19 @@ func Search(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
// Check visibility.
|
||||
if ctx.IsSigned && (ctx.User.ID == repoOwner.ID || (repoOwner.IsOrganization() && repoOwner.IsOwnedBy(ctx.User.ID))) {
|
||||
opts.Private = true
|
||||
if ctx.IsSigned {
|
||||
if ctx.User.ID == repoOwner.ID {
|
||||
opts.Private = true
|
||||
} else if repoOwner.IsOrganization() {
|
||||
opts.Private, err = repoOwner.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +256,11 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
|
|||
return
|
||||
}
|
||||
|
||||
if !org.IsOwnedBy(ctx.User.ID) {
|
||||
isOwner, err := org.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOwnedBy", err)
|
||||
return
|
||||
} else if !isOwner {
|
||||
ctx.Error(403, "", "Given user is not owner of organization.")
|
||||
return
|
||||
}
|
||||
|
|
@ -292,7 +307,11 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||
|
||||
if ctxUser.IsOrganization() && !ctx.User.IsAdmin {
|
||||
// Check ownership of organization.
|
||||
if !ctxUser.IsOwnedBy(ctx.User.ID) {
|
||||
isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOwnedBy", err)
|
||||
return
|
||||
} else if !isOwner {
|
||||
ctx.Error(403, "", "Given user is not owner of organization.")
|
||||
return
|
||||
}
|
||||
|
|
@ -431,9 +450,15 @@ func Delete(ctx *context.APIContext) {
|
|||
owner := ctx.Repo.Owner
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
if owner.IsOrganization() && !owner.IsOwnedBy(ctx.User.ID) {
|
||||
ctx.Error(403, "", "Given user is not owner of organization.")
|
||||
return
|
||||
if owner.IsOrganization() {
|
||||
isOwner, err := owner.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "IsOwnedBy", err)
|
||||
return
|
||||
} else if !isOwner {
|
||||
ctx.Error(403, "", "Given user is not owner of organization.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil {
|
||||
|
|
|
|||
|
|
@ -191,8 +191,8 @@ func Issues(ctx *context.Context) {
|
|||
issues = []*models.Issue{}
|
||||
} else {
|
||||
issues, err = models.Issues(&models.IssuesOptions{
|
||||
RepoIDs: []int64{repo.ID},
|
||||
AssigneeID: assigneeID,
|
||||
RepoID: repo.ID,
|
||||
PosterID: posterID,
|
||||
MentionedID: mentionedID,
|
||||
MilestoneID: milestoneID,
|
||||
|
|
@ -476,6 +476,26 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
|
|||
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
|
||||
}
|
||||
|
||||
// commentTag returns the CommentTag for a comment in/with the given repo, poster and issue
|
||||
func commentTag(repo *models.Repository, poster *models.User, issue *models.Issue) (models.CommentTag, error) {
|
||||
if repo.IsOwnedBy(poster.ID) {
|
||||
return models.CommentTagOwner, nil
|
||||
} else if repo.Owner.IsOrganization() {
|
||||
isOwner, err := repo.Owner.IsOwnedBy(poster.ID)
|
||||
if err != nil {
|
||||
return models.CommentTagNone, err
|
||||
} else if isOwner {
|
||||
return models.CommentTagOwner, nil
|
||||
}
|
||||
}
|
||||
if poster.IsWriterOfRepo(repo) {
|
||||
return models.CommentTagWriter, nil
|
||||
} else if poster.ID == issue.PosterID {
|
||||
return models.CommentTagPoster, nil
|
||||
}
|
||||
return models.CommentTagNone, nil
|
||||
}
|
||||
|
||||
// ViewIssue render issue view page
|
||||
func ViewIssue(ctx *context.Context) {
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
|
|
@ -648,15 +668,11 @@ func ViewIssue(ctx *context.Context) {
|
|||
continue
|
||||
}
|
||||
|
||||
if repo.IsOwnedBy(comment.PosterID) ||
|
||||
(repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(comment.PosterID)) {
|
||||
comment.ShowTag = models.CommentTagOwner
|
||||
} else if comment.Poster.IsWriterOfRepo(repo) {
|
||||
comment.ShowTag = models.CommentTagWriter
|
||||
} else if comment.PosterID == issue.PosterID {
|
||||
comment.ShowTag = models.CommentTagPoster
|
||||
comment.ShowTag, err = commentTag(repo, comment.Poster, issue)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "commentTag", err)
|
||||
return
|
||||
}
|
||||
|
||||
marked[comment.PosterID] = comment.ShowTag
|
||||
|
||||
isAdded := false
|
||||
|
|
|
|||
|
|
@ -173,7 +173,11 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) {
|
|||
|
||||
// Check ownership of organization.
|
||||
if ctxUser.IsOrganization() {
|
||||
if !ctxUser.IsOwnedBy(ctx.User.ID) {
|
||||
isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOwnedBy", err)
|
||||
return
|
||||
} else if !isOwner {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,13 +74,36 @@ func checkContextUser(ctx *context.Context, uid int64) *models.User {
|
|||
}
|
||||
|
||||
// Check ownership of organization.
|
||||
if !org.IsOrganization() || !(ctx.User.IsAdmin || org.IsOwnedBy(ctx.User.ID)) {
|
||||
if !org.IsOrganization() {
|
||||
ctx.Error(403)
|
||||
return nil
|
||||
}
|
||||
if !ctx.User.IsAdmin {
|
||||
isOwner, err := org.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOwnedBy", err)
|
||||
return nil
|
||||
} else if !isOwner {
|
||||
ctx.Error(403)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return org
|
||||
}
|
||||
|
||||
func getRepoPrivate(ctx *context.Context) bool {
|
||||
switch strings.ToLower(setting.Repository.DefaultPrivate) {
|
||||
case setting.RepoCreatingLastUserVisibility:
|
||||
return ctx.User.LastRepoVisibility
|
||||
case setting.RepoCreatingPrivate:
|
||||
return true
|
||||
case setting.RepoCreatingPublic:
|
||||
return false
|
||||
default:
|
||||
return ctx.User.LastRepoVisibility
|
||||
}
|
||||
}
|
||||
|
||||
// Create render creating repository page
|
||||
func Create(ctx *context.Context) {
|
||||
if !ctx.User.CanCreateRepo() {
|
||||
|
|
@ -94,7 +117,7 @@ func Create(ctx *context.Context) {
|
|||
ctx.Data["Licenses"] = models.Licenses
|
||||
ctx.Data["Readmes"] = models.Readmes
|
||||
ctx.Data["readme"] = "Default"
|
||||
ctx.Data["private"] = ctx.User.LastRepoVisibility
|
||||
ctx.Data["private"] = getRepoPrivate(ctx)
|
||||
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
|
||||
|
||||
ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
|
||||
|
|
@ -170,7 +193,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
|
|||
// Migrate render migration of repository page
|
||||
func Migrate(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("new_migrate")
|
||||
ctx.Data["private"] = ctx.User.LastRepoVisibility
|
||||
ctx.Data["private"] = getRepoPrivate(ctx)
|
||||
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
|
||||
ctx.Data["mirror"] = ctx.Query("mirror") == "1"
|
||||
ctx.Data["LFSActive"] = setting.LFS.StartServer
|
||||
|
|
|
|||
|
|
@ -235,13 +235,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.Owner.IsOrganization() {
|
||||
if !ctx.Repo.Owner.IsOwnedBy(ctx.User.ID) {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !repo.IsMirror {
|
||||
ctx.Error(404)
|
||||
return
|
||||
|
|
@ -269,13 +262,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.Owner.IsOrganization() {
|
||||
if !ctx.Repo.Owner.IsOwnedBy(ctx.User.ID) {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
newOwner := ctx.Query("new_owner_name")
|
||||
isExist, err := models.IsUserExist(0, newOwner)
|
||||
if err != nil {
|
||||
|
|
@ -308,13 +294,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.Owner.IsOrganization() {
|
||||
if !ctx.Repo.Owner.IsOwnedBy(ctx.User.ID) {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := models.DeleteRepository(ctx.User, ctx.Repo.Owner.ID, repo.ID); err != nil {
|
||||
ctx.Handle(500, "DeleteRepository", err)
|
||||
return
|
||||
|
|
@ -334,13 +313,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.Owner.IsOrganization() {
|
||||
if !ctx.Repo.Owner.IsOwnedBy(ctx.User.ID) {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
repo.DeleteWiki()
|
||||
log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
|
||||
|
|
@ -394,10 +366,16 @@ func CollaborationPost(ctx *context.Context) {
|
|||
}
|
||||
|
||||
// Check if user is organization member.
|
||||
if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.ID) {
|
||||
ctx.Flash.Info(ctx.Tr("repo.settings.user_is_org_member"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
||||
return
|
||||
if ctx.Repo.Owner.IsOrganization() {
|
||||
isMember, err := ctx.Repo.Owner.IsOrgMember(u.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "IsOrgMember", err)
|
||||
return
|
||||
} else if isMember {
|
||||
ctx.Flash.Info(ctx.Tr("repo.settings.user_is_org_member"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = ctx.Repo.Repository.AddCollaborator(u); err != nil {
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/Unknwon/paginater"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/paginater"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -231,21 +232,30 @@ func Issues(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(userRepoIDs) <= 0 {
|
||||
userRepoIDs = []int64{-1}
|
||||
}
|
||||
|
||||
opts := &models.IssuesOptions{
|
||||
RepoID: repoID,
|
||||
IsClosed: util.OptionalBoolOf(isShowClosed),
|
||||
IsPull: util.OptionalBoolOf(isPullList),
|
||||
SortType: sortType,
|
||||
}
|
||||
|
||||
if repoID > 0 {
|
||||
opts.RepoIDs = []int64{repoID}
|
||||
}
|
||||
|
||||
switch filterMode {
|
||||
case models.FilterModeAll:
|
||||
opts.RepoIDs = userRepoIDs
|
||||
if repoID > 0 {
|
||||
if !com.IsSliceContainsInt64(userRepoIDs, repoID) {
|
||||
// force an empty result
|
||||
opts.RepoIDs = []int64{-1}
|
||||
}
|
||||
} else {
|
||||
opts.RepoIDs = userRepoIDs
|
||||
}
|
||||
case models.FilterModeAssign:
|
||||
opts.AssigneeID = ctxUser.ID
|
||||
case models.FilterModeCreate:
|
||||
|
|
@ -308,7 +318,18 @@ func Issues(ctx *context.Context) {
|
|||
issue.Repo = showReposMap[issue.RepoID]
|
||||
}
|
||||
|
||||
issueStats := models.GetUserIssueStats(repoID, ctxUser.ID, userRepoIDs, filterMode, isPullList)
|
||||
issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
|
||||
UserID: ctxUser.ID,
|
||||
RepoID: repoID,
|
||||
UserRepoIDs: userRepoIDs,
|
||||
FilterMode: filterMode,
|
||||
IsPull: isPullList,
|
||||
IsClosed: isShowClosed,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetUserIssueStats", err)
|
||||
return
|
||||
}
|
||||
|
||||
var total int
|
||||
if !isShowClosed {
|
||||
|
|
|
|||
16
vendor/code.gitea.io/git/commit_info.go
generated
vendored
16
vendor/code.gitea.io/git/commit_info.go
generated
vendored
|
|
@ -79,7 +79,7 @@ func targetedSearch(state *getCommitsInfoState, done chan error) {
|
|||
done <- nil
|
||||
return
|
||||
}
|
||||
command := NewCommand("rev-list", "-1", "HEAD", "--", entryPath)
|
||||
command := NewCommand("rev-list", "-1", state.headCommit.ID.String(), "--", entryPath)
|
||||
output, err := command.RunInDir(state.headCommit.repo.Path)
|
||||
if err != nil {
|
||||
done <- err
|
||||
|
|
@ -192,7 +192,7 @@ func getCommitsInfo(state *getCommitsInfoState) error {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
args := []string{"log", getCommitsInfoPretty, "--name-status", "-c"}
|
||||
args := []string{"log", state.headCommit.ID.String(), getCommitsInfoPretty, "--name-status", "-c"}
|
||||
if len(state.treePath) > 0 {
|
||||
args = append(args, "--", state.treePath)
|
||||
}
|
||||
|
|
@ -207,6 +207,10 @@ func getCommitsInfo(state *getCommitsInfoState) error {
|
|||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
// it's okay to ignore the error returned by cmd.Wait(); we expect the
|
||||
// subprocess to sometimes have a non-zero exit status, since we may
|
||||
// prematurely close stdout, resulting in a broken pipe.
|
||||
defer cmd.Wait()
|
||||
|
||||
numThreads := runtime.NumCPU()
|
||||
done := make(chan error, numThreads)
|
||||
|
|
@ -216,6 +220,14 @@ func getCommitsInfo(state *getCommitsInfoState) error {
|
|||
|
||||
scanner := bufio.NewScanner(readCloser)
|
||||
err = state.processGitLogOutput(scanner)
|
||||
|
||||
// it is important that we close stdout here; if we do not close
|
||||
// stdout, the subprocess will keep running, and the deffered call
|
||||
// cmd.Wait() may block for a long time.
|
||||
if closeErr := readCloser.Close(); closeErr != nil && err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
|
||||
for i := 0; i < numThreads; i++ {
|
||||
doneErr := <-done
|
||||
if doneErr != nil && err == nil {
|
||||
|
|
|
|||
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
|
|
@ -3,10 +3,10 @@
|
|||
"ignore": "test appengine",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "UnJFMWkh0ulYlOV0etJYgt5SzJY=",
|
||||
"checksumSHA1": "Em29XiKkOh5rFFXdkCjqqsQ7fe4=",
|
||||
"path": "code.gitea.io/git",
|
||||
"revision": "4768133d10fa395278f545f3bf3ce44552b30ad6",
|
||||
"revisionTime": "2017-12-10T10:06:09Z"
|
||||
"revision": "4ec3654064ef7eef4f05f891073a38039ad8d0f7",
|
||||
"revisionTime": "2017-12-22T02:43:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QQ7g7B9+EIzGjO14KCGEs9TNEzM=",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user