mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge remote-tracking branch 'upstream/master' into team-grant-all-repos
This commit is contained in:
@@ -246,6 +246,55 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err
|
||||
return repo.refreshAccesses(e, accessMap)
|
||||
}
|
||||
|
||||
// recalculateUserAccess recalculates new access for a single user
|
||||
// Usable if we know access only affected one user
|
||||
func (repo *Repository) recalculateUserAccess(e Engine, uid int64) (err error) {
|
||||
minMode := AccessModeRead
|
||||
if !repo.IsPrivate {
|
||||
minMode = AccessModeWrite
|
||||
}
|
||||
|
||||
accessMode := AccessModeNone
|
||||
collaborator, err := repo.getCollaboration(e, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if collaborator != nil {
|
||||
accessMode = collaborator.Mode
|
||||
}
|
||||
|
||||
if err = repo.getOwner(e); err != nil {
|
||||
return err
|
||||
} else if repo.Owner.IsOrganization() {
|
||||
var teams []Team
|
||||
if err := e.Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||
Join("INNER", "team_user", "team_user.team_id = team.id").
|
||||
Where("team.org_id = ?", repo.OwnerID).
|
||||
And("team_repo.repo_id=?", repo.ID).
|
||||
And("team_user.uid=?", uid).
|
||||
Find(&teams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, t := range teams {
|
||||
if t.IsOwnerTeam() {
|
||||
t.Authorize = AccessModeOwner
|
||||
}
|
||||
|
||||
accessMode = maxAccessMode(accessMode, t.Authorize)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old user accesses and insert new one for repository.
|
||||
if _, err = e.Delete(&Access{RepoID: repo.ID, UserID: uid}); err != nil {
|
||||
return fmt.Errorf("delete old user accesses: %v", err)
|
||||
} else if accessMode >= minMode {
|
||||
if _, err = e.Insert(&Access{RepoID: repo.ID, UserID: uid, Mode: accessMode}); err != nil {
|
||||
return fmt.Errorf("insert new user accesses: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) recalculateAccesses(e Engine) error {
|
||||
if repo.Owner.IsOrganization() {
|
||||
return repo.recalculateTeamAccesses(e, 0)
|
||||
|
||||
+39
-216
@@ -6,19 +6,17 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
@@ -54,29 +52,6 @@ const (
|
||||
ActionMirrorSyncDelete // 20
|
||||
)
|
||||
|
||||
var (
|
||||
// Same as GitHub. See
|
||||
// https://help.github.com/articles/closing-issues-via-commit-messages
|
||||
issueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
|
||||
issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
|
||||
|
||||
issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
|
||||
issueReferenceKeywordsPat *regexp.Regexp
|
||||
)
|
||||
|
||||
const issueRefRegexpStr = `(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)+`
|
||||
const issueRefRegexpStrNoKeyword = `(?:\s|^|\(|\[)(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`
|
||||
|
||||
func assembleKeywordsPattern(words []string) string {
|
||||
return fmt.Sprintf(`(?i)(?:%s)(?::?) %s`, strings.Join(words, "|"), issueRefRegexpStr)
|
||||
}
|
||||
|
||||
func init() {
|
||||
issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
|
||||
issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
|
||||
issueReferenceKeywordsPat = regexp.MustCompile(issueRefRegexpStrNoKeyword)
|
||||
}
|
||||
|
||||
// Action represents user operation type and other information to
|
||||
// repository. It implemented interface base.Actioner so that can be
|
||||
// used in template render.
|
||||
@@ -351,10 +326,6 @@ func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error
|
||||
return renameRepoAction(x, actUser, oldRepoName, repo)
|
||||
}
|
||||
|
||||
func issueIndexTrimRight(c rune) bool {
|
||||
return !unicode.IsDigit(c)
|
||||
}
|
||||
|
||||
// PushCommit represents a commit in a push operation.
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
@@ -480,39 +451,9 @@ func (pc *PushCommits) AvatarLink(email string) string {
|
||||
}
|
||||
|
||||
// getIssueFromRef returns the issue referenced by a ref. Returns a nil *Issue
|
||||
// if the provided ref is misformatted or references a non-existent issue.
|
||||
func getIssueFromRef(repo *Repository, ref string) (*Issue, error) {
|
||||
ref = ref[strings.IndexByte(ref, ' ')+1:]
|
||||
ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
|
||||
|
||||
var refRepo *Repository
|
||||
poundIndex := strings.IndexByte(ref, '#')
|
||||
if poundIndex < 0 {
|
||||
return nil, nil
|
||||
} else if poundIndex == 0 {
|
||||
refRepo = repo
|
||||
} else {
|
||||
slashIndex := strings.IndexByte(ref, '/')
|
||||
if slashIndex < 0 || slashIndex >= poundIndex {
|
||||
return nil, nil
|
||||
}
|
||||
ownerName := ref[:slashIndex]
|
||||
repoName := ref[slashIndex+1 : poundIndex]
|
||||
var err error
|
||||
refRepo, err = GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
if err != nil {
|
||||
if IsErrRepoNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
issueIndex, err := strconv.ParseInt(ref[poundIndex+1:], 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issue, err := GetIssueByIndex(refRepo.ID, issueIndex)
|
||||
// if the provided ref references a non-existent issue.
|
||||
func getIssueFromRef(repo *Repository, index int64) (*Issue, error) {
|
||||
issue, err := GetIssueByIndex(repo.ID, index)
|
||||
if err != nil {
|
||||
if IsErrIssueNotExist(err) {
|
||||
return nil, nil
|
||||
@@ -522,20 +463,7 @@ func getIssueFromRef(repo *Repository, ref string) (*Issue, error) {
|
||||
return issue, nil
|
||||
}
|
||||
|
||||
func changeIssueStatus(repo *Repository, doer *User, ref string, refMarked map[int64]bool, status bool) error {
|
||||
issue, err := getIssueFromRef(repo, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if issue == nil || refMarked[issue.ID] {
|
||||
return nil
|
||||
}
|
||||
refMarked[issue.ID] = true
|
||||
|
||||
if issue.RepoID != repo.ID || issue.IsClosed == status {
|
||||
return nil
|
||||
}
|
||||
func changeIssueStatus(repo *Repository, issue *Issue, doer *User, status bool) error {
|
||||
|
||||
stopTimerIfAvailable := func(doer *User, issue *Issue) error {
|
||||
|
||||
@@ -549,7 +477,7 @@ func changeIssueStatus(repo *Repository, doer *User, ref string, refMarked map[i
|
||||
}
|
||||
|
||||
issue.Repo = repo
|
||||
if err = issue.ChangeStatus(doer, status); err != nil {
|
||||
if err := issue.ChangeStatus(doer, status); err != nil {
|
||||
// Don't return an error when dependencies are open as this would let the push fail
|
||||
if IsErrDependenciesLeft(err) {
|
||||
return stopTimerIfAvailable(doer, issue)
|
||||
@@ -566,99 +494,67 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
|
||||
for i := len(commits) - 1; i >= 0; i-- {
|
||||
c := commits[i]
|
||||
|
||||
refMarked := make(map[int64]bool)
|
||||
type markKey struct {
|
||||
ID int64
|
||||
Action references.XRefAction
|
||||
}
|
||||
|
||||
refMarked := make(map[markKey]bool)
|
||||
var refRepo *Repository
|
||||
var refIssue *Issue
|
||||
var err error
|
||||
for _, m := range issueReferenceKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
|
||||
if len(m[3]) == 0 {
|
||||
continue
|
||||
}
|
||||
ref := m[3]
|
||||
for _, ref := range references.FindAllIssueReferences(c.Message) {
|
||||
|
||||
// issue is from another repo
|
||||
if len(m[1]) > 0 && len(m[2]) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(m[1], m[2])
|
||||
if len(ref.Owner) > 0 && len(ref.Name) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(ref.Owner, ref.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
refRepo = repo
|
||||
}
|
||||
issue, err := getIssueFromRef(refRepo, ref)
|
||||
if err != nil {
|
||||
if refIssue, err = getIssueFromRef(refRepo, ref.Index); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if issue == nil || refMarked[issue.ID] {
|
||||
if refIssue == nil {
|
||||
continue
|
||||
}
|
||||
refMarked[issue.ID] = true
|
||||
|
||||
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
|
||||
if err = CreateRefComment(doer, refRepo, issue, message, c.Sha1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Change issue status only if the commit has been pushed to the default branch.
|
||||
// and if the repo is configured to allow only that
|
||||
if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
|
||||
continue
|
||||
}
|
||||
refMarked = make(map[int64]bool)
|
||||
for _, m := range issueCloseKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
|
||||
if len(m[3]) == 0 {
|
||||
continue
|
||||
}
|
||||
ref := m[3]
|
||||
|
||||
// issue is from another repo
|
||||
if len(m[1]) > 0 && len(m[2]) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(m[1], m[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
refRepo = repo
|
||||
}
|
||||
|
||||
perm, err := GetUserRepoPermission(refRepo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// only close issues in another repo if user has push access
|
||||
if perm.CanWrite(UnitTypeCode) {
|
||||
if err := changeIssueStatus(refRepo, doer, ref, refMarked, true); err != nil {
|
||||
|
||||
key := markKey{ID: refIssue.ID, Action: ref.Action}
|
||||
if refMarked[key] {
|
||||
continue
|
||||
}
|
||||
refMarked[key] = true
|
||||
|
||||
// only create comments for issues if user has permission for it
|
||||
if perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeIssues) {
|
||||
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
|
||||
if err = CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It is conflict to have close and reopen at same time, so refsMarked doesn't need to reinit here.
|
||||
for _, m := range issueReopenKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
|
||||
if len(m[3]) == 0 {
|
||||
// Process closing/reopening keywords
|
||||
if ref.Action != references.XRefActionCloses && ref.Action != references.XRefActionReopens {
|
||||
continue
|
||||
}
|
||||
ref := m[3]
|
||||
|
||||
// issue is from another repo
|
||||
if len(m[1]) > 0 && len(m[2]) > 0 {
|
||||
refRepo, err = GetRepositoryFromMatch(m[1], m[2])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
refRepo = repo
|
||||
// Change issue status only if the commit has been pushed to the default branch.
|
||||
// and if the repo is configured to allow only that
|
||||
// FIXME: we should be using Issue.ref if set instead of repo.DefaultBranch
|
||||
if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
|
||||
continue
|
||||
}
|
||||
|
||||
perm, err := GetUserRepoPermission(refRepo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// only reopen issues in another repo if user has push access
|
||||
if perm.CanWrite(UnitTypeCode) {
|
||||
if err := changeIssueStatus(refRepo, doer, ref, refMarked, false); err != nil {
|
||||
// only close issues in another repo if user has push access
|
||||
if perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeCode) {
|
||||
if err := changeIssueStatus(refRepo, refIssue, doer, ref.Action == references.XRefActionCloses); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -713,79 +609,6 @@ func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error
|
||||
return mergePullRequestAction(x, actUser, repo, pull)
|
||||
}
|
||||
|
||||
func mirrorSyncAction(e Engine, opType ActionType, repo *Repository, refName string, data []byte) error {
|
||||
if err := notifyWatchers(e, &Action{
|
||||
ActUserID: repo.OwnerID,
|
||||
ActUser: repo.MustOwner(),
|
||||
OpType: opType,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
RefName: refName,
|
||||
Content: string(data),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
go HookQueue.Add(repo.ID)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MirrorSyncPushActionOptions mirror synchronization action options.
|
||||
type MirrorSyncPushActionOptions struct {
|
||||
RefName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
Commits *PushCommits
|
||||
}
|
||||
|
||||
// MirrorSyncPushAction adds new action for mirror synchronization of pushed commits.
|
||||
func MirrorSyncPushAction(repo *Repository, opts MirrorSyncPushActionOptions) error {
|
||||
if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
|
||||
apiCommits, err := opts.Commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||
apiPusher := repo.MustOwner().APIFormat()
|
||||
if err := PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
|
||||
Ref: opts.RefName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppURL + opts.Commits.CompareURL,
|
||||
Commits: apiCommits,
|
||||
Repo: repo.APIFormat(AccessModeOwner),
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("PrepareWebhooks: %v", err)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(opts.Commits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mirrorSyncAction(x, ActionMirrorSyncPush, repo, opts.RefName, data)
|
||||
}
|
||||
|
||||
// MirrorSyncCreateAction adds new action for mirror synchronization of new reference.
|
||||
func MirrorSyncCreateAction(repo *Repository, refName string) error {
|
||||
return mirrorSyncAction(x, ActionMirrorSyncCreate, repo, refName, nil)
|
||||
}
|
||||
|
||||
// MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference.
|
||||
func MirrorSyncDeleteAction(repo *Repository, refName string) error {
|
||||
return mirrorSyncAction(x, ActionMirrorSyncDelete, repo, refName, nil)
|
||||
}
|
||||
|
||||
// GetFeedsOptions options for retrieving feeds
|
||||
type GetFeedsOptions struct {
|
||||
RequestedUser *User
|
||||
|
||||
+1
-52
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -181,56 +180,6 @@ func TestPushCommits_AvatarLink(t *testing.T) {
|
||||
pushCommits.AvatarLink("nonexistent@example.com"))
|
||||
}
|
||||
|
||||
func TestRegExp_issueReferenceKeywordsPat(t *testing.T) {
|
||||
trueTestCases := []string{
|
||||
"#2",
|
||||
"[#2]",
|
||||
"please see go-gitea/gitea#5",
|
||||
"#2:",
|
||||
}
|
||||
falseTestCases := []string{
|
||||
"kb#2",
|
||||
"#2xy",
|
||||
}
|
||||
|
||||
for _, testCase := range trueTestCases {
|
||||
assert.True(t, issueReferenceKeywordsPat.MatchString(testCase))
|
||||
}
|
||||
for _, testCase := range falseTestCases {
|
||||
assert.False(t, issueReferenceKeywordsPat.MatchString(testCase))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getIssueFromRef(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
|
||||
for _, test := range []struct {
|
||||
Ref string
|
||||
ExpectedIssueID int64
|
||||
}{
|
||||
{"#2", 2},
|
||||
{"reopen #2", 2},
|
||||
{"user2/repo2#1", 4},
|
||||
{"fixes user2/repo2#1", 4},
|
||||
{"fixes: user2/repo2#1", 4},
|
||||
} {
|
||||
issue, err := getIssueFromRef(repo, test.Ref)
|
||||
assert.NoError(t, err)
|
||||
if assert.NotNil(t, issue) {
|
||||
assert.EqualValues(t, test.ExpectedIssueID, issue.ID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, badRef := range []string{
|
||||
"doesnotexist/doesnotexist#1",
|
||||
fmt.Sprintf("#%d", NonexistentID),
|
||||
} {
|
||||
issue, err := getIssueFromRef(repo, badRef)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, issue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateIssuesCommit(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
pushCommits := []*PushCommit{
|
||||
@@ -431,7 +380,7 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
|
||||
AssertNotExistsBean(t, commentBean)
|
||||
AssertNotExistsBean(t, issueBean, "is_closed=1")
|
||||
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
|
||||
AssertExistsAndLoadBean(t, commentBean)
|
||||
AssertNotExistsBean(t, commentBean)
|
||||
AssertNotExistsBean(t, issueBean, "is_closed=1")
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Attachment represent a attachment of issue/comment/release.
|
||||
|
||||
+23
-1
@@ -34,6 +34,7 @@ type ProtectedBranch struct {
|
||||
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
|
||||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
|
||||
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
|
||||
@@ -195,7 +196,7 @@ func UpdateProtectBranch(repo *Repository, protectBranch *ProtectedBranch, opts
|
||||
}
|
||||
protectBranch.MergeWhitelistUserIDs = whitelist
|
||||
|
||||
whitelist, err = updateUserWhitelist(repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
|
||||
whitelist, err = updateApprovalWhitelist(repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -301,6 +302,27 @@ func (repo *Repository) IsProtectedBranchForMerging(pr *PullRequest, branchName
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
|
||||
// the users from newWhitelist which have explicit read or write access to the repo.
|
||||
func updateApprovalWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
|
||||
hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist)
|
||||
if !hasUsersChanged {
|
||||
return currentWhitelist, nil
|
||||
}
|
||||
|
||||
whitelist = make([]int64, 0, len(newWhitelist))
|
||||
for _, userID := range newWhitelist {
|
||||
if reader, err := repo.IsReader(userID); err != nil {
|
||||
return nil, err
|
||||
} else if !reader {
|
||||
continue
|
||||
}
|
||||
whitelist = append(whitelist, userID)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
|
||||
// the users from newWhitelist which have write access to the repo.
|
||||
func updateUserWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CommitStatusState holds the state of a Status
|
||||
|
||||
+125
-18
@@ -4,13 +4,34 @@
|
||||
|
||||
package models
|
||||
|
||||
import "github.com/markbates/goth"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
|
||||
type ExternalLoginUser struct {
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]interface{} `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
Email string
|
||||
Name string
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
Description string
|
||||
AvatarURL string
|
||||
Location string
|
||||
AccessToken string `xorm:"TEXT"`
|
||||
AccessTokenSecret string `xorm:"TEXT"`
|
||||
RefreshToken string `xorm:"TEXT"`
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// GetExternalLogin checks if a externalID in loginSourceID scope already exists
|
||||
@@ -32,23 +53,15 @@ func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) {
|
||||
return externalAccounts, nil
|
||||
}
|
||||
|
||||
// LinkAccountToUser link the gothUser to the user
|
||||
func LinkAccountToUser(user *User, gothUser goth.User) error {
|
||||
loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
externalLoginUser := &ExternalLoginUser{
|
||||
ExternalID: gothUser.UserID,
|
||||
UserID: user.ID,
|
||||
LoginSourceID: loginSource.ID,
|
||||
}
|
||||
has, err := x.Get(externalLoginUser)
|
||||
// LinkExternalToUser link the external user to the user
|
||||
func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error {
|
||||
has, err := x.Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
|
||||
NoAutoCondition().
|
||||
Exist(externalLoginUser)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrExternalLoginUserAlreadyExist{gothUser.UserID, user.ID, loginSource.ID}
|
||||
return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID}
|
||||
}
|
||||
|
||||
_, err = x.Insert(externalLoginUser)
|
||||
@@ -72,3 +85,97 @@ func removeAllAccountLinks(e Engine, user *User) error {
|
||||
_, err := e.Delete(&ExternalLoginUser{UserID: user.ID})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserIDByExternalUserID get user id according to provider and userID
|
||||
func GetUserIDByExternalUserID(provider string, userID string) (int64, error) {
|
||||
var id int64
|
||||
_, err := x.Table("external_login_user").
|
||||
Select("user_id").
|
||||
Where("provider=?", provider).
|
||||
And("external_id=?", userID).
|
||||
Get(&id)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateExternalUser updates external user's information
|
||||
func UpdateExternalUser(user *User, gothUser goth.User) error {
|
||||
loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
externalLoginUser := &ExternalLoginUser{
|
||||
ExternalID: gothUser.UserID,
|
||||
UserID: user.ID,
|
||||
LoginSourceID: loginSource.ID,
|
||||
RawData: gothUser.RawData,
|
||||
Provider: gothUser.Provider,
|
||||
Email: gothUser.Email,
|
||||
Name: gothUser.Name,
|
||||
FirstName: gothUser.FirstName,
|
||||
LastName: gothUser.LastName,
|
||||
NickName: gothUser.NickName,
|
||||
Description: gothUser.Description,
|
||||
AvatarURL: gothUser.AvatarURL,
|
||||
Location: gothUser.Location,
|
||||
AccessToken: gothUser.AccessToken,
|
||||
AccessTokenSecret: gothUser.AccessTokenSecret,
|
||||
RefreshToken: gothUser.RefreshToken,
|
||||
ExpiresAt: gothUser.ExpiresAt,
|
||||
}
|
||||
|
||||
has, err := x.Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).
|
||||
NoAutoCondition().
|
||||
Exist(externalLoginUser)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrExternalLoginUserNotExist{user.ID, loginSource.ID}
|
||||
}
|
||||
|
||||
_, err = x.Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).AllCols().Update(externalLoginUser)
|
||||
return err
|
||||
}
|
||||
|
||||
// FindExternalUserOptions represents an options to find external users
|
||||
type FindExternalUserOptions struct {
|
||||
Provider string
|
||||
Limit int
|
||||
Start int
|
||||
}
|
||||
|
||||
func (opts FindExternalUserOptions) toConds() builder.Cond {
|
||||
var cond = builder.NewCond()
|
||||
if len(opts.Provider) > 0 {
|
||||
cond = cond.And(builder.Eq{"provider": opts.Provider})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
// FindExternalUsersByProvider represents external users via provider
|
||||
func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
|
||||
var users []ExternalLoginUser
|
||||
err := x.Where(opts.toConds()).
|
||||
Limit(opts.Limit, opts.Start).
|
||||
OrderBy("login_source_id ASC, external_id ASC").
|
||||
Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
|
||||
func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error {
|
||||
if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return UpdateReleasesMigrationsByType(tp, externalUserID, userID)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
act_user_id: 2
|
||||
repo_id: 2
|
||||
is_private: true
|
||||
created_unix: 1540139562
|
||||
created_unix: 1571686356
|
||||
|
||||
-
|
||||
id: 2
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
index: 2
|
||||
head_repo_id: 1
|
||||
base_repo_id: 1
|
||||
head_user_name: user1
|
||||
head_branch: branch1
|
||||
base_branch: master
|
||||
merge_base: 1234567890abcdef
|
||||
@@ -21,7 +20,6 @@
|
||||
index: 3
|
||||
head_repo_id: 1
|
||||
base_repo_id: 1
|
||||
head_user_name: user1
|
||||
head_branch: branch2
|
||||
base_branch: master
|
||||
merge_base: fedcba9876543210
|
||||
@@ -35,7 +33,6 @@
|
||||
index: 1
|
||||
head_repo_id: 11
|
||||
base_repo_id: 10
|
||||
head_user_name: user13
|
||||
head_branch: branch2
|
||||
base_branch: master
|
||||
merge_base: 0abcb056019adb83
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
num_milestones: 3
|
||||
num_closed_milestones: 1
|
||||
num_watches: 3
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 2
|
||||
@@ -24,6 +25,7 @@
|
||||
num_closed_pulls: 0
|
||||
num_stars: 1
|
||||
close_issues_via_commit_in_any_branch: true
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 3
|
||||
@@ -36,6 +38,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
num_watches: 0
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 4
|
||||
@@ -48,6 +51,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
num_stars: 1
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 5
|
||||
@@ -61,6 +65,7 @@
|
||||
num_closed_pulls: 0
|
||||
num_watches: 0
|
||||
is_mirror: true
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 6
|
||||
@@ -73,6 +78,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 7
|
||||
@@ -85,6 +91,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 8
|
||||
@@ -97,6 +104,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 9
|
||||
@@ -109,6 +117,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 10
|
||||
@@ -122,6 +131,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
num_forks: 1
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 11
|
||||
@@ -135,6 +145,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 12
|
||||
@@ -147,6 +158,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 13
|
||||
@@ -159,6 +171,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 14
|
||||
@@ -172,6 +185,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 15
|
||||
@@ -179,6 +193,7 @@
|
||||
lower_name: repo15
|
||||
name: repo15
|
||||
is_empty: true
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 16
|
||||
@@ -191,6 +206,7 @@
|
||||
num_pulls: 0
|
||||
num_closed_pulls: 0
|
||||
num_watches: 0
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 17
|
||||
@@ -205,6 +221,7 @@
|
||||
num_watches: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 18
|
||||
@@ -218,6 +235,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 19
|
||||
@@ -231,6 +249,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 20
|
||||
@@ -244,6 +263,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 21
|
||||
@@ -257,6 +277,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 22
|
||||
@@ -270,6 +291,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 23
|
||||
@@ -283,6 +305,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 24
|
||||
@@ -296,6 +319,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 25
|
||||
@@ -310,6 +334,7 @@
|
||||
num_watches: 0
|
||||
is_mirror: true
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 26
|
||||
@@ -324,6 +349,7 @@
|
||||
num_watches: 0
|
||||
is_mirror: true
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 27
|
||||
@@ -339,6 +365,7 @@
|
||||
is_mirror: true
|
||||
num_forks: 1
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 28
|
||||
@@ -354,6 +381,7 @@
|
||||
is_mirror: true
|
||||
num_forks: 1
|
||||
is_fork: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 29
|
||||
@@ -368,6 +396,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: true
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 30
|
||||
@@ -382,6 +411,7 @@
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
is_fork: true
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 31
|
||||
@@ -392,6 +422,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 32 # org public repo
|
||||
@@ -403,6 +434,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 33
|
||||
@@ -410,6 +442,7 @@
|
||||
lower_name: utf8
|
||||
name: utf8
|
||||
is_private: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 34
|
||||
@@ -421,6 +454,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 35
|
||||
@@ -432,6 +466,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 36
|
||||
@@ -443,6 +478,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 37
|
||||
@@ -454,6 +490,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 38
|
||||
@@ -465,6 +502,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 39
|
||||
@@ -476,6 +514,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 40
|
||||
@@ -487,6 +526,7 @@
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
id: 41
|
||||
@@ -519,4 +559,5 @@
|
||||
num_stars: 0
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
is_mirror: false
|
||||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
+298
-71
@@ -17,12 +17,13 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/armor"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// GPGKey represents a GPG key.
|
||||
@@ -80,6 +81,12 @@ func GetGPGKeyByID(keyID int64) (*GPGKey, error) {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GetGPGKeysByKeyID returns public key by given ID.
|
||||
func GetGPGKeysByKeyID(keyID string) ([]*GPGKey, error) {
|
||||
keys := make([]*GPGKey, 0, 1)
|
||||
return keys, x.Where("key_id=?", keyID).Find(&keys)
|
||||
}
|
||||
|
||||
// GetGPGImportByKeyID returns the import public armored key by given KeyID.
|
||||
func GetGPGImportByKeyID(keyID string) (*GPGKeyImport, error) {
|
||||
key := new(GPGKeyImport)
|
||||
@@ -355,10 +362,13 @@ func DeleteGPGKey(doer *User, id int64) (err error) {
|
||||
|
||||
// CommitVerification represents a commit validation of signature
|
||||
type CommitVerification struct {
|
||||
Verified bool
|
||||
Reason string
|
||||
SigningUser *User
|
||||
SigningKey *GPGKey
|
||||
Verified bool
|
||||
Warning bool
|
||||
Reason string
|
||||
SigningUser *User
|
||||
CommittingUser *User
|
||||
SigningEmail string
|
||||
SigningKey *GPGKey
|
||||
}
|
||||
|
||||
// SignCommit represents a commit with validation of signature.
|
||||
@@ -367,6 +377,17 @@ type SignCommit struct {
|
||||
*UserCommit
|
||||
}
|
||||
|
||||
const (
|
||||
// BadSignature is used as the reason when the signature has a KeyID that is in the db
|
||||
// but no key that has that ID verifies the signature. This is a suspicious failure.
|
||||
BadSignature = "gpg.error.probable_bad_signature"
|
||||
// BadDefaultSignature is used as the reason when the signature has a KeyID that matches the
|
||||
// default Key but is not verified by the default key. This is a suspicious failure.
|
||||
BadDefaultSignature = "gpg.error.probable_bad_default_signature"
|
||||
// NoKeyFound is used as the reason when no key can be found to verify the signature.
|
||||
NoKeyFound = "gpg.error.no_gpg_keys_found"
|
||||
)
|
||||
|
||||
func readerFromBase64(s string) (io.Reader, error) {
|
||||
bs, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
@@ -424,49 +445,207 @@ func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
|
||||
return pkey.VerifySignature(h, s)
|
||||
}
|
||||
|
||||
// ParseCommitWithSignature check if signature is good against keystore.
|
||||
func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
|
||||
if c.Signature != nil && c.Committer != nil {
|
||||
//Parsing signature
|
||||
sig, err := extractSignature(c.Signature.Signature)
|
||||
if err != nil { //Skipping failed to extract sign
|
||||
log.Error("SignatureRead err: %v", err)
|
||||
return &CommitVerification{
|
||||
Verified: false,
|
||||
Reason: "gpg.error.extract_sign",
|
||||
func hashAndVerify(sig *packet.Signature, payload string, k *GPGKey, committer, signer *User, email string) *CommitVerification {
|
||||
//Generating hash of commit
|
||||
hash, err := populateHash(sig.Hash, []byte(payload))
|
||||
if err != nil { //Skipping failed to generate hash
|
||||
log.Error("PopulateHash: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
|
||||
if err := verifySign(sig, hash, k); err == nil {
|
||||
return &CommitVerification{ //Everything is ok
|
||||
CommittingUser: committer,
|
||||
Verified: true,
|
||||
Reason: fmt.Sprintf("%s <%s> / %s", signer.Name, signer.Email, k.KeyID),
|
||||
SigningUser: signer,
|
||||
SigningKey: k,
|
||||
SigningEmail: email,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey, committer, signer *User, email string) *CommitVerification {
|
||||
commitVerification := hashAndVerify(sig, payload, k, committer, signer, email)
|
||||
if commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
|
||||
//And test also SubsKey
|
||||
for _, sk := range k.SubsKey {
|
||||
commitVerification := hashAndVerify(sig, payload, sk, committer, signer, email)
|
||||
if commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hashAndVerifyForKeyID(sig *packet.Signature, payload string, committer *User, keyID, name, email string) *CommitVerification {
|
||||
if keyID == "" {
|
||||
return nil
|
||||
}
|
||||
keys, err := GetGPGKeysByKeyID(keyID)
|
||||
if err != nil {
|
||||
log.Error("GetGPGKeysByKeyID: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
}
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, key := range keys {
|
||||
activated := false
|
||||
if len(email) != 0 {
|
||||
for _, e := range key.Emails {
|
||||
if e.IsActivated && strings.EqualFold(e.Email, email) {
|
||||
activated = true
|
||||
email = e.Email
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, e := range key.Emails {
|
||||
if e.IsActivated {
|
||||
activated = true
|
||||
email = e.Email
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !activated {
|
||||
continue
|
||||
}
|
||||
signer := &User{
|
||||
Name: name,
|
||||
Email: email,
|
||||
}
|
||||
if key.OwnerID != 0 {
|
||||
owner, err := GetUserByID(key.OwnerID)
|
||||
if err == nil {
|
||||
signer = owner
|
||||
} else if !IsErrUserNotExist(err) {
|
||||
log.Error("Failed to GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.no_committer_account",
|
||||
}
|
||||
}
|
||||
}
|
||||
commitVerification := hashAndVerifyWithSubKeys(sig, payload, key, committer, signer, email)
|
||||
if commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
// This is a bad situation ... We have a key id that is in our database but the signature doesn't match.
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Warning: true,
|
||||
Reason: BadSignature,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseCommitWithSignature check if signature is good against keystore.
|
||||
func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
|
||||
var committer *User
|
||||
if c.Committer != nil {
|
||||
var err error
|
||||
//Find Committer account
|
||||
committer, err := GetUserByEmail(c.Committer.Email) //This find the user by primary email or activated email so commit will not be valid if email is not
|
||||
if err != nil { //Skipping not user for commiter
|
||||
committer, err = GetUserByEmail(c.Committer.Email) //This finds the user by primary email or activated email so commit will not be valid if email is not
|
||||
if err != nil { //Skipping not user for commiter
|
||||
committer = &User{
|
||||
Name: c.Committer.Name,
|
||||
Email: c.Committer.Email,
|
||||
}
|
||||
// We can expect this to often be an ErrUserNotExist. in the case
|
||||
// it is not, however, it is important to log it.
|
||||
if !IsErrUserNotExist(err) {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.no_committer_account",
|
||||
}
|
||||
}
|
||||
return &CommitVerification{
|
||||
Verified: false,
|
||||
Reason: "gpg.error.no_committer_account",
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If no signature just report the committer
|
||||
if c.Signature == nil {
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false, //Default value
|
||||
Reason: "gpg.error.not_signed_commit", //Default value
|
||||
}
|
||||
}
|
||||
|
||||
//Parsing signature
|
||||
sig, err := extractSignature(c.Signature.Signature)
|
||||
if err != nil { //Skipping failed to extract sign
|
||||
log.Error("SignatureRead err: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.extract_sign",
|
||||
}
|
||||
}
|
||||
|
||||
keyID := ""
|
||||
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
|
||||
keyID = fmt.Sprintf("%X", *sig.IssuerKeyId)
|
||||
}
|
||||
if keyID == "" && sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
|
||||
keyID = fmt.Sprintf("%X", sig.IssuerFingerprint[12:20])
|
||||
}
|
||||
|
||||
defaultReason := NoKeyFound
|
||||
|
||||
// First check if the sig has a keyID and if so just look at that
|
||||
if commitVerification := hashAndVerifyForKeyID(
|
||||
sig,
|
||||
c.Signature.Payload,
|
||||
committer,
|
||||
keyID,
|
||||
setting.AppName,
|
||||
""); commitVerification != nil {
|
||||
if commitVerification.Reason == BadSignature {
|
||||
defaultReason = BadSignature
|
||||
} else {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
|
||||
// Now try to associate the signature with the committer, if present
|
||||
if committer.ID != 0 {
|
||||
keys, err := ListGPGKeys(committer.ID)
|
||||
if err != nil { //Skipping failed to get gpg keys of user
|
||||
log.Error("ListGPGKeys: %v", err)
|
||||
return &CommitVerification{
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
}
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
//Pre-check (& optimization) that emails attached to key can be attached to the commiter email and can validate
|
||||
canValidate := false
|
||||
lowerCommiterEmail := strings.ToLower(c.Committer.Email)
|
||||
email := ""
|
||||
for _, e := range k.Emails {
|
||||
if e.IsActivated && strings.ToLower(e.Email) == lowerCommiterEmail {
|
||||
if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
|
||||
canValidate = true
|
||||
email = e.Email
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -474,56 +653,104 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
|
||||
continue //Skip this key
|
||||
}
|
||||
|
||||
//Generating hash of commit
|
||||
hash, err := populateHash(sig.Hash, []byte(c.Signature.Payload))
|
||||
if err != nil { //Skipping ailed to generate hash
|
||||
log.Error("PopulateHash: %v", err)
|
||||
return &CommitVerification{
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
commitVerification := hashAndVerifyWithSubKeys(sig, c.Signature.Payload, k, committer, committer, email)
|
||||
if commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
//We get PK
|
||||
if err := verifySign(sig, hash, k); err == nil {
|
||||
return &CommitVerification{ //Everything is ok
|
||||
Verified: true,
|
||||
Reason: fmt.Sprintf("%s <%s> / %s", c.Committer.Name, c.Committer.Email, k.KeyID),
|
||||
SigningUser: committer,
|
||||
SigningKey: k,
|
||||
}
|
||||
}
|
||||
//And test also SubsKey
|
||||
for _, sk := range k.SubsKey {
|
||||
|
||||
//Generating hash of commit
|
||||
hash, err := populateHash(sig.Hash, []byte(c.Signature.Payload))
|
||||
if err != nil { //Skipping ailed to generate hash
|
||||
log.Error("PopulateHash: %v", err)
|
||||
return &CommitVerification{
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
if err := verifySign(sig, hash, sk); err == nil {
|
||||
return &CommitVerification{ //Everything is ok
|
||||
Verified: true,
|
||||
Reason: fmt.Sprintf("%s <%s> / %s", c.Committer.Name, c.Committer.Email, sk.KeyID),
|
||||
SigningUser: committer,
|
||||
SigningKey: sk,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return &CommitVerification{ //Default at this stage
|
||||
Verified: false,
|
||||
Reason: "gpg.error.no_gpg_keys_found",
|
||||
}
|
||||
}
|
||||
|
||||
return &CommitVerification{
|
||||
Verified: false, //Default value
|
||||
Reason: "gpg.error.not_signed_commit", //Default value
|
||||
if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" {
|
||||
// OK we should try the default key
|
||||
gpgSettings := git.GPGSettings{
|
||||
Sign: true,
|
||||
KeyID: setting.Repository.Signing.SigningKey,
|
||||
Name: setting.Repository.Signing.SigningName,
|
||||
Email: setting.Repository.Signing.SigningEmail,
|
||||
}
|
||||
if err := gpgSettings.LoadPublicKeyContent(); err != nil {
|
||||
log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
|
||||
} else if commitVerification := verifyWithGPGSettings(&gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
|
||||
if commitVerification.Reason == BadSignature {
|
||||
defaultReason = BadSignature
|
||||
} else {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false)
|
||||
if err != nil {
|
||||
log.Error("Error getting default public gpg key: %v", err)
|
||||
} else if defaultGPGSettings == nil {
|
||||
log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
|
||||
} else if defaultGPGSettings.Sign {
|
||||
if commitVerification := verifyWithGPGSettings(defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
|
||||
if commitVerification.Reason == BadSignature {
|
||||
defaultReason = BadSignature
|
||||
} else {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &CommitVerification{ //Default at this stage
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Warning: defaultReason != NoKeyFound,
|
||||
Reason: defaultReason,
|
||||
SigningKey: &GPGKey{
|
||||
KeyID: keyID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *User, keyID string) *CommitVerification {
|
||||
// First try to find the key in the db
|
||||
if commitVerification := hashAndVerifyForKeyID(sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
|
||||
// Otherwise we have to parse the key
|
||||
ekey, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent)
|
||||
if err != nil {
|
||||
log.Error("Unable to get default signing key: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
pubkey := ekey.PrimaryKey
|
||||
content, err := base64EncPubKey(pubkey)
|
||||
if err != nil {
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
k := &GPGKey{
|
||||
Content: content,
|
||||
CanSign: pubkey.CanSign(),
|
||||
KeyID: pubkey.KeyIdString(),
|
||||
}
|
||||
if commitVerification := hashAndVerifyWithSubKeys(sig, payload, k, committer, &User{
|
||||
Name: gpgSettings.Name,
|
||||
Email: gpgSettings.Email,
|
||||
}, gpgSettings.Email); commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
if keyID == k.KeyID {
|
||||
// This is a bad situation ... We have a key id that matches our default key but the signature doesn't match.
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Warning: true,
|
||||
Reason: BadSignature,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
|
||||
|
||||
+2
-1
@@ -30,7 +30,7 @@ type GraphItem struct {
|
||||
type GraphItems []GraphItem
|
||||
|
||||
// GetCommitGraph return a list of commit (GraphItems) from all branches
|
||||
func GetCommitGraph(r *git.Repository) (GraphItems, error) {
|
||||
func GetCommitGraph(r *git.Repository, page int) (GraphItems, error) {
|
||||
|
||||
var CommitGraph []GraphItem
|
||||
|
||||
@@ -43,6 +43,7 @@ func GetCommitGraph(r *git.Repository) (GraphItems, error) {
|
||||
"-C",
|
||||
"-M",
|
||||
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum),
|
||||
fmt.Sprintf("--skip=%d", setting.UI.GraphMaxCommitNum*(page-1)),
|
||||
"--date=iso",
|
||||
fmt.Sprintf("--pretty=format:%s", format),
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ func BenchmarkGetCommitGraph(b *testing.B) {
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
graph, err := GetCommitGraph(currentRepo)
|
||||
graph, err := GetCommitGraph(currentRepo, 1)
|
||||
if err != nil {
|
||||
b.Error("Could get commit graph")
|
||||
}
|
||||
|
||||
+174
-259
@@ -14,13 +14,14 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Issue represents an issue or pull request of repository.
|
||||
@@ -32,7 +33,7 @@ type Issue struct {
|
||||
PosterID int64 `xorm:"INDEX"`
|
||||
Poster *User `xorm:"-"`
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64
|
||||
OriginalAuthorID int64 `xorm:"index"`
|
||||
Title string `xorm:"name"`
|
||||
Content string `xorm:"TEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
@@ -427,52 +428,6 @@ func (issue *Issue) HasLabel(labelID int64) bool {
|
||||
return issue.hasLabel(x, labelID)
|
||||
}
|
||||
|
||||
func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
||||
var err error
|
||||
|
||||
if err = issue.loadRepo(x); err != nil {
|
||||
log.Error("loadRepo: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = issue.loadPoster(x); err != nil {
|
||||
log.Error("loadPoster: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
||||
if issue.IsPull {
|
||||
if err = issue.loadPullRequest(x); err != nil {
|
||||
log.Error("loadPullRequest: %v", err)
|
||||
return
|
||||
}
|
||||
if err = issue.PullRequest.LoadIssue(); err != nil {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueLabelUpdated,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
|
||||
Action: api.HookIssueLabelUpdated,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
|
||||
} else {
|
||||
go HookQueue.Add(issue.RepoID)
|
||||
}
|
||||
}
|
||||
|
||||
// ReplyReference returns tokenized address to use for email reply headers
|
||||
func (issue *Issue) ReplyReference() string {
|
||||
var path string
|
||||
@@ -489,30 +444,10 @@ func (issue *Issue) addLabel(e *xorm.Session, label *Label, doer *User) error {
|
||||
return newIssueLabel(e, issue, label, doer)
|
||||
}
|
||||
|
||||
// AddLabel adds a new label to the issue.
|
||||
func (issue *Issue) AddLabel(doer *User, label *Label) error {
|
||||
if err := NewIssueLabel(issue, label, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
issue.sendLabelUpdatedWebhook(doer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) addLabels(e *xorm.Session, labels []*Label, doer *User) error {
|
||||
return newIssueLabels(e, issue, labels, doer)
|
||||
}
|
||||
|
||||
// AddLabels adds a list of new labels to the issue.
|
||||
func (issue *Issue) AddLabels(doer *User, labels []*Label) error {
|
||||
if err := NewIssueLabels(issue, labels, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
issue.sendLabelUpdatedWebhook(doer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) getLabels(e Engine) (err error) {
|
||||
if len(issue.Labels) > 0 {
|
||||
return nil
|
||||
@@ -529,28 +464,6 @@ func (issue *Issue) removeLabel(e *xorm.Session, doer *User, label *Label) error
|
||||
return deleteIssueLabel(e, issue, label, doer)
|
||||
}
|
||||
|
||||
// RemoveLabel removes a label from issue by given ID.
|
||||
func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
|
||||
if err := issue.loadRepo(x); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
perm, err := GetUserRepoPermission(issue.Repo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
return ErrLabelNotExist{}
|
||||
}
|
||||
|
||||
if err := DeleteIssueLabel(issue, label, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
issue.sendLabelUpdatedWebhook(doer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) clearLabels(e *xorm.Session, doer *User) (err error) {
|
||||
if err = issue.getLabels(e); err != nil {
|
||||
return fmt.Errorf("getLabels: %v", err)
|
||||
@@ -595,40 +508,6 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||
if err = sess.Commit(); err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
sess.Close()
|
||||
|
||||
if err = issue.LoadPoster(); err != nil {
|
||||
return fmt.Errorf("loadPoster: %v", err)
|
||||
}
|
||||
|
||||
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
||||
if issue.IsPull {
|
||||
err = issue.PullRequest.LoadIssue()
|
||||
if err != nil {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueLabelCleared,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
|
||||
Action: api.HookIssueLabelCleared,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
|
||||
} else {
|
||||
go HookQueue.Add(issue.RepoID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -714,11 +593,6 @@ func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateIssueCols only updates values of specific columns for given issue.
|
||||
func UpdateIssueCols(issue *Issue, cols ...string) error {
|
||||
return updateIssueCols(x, issue, cols...)
|
||||
}
|
||||
|
||||
func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err error) {
|
||||
// Reload the issue
|
||||
currentIssue, err := getIssueByID(e, issue.ID)
|
||||
@@ -766,7 +640,7 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (er
|
||||
}
|
||||
|
||||
// Update issue count of milestone
|
||||
if err = changeMilestoneIssueStats(e, issue); err != nil {
|
||||
if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -844,9 +718,7 @@ func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) {
|
||||
}
|
||||
|
||||
// ChangeTitle changes the title of this issue, as the given user.
|
||||
func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
oldTitle := issue.Title
|
||||
issue.Title = title
|
||||
func (issue *Issue) ChangeTitle(doer *User, oldTitle string) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
@@ -862,7 +734,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
return fmt.Errorf("loadRepo: %v", err)
|
||||
}
|
||||
|
||||
if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, title); err != nil {
|
||||
if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, issue.Title); err != nil {
|
||||
return fmt.Errorf("createChangeTitleComment: %v", err)
|
||||
}
|
||||
|
||||
@@ -874,51 +746,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
sess.Close()
|
||||
|
||||
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
||||
if issue.IsPull {
|
||||
if err = issue.loadPullRequest(sess); err != nil {
|
||||
return fmt.Errorf("loadPullRequest: %v", err)
|
||||
}
|
||||
issue.PullRequest.Issue = issue
|
||||
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueEdited,
|
||||
Index: issue.Index,
|
||||
Changes: &api.ChangesPayload{
|
||||
Title: &api.ChangesFromPayload{
|
||||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
|
||||
Action: api.HookIssueEdited,
|
||||
Index: issue.Index,
|
||||
Changes: &api.ChangesPayload{
|
||||
Title: &api.ChangesFromPayload{
|
||||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: issue.Poster.APIFormat(),
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
|
||||
} else {
|
||||
go HookQueue.Add(issue.RepoID)
|
||||
}
|
||||
|
||||
return nil
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// AddDeletePRBranchComment adds delete branch comment for pull request issue
|
||||
@@ -939,6 +767,26 @@ func AddDeletePRBranchComment(doer *User, repo *Repository, issueID int64, branc
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// UpdateAttachments update attachments by UUIDs for the issue
|
||||
func (issue *Issue) UpdateAttachments(uuids []string) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
attachments, err := getAttachmentsByUUIDs(sess, uuids)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", uuids, err)
|
||||
}
|
||||
for i := 0; i < len(attachments); i++ {
|
||||
attachments[i].IssueID = issue.ID
|
||||
if err := updateAttachment(sess, attachments[i]); err != nil {
|
||||
return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err)
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// ChangeContent changes issue content, as the given user.
|
||||
func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||
oldContent := issue.Content
|
||||
@@ -1048,7 +896,6 @@ type NewIssueOptions struct {
|
||||
Repo *Repository
|
||||
Issue *Issue
|
||||
LabelIDs []int64
|
||||
AssigneeIDs []int64
|
||||
Attachments []string // In UUID format.
|
||||
IsPull bool
|
||||
}
|
||||
@@ -1070,40 +917,7 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the old assignee id thingy for compatibility reasons
|
||||
if opts.Issue.AssigneeID > 0 {
|
||||
isAdded := false
|
||||
// Check if the user has already been passed to issue.AssigneeIDs, if not, add it
|
||||
for _, aID := range opts.AssigneeIDs {
|
||||
if aID == opts.Issue.AssigneeID {
|
||||
isAdded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isAdded {
|
||||
opts.AssigneeIDs = append(opts.AssigneeIDs, opts.Issue.AssigneeID)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for and validate assignees
|
||||
if len(opts.AssigneeIDs) > 0 {
|
||||
for _, assigneeID := range opts.AssigneeIDs {
|
||||
user, err := getUserByID(e, assigneeID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getUserByID [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
|
||||
}
|
||||
valid, err := canBeAssigned(e, user, opts.Repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("canBeAssigned [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
|
||||
}
|
||||
if !valid {
|
||||
return ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: opts.Repo.Name}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Milestone and assignee validation should happen before insert actual object.
|
||||
// Milestone validation should happen before insert actual object.
|
||||
if _, err := e.SetExpr("`index`", "coalesce(MAX(`index`),0)+1").
|
||||
Where("repo_id=?", opts.Issue.RepoID).
|
||||
Insert(opts.Issue); err != nil {
|
||||
@@ -1119,15 +933,11 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||
opts.Issue.Index = inserted.Index
|
||||
|
||||
if opts.Issue.MilestoneID > 0 {
|
||||
if err = changeMilestoneAssign(e, doer, opts.Issue, -1); err != nil {
|
||||
if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the assignees
|
||||
for _, assigneeID := range opts.AssigneeIDs {
|
||||
err = opts.Issue.changeAssignee(e, doer, assigneeID, true)
|
||||
if err != nil {
|
||||
if _, err = createMilestoneComment(e, doer, opts.Repo, opts.Issue, 0, opts.Issue.MilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1189,11 +999,11 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||
}
|
||||
|
||||
// NewIssue creates new issue with labels for repository.
|
||||
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) (err error) {
|
||||
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
|
||||
// Retry several times in case INSERT fails due to duplicate key for (repo_id, index); see #7887
|
||||
i := 0
|
||||
for {
|
||||
if err = newIssueAttempt(repo, issue, labelIDs, assigneeIDs, uuids); err == nil {
|
||||
if err = newIssueAttempt(repo, issue, labelIDs, uuids); err == nil {
|
||||
return nil
|
||||
}
|
||||
if !IsErrNewIssueInsert(err) {
|
||||
@@ -1207,7 +1017,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in
|
||||
return fmt.Errorf("NewIssue: too many errors attempting to insert the new issue. Last error was: %v", err)
|
||||
}
|
||||
|
||||
func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) (err error) {
|
||||
func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
@@ -1219,7 +1029,6 @@ func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, assigneeI
|
||||
Issue: issue,
|
||||
LabelIDs: labelIDs,
|
||||
Attachments: uuids,
|
||||
AssigneeIDs: assigneeIDs,
|
||||
}); err != nil {
|
||||
if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
||||
return err
|
||||
@@ -1400,8 +1209,12 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
|
||||
|
||||
if opts.LabelIDs != nil {
|
||||
for i, labelID := range opts.LabelIDs {
|
||||
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
|
||||
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
|
||||
if labelID > 0 {
|
||||
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
|
||||
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
|
||||
} else {
|
||||
sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1477,46 +1290,18 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
|
||||
return users, e.In("id", userIDs).Find(&users)
|
||||
}
|
||||
|
||||
// UpdateIssueMentions extracts mentioned people from content and
|
||||
// updates issue-user relations for them.
|
||||
func UpdateIssueMentions(ctx DBContext, issueID int64, mentions []string) error {
|
||||
// UpdateIssueMentions updates issue-user relations for mentioned users.
|
||||
func UpdateIssueMentions(ctx DBContext, issueID int64, mentions []*User) error {
|
||||
if len(mentions) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range mentions {
|
||||
mentions[i] = strings.ToLower(mentions[i])
|
||||
ids := make([]int64, len(mentions))
|
||||
for i, u := range mentions {
|
||||
ids[i] = u.ID
|
||||
}
|
||||
users := make([]*User, 0, len(mentions))
|
||||
|
||||
if err := ctx.e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
|
||||
return fmt.Errorf("find mentioned users: %v", err)
|
||||
}
|
||||
|
||||
ids := make([]int64, 0, len(mentions))
|
||||
for _, user := range users {
|
||||
ids = append(ids, user.ID)
|
||||
if !user.IsOrganization() || user.NumMembers == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
memberIDs := make([]int64, 0, user.NumMembers)
|
||||
orgUsers, err := getOrgUsersByOrgID(ctx.e, user.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetOrgUsersByOrgID [%d]: %v", user.ID, err)
|
||||
}
|
||||
|
||||
for _, orgUser := range orgUsers {
|
||||
memberIDs = append(memberIDs, orgUser.ID)
|
||||
}
|
||||
|
||||
ids = append(ids, memberIDs...)
|
||||
}
|
||||
|
||||
if err := UpdateIssueUsersByMentions(ctx, issueID, ids); err != nil {
|
||||
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1909,3 +1694,133 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
|
||||
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
|
||||
func (issue *Issue) ResolveMentionsByVisibility(ctx DBContext, doer *User, mentions []string) (users []*User, err error) {
|
||||
if len(mentions) == 0 {
|
||||
return
|
||||
}
|
||||
if err = issue.loadRepo(ctx.e); err != nil {
|
||||
return
|
||||
}
|
||||
resolved := make(map[string]bool, 20)
|
||||
names := make([]string, 0, 20)
|
||||
resolved[doer.LowerName] = true
|
||||
for _, name := range mentions {
|
||||
name := strings.ToLower(name)
|
||||
if _, ok := resolved[name]; ok {
|
||||
continue
|
||||
}
|
||||
resolved[name] = false
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
if err := issue.Repo.getOwner(ctx.e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if issue.Repo.Owner.IsOrganization() {
|
||||
// Since there can be users with names that match the name of a team,
|
||||
// if the team exists and can read the issue, the team takes precedence.
|
||||
teams := make([]*Team, 0, len(names))
|
||||
if err := ctx.e.
|
||||
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||
Where("team_repo.repo_id=?", issue.Repo.ID).
|
||||
In("team.lower_name", names).
|
||||
Find(&teams); err != nil {
|
||||
return nil, fmt.Errorf("find mentioned teams: %v", err)
|
||||
}
|
||||
if len(teams) != 0 {
|
||||
checked := make([]int64, 0, len(teams))
|
||||
unittype := UnitTypeIssues
|
||||
if issue.IsPull {
|
||||
unittype = UnitTypePullRequests
|
||||
}
|
||||
for _, team := range teams {
|
||||
if team.Authorize >= AccessModeOwner {
|
||||
checked = append(checked, team.ID)
|
||||
resolved[team.LowerName] = true
|
||||
continue
|
||||
}
|
||||
has, err := ctx.e.Get(&TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get team units (%d): %v", team.ID, err)
|
||||
}
|
||||
if has {
|
||||
checked = append(checked, team.ID)
|
||||
resolved[team.LowerName] = true
|
||||
}
|
||||
}
|
||||
if len(checked) != 0 {
|
||||
teamusers := make([]*User, 0, 20)
|
||||
if err := ctx.e.
|
||||
Join("INNER", "team_user", "team_user.uid = `user`.id").
|
||||
In("`team_user`.team_id", checked).
|
||||
And("`user`.is_active = ?", true).
|
||||
And("`user`.prohibit_login = ?", false).
|
||||
Find(&teamusers); err != nil {
|
||||
return nil, fmt.Errorf("get teams users: %v", err)
|
||||
}
|
||||
if len(teamusers) > 0 {
|
||||
users = make([]*User, 0, len(teamusers))
|
||||
for _, user := range teamusers {
|
||||
if already, ok := resolved[user.LowerName]; !ok || !already {
|
||||
users = append(users, user)
|
||||
resolved[user.LowerName] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove names already in the list to avoid querying the database if pending names remain
|
||||
names = make([]string, 0, len(resolved))
|
||||
for name, already := range resolved {
|
||||
if !already {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
unchecked := make([]*User, 0, len(names))
|
||||
if err := ctx.e.
|
||||
Where("`user`.is_active = ?", true).
|
||||
And("`user`.prohibit_login = ?", false).
|
||||
In("`user`.lower_name", names).
|
||||
Find(&unchecked); err != nil {
|
||||
return nil, fmt.Errorf("find mentioned users: %v", err)
|
||||
}
|
||||
for _, user := range unchecked {
|
||||
if already := resolved[user.LowerName]; already || user.IsOrganization() {
|
||||
continue
|
||||
}
|
||||
// Normal users must have read access to the referencing issue
|
||||
perm, err := getUserRepoPermission(ctx.e, issue.Repo, user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getUserRepoPermission [%d]: %v", user.ID, err)
|
||||
}
|
||||
if !perm.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
continue
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateIssuesMigrationsByType updates all migrated repositories' issues from gitServiceType to replace originalAuthorID to posterID
|
||||
func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID string, posterID int64) error {
|
||||
_, err := x.Table("issue").
|
||||
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
|
||||
And("original_author_id = ?", originalAuthorID).
|
||||
Update(map[string]interface{}{
|
||||
"poster_id": posterID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
+55
-85
@@ -10,7 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// IssueAssignees saves all issue assignees
|
||||
@@ -58,8 +58,11 @@ func getAssigneesByIssue(e Engine, issue *Issue) (assignees []*User, err error)
|
||||
|
||||
// IsUserAssignedToIssue returns true when the user is assigned to the issue
|
||||
func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) {
|
||||
isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
|
||||
return
|
||||
return isUserAssignedToIssue(x, issue, user)
|
||||
}
|
||||
|
||||
func isUserAssignedToIssue(e Engine, issue *Issue, user *User) (isAssigned bool, err error) {
|
||||
return e.Get(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
|
||||
}
|
||||
|
||||
// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
|
||||
@@ -78,7 +81,7 @@ func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err e
|
||||
|
||||
if !found {
|
||||
// This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here
|
||||
if err := UpdateAssignee(issue, doer, assignee.ID); err != nil {
|
||||
if _, _, err := issue.ToggleAssignee(doer, assignee.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -110,73 +113,56 @@ func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue
|
||||
func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) {
|
||||
// Check if the user is already assigned
|
||||
isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isAssigned {
|
||||
return issue.ChangeAssignee(doer, assigneeID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAssignee deletes or adds an assignee to an issue
|
||||
func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) {
|
||||
return issue.ChangeAssignee(doer, assigneeID)
|
||||
}
|
||||
|
||||
// ChangeAssignee changes the Assignee of this issue.
|
||||
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
||||
// ToggleAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
|
||||
func (issue *Issue) ToggleAssignee(doer *User, assigneeID int64) (removed bool, comment *Comment, err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if err := issue.changeAssignee(sess, doer, assigneeID, false); err != nil {
|
||||
return err
|
||||
removed, comment, err = issue.toggleAssignee(sess, doer, assigneeID, false)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
go HookQueue.Add(issue.RepoID)
|
||||
return nil
|
||||
|
||||
return removed, comment, nil
|
||||
}
|
||||
|
||||
func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (err error) {
|
||||
// Update the assignee
|
||||
removed, err := updateIssueAssignee(sess, issue, assigneeID)
|
||||
func (issue *Issue) toggleAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (removed bool, comment *Comment, err error) {
|
||||
removed, err = toggleUserAssignee(sess, issue, assigneeID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
|
||||
return false, nil, fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
|
||||
}
|
||||
|
||||
// Repo infos
|
||||
if err = issue.loadRepo(sess); err != nil {
|
||||
return fmt.Errorf("loadRepo: %v", err)
|
||||
return false, nil, fmt.Errorf("loadRepo: %v", err)
|
||||
}
|
||||
|
||||
// Comment
|
||||
if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil {
|
||||
return fmt.Errorf("createAssigneeComment: %v", err)
|
||||
comment, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed)
|
||||
if err != nil {
|
||||
return false, nil, fmt.Errorf("createAssigneeComment: %v", err)
|
||||
}
|
||||
|
||||
// if pull request is in the middle of creation - don't call webhook
|
||||
if isCreate {
|
||||
return nil
|
||||
return removed, comment, err
|
||||
}
|
||||
|
||||
if issue.IsPull {
|
||||
mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypePullRequests)
|
||||
|
||||
if err = issue.loadPullRequest(sess); err != nil {
|
||||
return fmt.Errorf("loadPullRequest: %v", err)
|
||||
return false, nil, fmt.Errorf("loadPullRequest: %v", err)
|
||||
}
|
||||
issue.PullRequest.Issue = issue
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
@@ -190,9 +176,10 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
|
||||
} else {
|
||||
apiPullRequest.Action = api.HookIssueAssigned
|
||||
}
|
||||
// Assignee comment triggers a webhook
|
||||
if err := prepareWebhooks(sess, issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
|
||||
log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
|
||||
return nil
|
||||
return false, nil, err
|
||||
}
|
||||
} else {
|
||||
mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypeIssues)
|
||||
@@ -208,67 +195,50 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
|
||||
} else {
|
||||
apiIssue.Action = api.HookIssueAssigned
|
||||
}
|
||||
// Assignee comment triggers a webhook
|
||||
if err := prepareWebhooks(sess, issue.Repo, HookEventIssues, apiIssue); err != nil {
|
||||
log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
|
||||
return nil
|
||||
return false, nil, err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return removed, comment, nil
|
||||
}
|
||||
|
||||
// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s)
|
||||
// Deleting is done the GitHub way (quote from their api documentation):
|
||||
// https://developer.github.com/v3/issues/#edit-an-issue
|
||||
// "assignees" (array): Logins for Users to assign to this issue.
|
||||
// Pass one or more user logins to replace the set of assignees on this Issue.
|
||||
// Send an empty array ([]) to clear all assignees from the Issue.
|
||||
func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) {
|
||||
var allNewAssignees []*User
|
||||
// toggles user assignee state in database
|
||||
func toggleUserAssignee(e *xorm.Session, issue *Issue, assigneeID int64) (removed bool, err error) {
|
||||
|
||||
// Keep the old assignee thingy for compatibility reasons
|
||||
if oneAssignee != "" {
|
||||
// Prevent double adding assignees
|
||||
var isDouble bool
|
||||
for _, assignee := range multipleAssignees {
|
||||
if assignee == oneAssignee {
|
||||
isDouble = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check if the user exists
|
||||
assignee, err := getUserByID(e, assigneeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !isDouble {
|
||||
multipleAssignees = append(multipleAssignees, oneAssignee)
|
||||
// Check if the submitted user is already assigned, if yes delete him otherwise add him
|
||||
var i int
|
||||
for i = 0; i < len(issue.Assignees); i++ {
|
||||
if issue.Assignees[i].ID == assigneeID {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through all assignees to add them
|
||||
for _, assigneeName := range multipleAssignees {
|
||||
assignee, err := GetUserByName(assigneeName)
|
||||
assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
|
||||
|
||||
toBeDeleted := i < len(issue.Assignees)
|
||||
if toBeDeleted {
|
||||
issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i:]...)
|
||||
_, err = e.Delete(assigneeIn)
|
||||
if err != nil {
|
||||
return err
|
||||
return toBeDeleted, err
|
||||
}
|
||||
|
||||
allNewAssignees = append(allNewAssignees, assignee)
|
||||
}
|
||||
|
||||
// Delete all old assignees not passed
|
||||
if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add all new assignees
|
||||
// Update the assignee. The function will check if the user exists, is already
|
||||
// assigned (which he shouldn't as we deleted all assignees before) and
|
||||
// has access to the repo.
|
||||
for _, assignee := range allNewAssignees {
|
||||
// Extra method to prevent double adding (which would result in removing)
|
||||
err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
|
||||
} else {
|
||||
issue.Assignees = append(issue.Assignees, assignee)
|
||||
_, err = e.Insert(assigneeIn)
|
||||
if err != nil {
|
||||
return err
|
||||
return toBeDeleted, err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return toBeDeleted, nil
|
||||
}
|
||||
|
||||
// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
|
||||
@@ -292,7 +262,7 @@ func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string
|
||||
}
|
||||
|
||||
// Get the IDs of all assignees
|
||||
assigneeIDs = GetUserIDsByNames(multipleAssignees)
|
||||
assigneeIDs, err = GetUserIDsByNames(multipleAssignees, false)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@ func TestUpdateAssignee(t *testing.T) {
|
||||
// Assign multiple users
|
||||
user2, err := GetUserByID(2)
|
||||
assert.NoError(t, err)
|
||||
err = UpdateAssignee(issue, &User{ID: 1}, user2.ID)
|
||||
_, _, err = issue.ToggleAssignee(&User{ID: 1}, user2.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
user3, err := GetUserByID(3)
|
||||
assert.NoError(t, err)
|
||||
err = UpdateAssignee(issue, &User{ID: 1}, user3.ID)
|
||||
_, _, err = issue.ToggleAssignee(&User{ID: 1}, user3.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
user1, err := GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running UpdateAssignee should unassign him
|
||||
assert.NoError(t, err)
|
||||
err = UpdateAssignee(issue, &User{ID: 1}, user1.ID)
|
||||
_, _, err = issue.ToggleAssignee(&User{ID: 1}, user1.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check if he got removed
|
||||
|
||||
+49
-6
@@ -13,12 +13,14 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
||||
@@ -144,10 +146,10 @@ type Comment struct {
|
||||
|
||||
// Reference an issue or pull from another comment, issue or PR
|
||||
// All information is about the origin of the reference
|
||||
RefRepoID int64 `xorm:"index"` // Repo where the referencing
|
||||
RefIssueID int64 `xorm:"index"`
|
||||
RefCommentID int64 `xorm:"index"` // 0 if origin is Issue title or content (or PR's)
|
||||
RefAction XRefAction `xorm:"SMALLINT"` // What hapens if RefIssueID resolves
|
||||
RefRepoID int64 `xorm:"index"` // Repo where the referencing
|
||||
RefIssueID int64 `xorm:"index"`
|
||||
RefCommentID int64 `xorm:"index"` // 0 if origin is Issue title or content (or PR's)
|
||||
RefAction references.XRefAction `xorm:"SMALLINT"` // What hapens if RefIssueID resolves
|
||||
RefIsPull bool
|
||||
|
||||
RefRepo *Repository `xorm:"-"`
|
||||
@@ -355,6 +357,27 @@ func (c *Comment) LoadAttachments() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAttachments update attachments by UUIDs for the comment
|
||||
func (c *Comment) UpdateAttachments(uuids []string) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
attachments, err := getAttachmentsByUUIDs(sess, uuids)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", uuids, err)
|
||||
}
|
||||
for i := 0; i < len(attachments); i++ {
|
||||
attachments[i].IssueID = c.IssueID
|
||||
attachments[i].CommentID = c.ID
|
||||
if err := updateAttachment(sess, attachments[i]); err != nil {
|
||||
return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err)
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees
|
||||
func (c *Comment) LoadAssigneeUser() error {
|
||||
var err error
|
||||
@@ -773,7 +796,7 @@ type CreateCommentOptions struct {
|
||||
RefRepoID int64
|
||||
RefIssueID int64
|
||||
RefCommentID int64
|
||||
RefAction XRefAction
|
||||
RefAction references.XRefAction
|
||||
RefIsPull bool
|
||||
}
|
||||
|
||||
@@ -1021,3 +1044,23 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review
|
||||
func FetchCodeComments(issue *Issue, currentUser *User) (CodeComments, error) {
|
||||
return fetchCodeComments(x, issue, currentUser)
|
||||
}
|
||||
|
||||
// UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id
|
||||
func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error {
|
||||
_, err := x.Table("comment").
|
||||
Where(builder.In("issue_id",
|
||||
builder.Select("issue.id").
|
||||
From("issue").
|
||||
InnerJoin("repository", "issue.repo_id = repository.id").
|
||||
Where(builder.Eq{
|
||||
"repository.original_service_type": tp,
|
||||
}),
|
||||
)).
|
||||
And("comment.original_author_id = ?", originalAuthorID).
|
||||
Update(map[string]interface{}{
|
||||
"poster_id": posterID,
|
||||
"original_author": "",
|
||||
"original_author_id": 0,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
+10
-6
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
|
||||
@@ -68,10 +68,11 @@ type Label struct {
|
||||
Color string `xorm:"VARCHAR(7)"`
|
||||
NumIssues int
|
||||
NumClosedIssues int
|
||||
NumOpenIssues int `xorm:"-"`
|
||||
IsChecked bool `xorm:"-"`
|
||||
QueryString string
|
||||
IsSelected bool
|
||||
NumOpenIssues int `xorm:"-"`
|
||||
IsChecked bool `xorm:"-"`
|
||||
QueryString string `xorm:"-"`
|
||||
IsSelected bool `xorm:"-"`
|
||||
IsExcluded bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// APIFormat converts a Label to the api.Label format
|
||||
@@ -97,7 +98,10 @@ func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64)
|
||||
for _, s := range currentSelectedLabels {
|
||||
if s == label.ID {
|
||||
labelSelected = true
|
||||
} else if s > 0 {
|
||||
} else if -s == label.ID {
|
||||
labelSelected = true
|
||||
label.IsExcluded = true
|
||||
} else if s != 0 {
|
||||
labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10))
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -28,7 +28,6 @@ func updateIssueLock(opts *IssueLockOptions, lock bool) error {
|
||||
}
|
||||
|
||||
opts.Issue.IsLocked = lock
|
||||
|
||||
var commentType CommentType
|
||||
if opts.Issue.IsLocked {
|
||||
commentType = CommentTypeLock
|
||||
@@ -36,16 +35,26 @@ func updateIssueLock(opts *IssueLockOptions, lock bool) error {
|
||||
commentType = CommentTypeUnlock
|
||||
}
|
||||
|
||||
if err := UpdateIssueCols(opts.Issue, "is_locked"); err != nil {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := CreateComment(&CreateCommentOptions{
|
||||
if err := updateIssueCols(sess, opts.Issue, "is_locked"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := createComment(sess, &CreateCommentOptions{
|
||||
Doer: opts.Doer,
|
||||
Issue: opts.Issue,
|
||||
Repo: opts.Issue.Repo,
|
||||
Type: commentType,
|
||||
Content: opts.Reason,
|
||||
})
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
+71
-59
@@ -10,8 +10,9 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"xorm.io/builder"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Milestone represents a milestone of repository.
|
||||
@@ -191,7 +192,6 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 {
|
||||
|
||||
// GetMilestonesByRepoID returns all opened milestones of a repository.
|
||||
func GetMilestonesByRepoID(repoID int64, state api.StateType) (MilestoneList, error) {
|
||||
|
||||
sess := x.Where("repo_id = ?", repoID)
|
||||
|
||||
switch state {
|
||||
@@ -238,13 +238,34 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile
|
||||
}
|
||||
|
||||
func updateMilestone(e Engine, m *Milestone) error {
|
||||
_, err := e.ID(m.ID).AllCols().Update(m)
|
||||
_, err := e.ID(m.ID).AllCols().
|
||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||
builder.Eq{"milestone_id": m.ID},
|
||||
)).
|
||||
SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
|
||||
builder.Eq{
|
||||
"milestone_id": m.ID,
|
||||
"is_closed": true,
|
||||
},
|
||||
)).
|
||||
Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateMilestone updates information of given milestone.
|
||||
func UpdateMilestone(m *Milestone) error {
|
||||
return updateMilestone(x, m)
|
||||
if err := updateMilestone(x, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return updateMilestoneCompleteness(x, m.ID)
|
||||
}
|
||||
|
||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
|
||||
_, err := e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
|
||||
milestoneID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func countRepoMilestones(e Engine, repoID int64) (int64, error) {
|
||||
@@ -278,11 +299,6 @@ func MilestoneStats(repoID int64) (open int64, closed int64, err error) {
|
||||
|
||||
// ChangeMilestoneStatus changes the milestone open/closed status.
|
||||
func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
|
||||
repo, err := GetRepositoryByID(m.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
@@ -290,92 +306,88 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
|
||||
}
|
||||
|
||||
m.IsClosed = isClosed
|
||||
if err = updateMilestone(sess, m); err != nil {
|
||||
if _, err := sess.ID(m.ID).Cols("is_closed").Update(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numMilestones, err := countRepoMilestones(sess, repo.ID)
|
||||
if err != nil {
|
||||
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
|
||||
return err
|
||||
}
|
||||
numClosedMilestones, err := countRepoClosedMilestones(sess, repo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repo.NumMilestones = int(numMilestones)
|
||||
repo.NumClosedMilestones = int(numClosedMilestones)
|
||||
|
||||
if _, err = sess.ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
|
||||
if issue.MilestoneID == 0 {
|
||||
return nil
|
||||
func updateRepoMilestoneNum(e Engine, repoID int64) error {
|
||||
_, err := e.Exec("UPDATE `repository` SET num_milestones=(SELECT count(*) FROM milestone WHERE repo_id=?),num_closed_milestones=(SELECT count(*) FROM milestone WHERE repo_id=? AND is_closed=?) WHERE id=?",
|
||||
repoID,
|
||||
repoID,
|
||||
true,
|
||||
repoID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func updateMilestoneTotalNum(e Engine, milestoneID int64) (err error) {
|
||||
if _, err = e.Exec("UPDATE `milestone` SET num_issues=(SELECT count(*) FROM issue WHERE milestone_id=?) WHERE id=?",
|
||||
milestoneID,
|
||||
milestoneID,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||
if err != nil {
|
||||
return err
|
||||
return updateMilestoneCompleteness(e, milestoneID)
|
||||
}
|
||||
|
||||
func updateMilestoneClosedNum(e Engine, milestoneID int64) (err error) {
|
||||
if _, err = e.Exec("UPDATE `milestone` SET num_closed_issues=(SELECT count(*) FROM issue WHERE milestone_id=? AND is_closed=?) WHERE id=?",
|
||||
milestoneID,
|
||||
true,
|
||||
milestoneID,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if issue.IsClosed {
|
||||
m.NumOpenIssues--
|
||||
m.NumClosedIssues++
|
||||
} else {
|
||||
m.NumOpenIssues++
|
||||
m.NumClosedIssues--
|
||||
}
|
||||
|
||||
return updateMilestone(e, m)
|
||||
return updateMilestoneCompleteness(e, milestoneID)
|
||||
}
|
||||
|
||||
func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilestoneID int64) error {
|
||||
if err := updateIssueCols(e, issue, "milestone_id"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if oldMilestoneID > 0 {
|
||||
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
|
||||
if err != nil {
|
||||
if err := updateMilestoneTotalNum(e, oldMilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.NumIssues--
|
||||
if issue.IsClosed {
|
||||
m.NumClosedIssues--
|
||||
}
|
||||
|
||||
if err = updateMilestone(e, m); err != nil {
|
||||
return err
|
||||
if err := updateMilestoneClosedNum(e, oldMilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if issue.MilestoneID > 0 {
|
||||
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||
if err != nil {
|
||||
if err := updateMilestoneTotalNum(e, issue.MilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.NumIssues++
|
||||
if issue.IsClosed {
|
||||
m.NumClosedIssues++
|
||||
if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = updateMilestone(e, m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := issue.loadRepo(e); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if oldMilestoneID > 0 || issue.MilestoneID > 0 {
|
||||
if err := issue.loadRepo(e); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := createMilestoneComment(e, doer, issue.Repo, issue, oldMilestoneID, issue.MilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return updateIssueCols(e, issue, "milestone_id")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
||||
|
||||
@@ -231,7 +231,7 @@ func TestChangeMilestoneStatus(t *testing.T) {
|
||||
CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
|
||||
}
|
||||
|
||||
func TestChangeMilestoneIssueStats(t *testing.T) {
|
||||
func TestUpdateMilestoneClosedNum(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
|
||||
"is_closed=0").(*Issue)
|
||||
@@ -240,14 +240,14 @@ func TestChangeMilestoneIssueStats(t *testing.T) {
|
||||
issue.ClosedUnix = timeutil.TimeStampNow()
|
||||
_, err := x.Cols("is_closed", "closed_unix").Update(issue)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
|
||||
assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
|
||||
CheckConsistencyFor(t, &Milestone{})
|
||||
|
||||
issue.IsClosed = false
|
||||
issue.ClosedUnix = 0
|
||||
_, err = x.Cols("is_closed", "closed_unix").Update(issue)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
|
||||
assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
|
||||
CheckConsistencyFor(t, &Milestone{})
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Reaction represents a reactions on issues and comments.
|
||||
|
||||
+34
-49
@@ -84,53 +84,6 @@ func TestGetParticipantsByIssueID(t *testing.T) {
|
||||
checkParticipants(1, []int{5})
|
||||
}
|
||||
|
||||
func TestIssue_AddLabel(t *testing.T) {
|
||||
var tests = []struct {
|
||||
issueID int64
|
||||
labelID int64
|
||||
doerID int64
|
||||
}{
|
||||
{1, 2, 2}, // non-pull-request, not-already-added label
|
||||
{1, 1, 2}, // non-pull-request, already-added label
|
||||
{2, 2, 2}, // pull-request, not-already-added label
|
||||
{2, 1, 2}, // pull-request, already-added label
|
||||
}
|
||||
for _, test := range tests {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
issue := AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
|
||||
label := AssertExistsAndLoadBean(t, &Label{ID: test.labelID}).(*Label)
|
||||
doer := AssertExistsAndLoadBean(t, &User{ID: test.doerID}).(*User)
|
||||
assert.NoError(t, issue.AddLabel(doer, label))
|
||||
AssertExistsAndLoadBean(t, &IssueLabel{IssueID: test.issueID, LabelID: test.labelID})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue_AddLabels(t *testing.T) {
|
||||
var tests = []struct {
|
||||
issueID int64
|
||||
labelIDs []int64
|
||||
doerID int64
|
||||
}{
|
||||
{1, []int64{1, 2}, 2}, // non-pull-request
|
||||
{1, []int64{}, 2}, // non-pull-request, empty
|
||||
{2, []int64{1, 2}, 2}, // pull-request
|
||||
{2, []int64{}, 1}, // pull-request, empty
|
||||
}
|
||||
for _, test := range tests {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
issue := AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
|
||||
labels := make([]*Label, len(test.labelIDs))
|
||||
for i, labelID := range test.labelIDs {
|
||||
labels[i] = AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label)
|
||||
}
|
||||
doer := AssertExistsAndLoadBean(t, &User{ID: test.doerID}).(*User)
|
||||
assert.NoError(t, issue.AddLabels(doer, labels))
|
||||
for _, labelID := range test.labelIDs {
|
||||
AssertExistsAndLoadBean(t, &IssueLabel{IssueID: test.issueID, LabelID: labelID})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue_ClearLabels(t *testing.T) {
|
||||
var tests = []struct {
|
||||
issueID int64
|
||||
@@ -160,7 +113,7 @@ func TestUpdateIssueCols(t *testing.T) {
|
||||
issue.Content = "This should have no effect"
|
||||
|
||||
now := time.Now().Unix()
|
||||
assert.NoError(t, UpdateIssueCols(issue, "name"))
|
||||
assert.NoError(t, updateIssueCols(x, issue, "name"))
|
||||
then := time.Now().Unix()
|
||||
|
||||
updatedIssue := AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
|
||||
@@ -344,7 +297,7 @@ func testInsertIssue(t *testing.T, title, content string) {
|
||||
Title: title,
|
||||
Content: content,
|
||||
}
|
||||
err := NewIssue(repo, &issue, nil, nil, nil)
|
||||
err := NewIssue(repo, &issue, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var newIssue Issue
|
||||
@@ -366,3 +319,35 @@ func TestIssue_InsertIssue(t *testing.T) {
|
||||
testInsertIssue(t, "my issue1", "special issue's comments?")
|
||||
testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?")
|
||||
}
|
||||
|
||||
func TestIssue_ResolveMentions(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
|
||||
o := AssertExistsAndLoadBean(t, &User{LowerName: owner}).(*User)
|
||||
r := AssertExistsAndLoadBean(t, &Repository{OwnerID: o.ID, LowerName: repo}).(*Repository)
|
||||
issue := &Issue{RepoID: r.ID}
|
||||
d := AssertExistsAndLoadBean(t, &User{LowerName: doer}).(*User)
|
||||
resolved, err := issue.ResolveMentionsByVisibility(DefaultDBContext(), d, mentions)
|
||||
assert.NoError(t, err)
|
||||
ids := make([]int64, len(resolved))
|
||||
for i, user := range resolved {
|
||||
ids[i] = user.ID
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
assert.EqualValues(t, expected, ids)
|
||||
}
|
||||
|
||||
// Public repo, existing user
|
||||
testSuccess("user2", "repo1", "user1", []string{"user5"}, []int64{5})
|
||||
// Public repo, non-existing user
|
||||
testSuccess("user2", "repo1", "user1", []string{"nonexisting"}, []int64{})
|
||||
// Public repo, doer
|
||||
testSuccess("user2", "repo1", "user1", []string{"user1"}, []int64{})
|
||||
// Private repo, team member
|
||||
testSuccess("user17", "big_test_private_4", "user20", []string{"user2"}, []int64{2})
|
||||
// Private repo, not a team member
|
||||
testSuccess("user17", "big_test_private_4", "user20", []string{"user5"}, []int64{})
|
||||
// Private repo, whole team
|
||||
testSuccess("user17", "big_test_private_4", "user15", []string{"owners"}, []int64{18})
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// TrackedTime represents a time that was spent for a specific issue.
|
||||
|
||||
@@ -6,8 +6,6 @@ package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
// IssueUser represents an issue-user relation.
|
||||
@@ -51,42 +49,6 @@ func newIssueUsers(e Engine, repo *Repository, issue *Issue) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateIssueAssignee(e *xorm.Session, issue *Issue, assigneeID int64) (removed bool, err error) {
|
||||
|
||||
// Check if the user exists
|
||||
assignee, err := getUserByID(e, assigneeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if the submitted user is already assigne, if yes delete him otherwise add him
|
||||
var i int
|
||||
for i = 0; i < len(issue.Assignees); i++ {
|
||||
if issue.Assignees[i].ID == assigneeID {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
|
||||
|
||||
toBeDeleted := i < len(issue.Assignees)
|
||||
if toBeDeleted {
|
||||
issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i:]...)
|
||||
_, err = e.Delete(assigneeIn)
|
||||
if err != nil {
|
||||
return toBeDeleted, err
|
||||
}
|
||||
} else {
|
||||
issue.Assignees = append(issue.Assignees, assignee)
|
||||
_, err = e.Insert(assigneeIn)
|
||||
if err != nil {
|
||||
return toBeDeleted, err
|
||||
}
|
||||
}
|
||||
|
||||
return toBeDeleted, nil
|
||||
}
|
||||
|
||||
// UpdateIssueUserByRead updates issue-user relation for reading.
|
||||
func UpdateIssueUserByRead(uid, issueID int64) error {
|
||||
_, err := x.Exec("UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?", true, uid, issueID)
|
||||
|
||||
+39
-72
@@ -5,42 +5,16 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO: Unify all regexp treatment of cross references in one place
|
||||
|
||||
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
||||
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(?:#)([0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`)
|
||||
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
||||
// e.g. gogits/gogs#12345
|
||||
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)#([0-9]+)(?:\s|$|\)|\]|\.(\s|$))`)
|
||||
)
|
||||
|
||||
// XRefAction represents the kind of effect a cross reference has once is resolved
|
||||
type XRefAction int64
|
||||
|
||||
const (
|
||||
// XRefActionNone means the cross-reference is a mention (commit, etc.)
|
||||
XRefActionNone XRefAction = iota // 0
|
||||
// XRefActionCloses means the cross-reference should close an issue if it is resolved
|
||||
XRefActionCloses // 1 - not implemented yet
|
||||
// XRefActionReopens means the cross-reference should reopen an issue if it is resolved
|
||||
XRefActionReopens // 2 - Not implemented yet
|
||||
// XRefActionNeutered means the cross-reference will no longer affect the source
|
||||
XRefActionNeutered // 3
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type crossReference struct {
|
||||
Issue *Issue
|
||||
Action XRefAction
|
||||
Action references.XRefAction
|
||||
}
|
||||
|
||||
// crossReferencesContext is context to pass along findCrossReference functions
|
||||
@@ -72,7 +46,7 @@ func newCrossReference(e *xorm.Session, ctx *crossReferencesContext, xref *cross
|
||||
|
||||
func neuterCrossReferences(e Engine, issueID int64, commentID int64) error {
|
||||
active := make([]*Comment, 0, 10)
|
||||
sess := e.Where("`ref_action` IN (?, ?, ?)", XRefActionNone, XRefActionCloses, XRefActionReopens)
|
||||
sess := e.Where("`ref_action` IN (?, ?, ?)", references.XRefActionNone, references.XRefActionCloses, references.XRefActionReopens)
|
||||
if issueID != 0 {
|
||||
sess = sess.And("`ref_issue_id` = ?", issueID)
|
||||
}
|
||||
@@ -86,7 +60,7 @@ func neuterCrossReferences(e Engine, issueID int64, commentID int64) error {
|
||||
for i, c := range active {
|
||||
ids[i] = c.ID
|
||||
}
|
||||
_, err := e.In("id", ids).Cols("`ref_action`").Update(&Comment{RefAction: XRefActionNeutered})
|
||||
_, err := e.In("id", ids).Cols("`ref_action`").Update(&Comment{RefAction: references.XRefActionNeutered})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -110,11 +84,11 @@ func (issue *Issue) addCrossReferences(e *xorm.Session, doer *User) error {
|
||||
Doer: doer,
|
||||
OrigIssue: issue,
|
||||
}
|
||||
return issue.createCrossReferences(e, ctx, issue.Title+"\n"+issue.Content)
|
||||
return issue.createCrossReferences(e, ctx, issue.Title, issue.Content)
|
||||
}
|
||||
|
||||
func (issue *Issue) createCrossReferences(e *xorm.Session, ctx *crossReferencesContext, content string) error {
|
||||
xreflist, err := ctx.OrigIssue.getCrossReferences(e, ctx, content)
|
||||
func (issue *Issue) createCrossReferences(e *xorm.Session, ctx *crossReferencesContext, plaincontent, mdcontent string) error {
|
||||
xreflist, err := ctx.OrigIssue.getCrossReferences(e, ctx, plaincontent, mdcontent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -126,47 +100,43 @@ func (issue *Issue) createCrossReferences(e *xorm.Session, ctx *crossReferencesC
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) getCrossReferences(e *xorm.Session, ctx *crossReferencesContext, content string) ([]*crossReference, error) {
|
||||
func (issue *Issue) getCrossReferences(e *xorm.Session, ctx *crossReferencesContext, plaincontent, mdcontent string) ([]*crossReference, error) {
|
||||
xreflist := make([]*crossReference, 0, 5)
|
||||
var xref *crossReference
|
||||
var (
|
||||
refRepo *Repository
|
||||
refIssue *Issue
|
||||
err error
|
||||
)
|
||||
|
||||
// Issues in the same repository
|
||||
// FIXME: Should we support IssueNameStyleAlphanumeric?
|
||||
matches := issueNumericPattern.FindAllStringSubmatch(content, -1)
|
||||
for _, match := range matches {
|
||||
if index, err := strconv.ParseInt(match[1], 10, 64); err == nil {
|
||||
if err = ctx.OrigIssue.loadRepo(e); err != nil {
|
||||
allrefs := append(references.FindAllIssueReferences(plaincontent), references.FindAllIssueReferencesMarkdown(mdcontent)...)
|
||||
|
||||
for _, ref := range allrefs {
|
||||
if ref.Owner == "" && ref.Name == "" {
|
||||
// Issues in the same repository
|
||||
if err := ctx.OrigIssue.loadRepo(e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xref, err = ctx.OrigIssue.isValidCommentReference(e, ctx, issue.Repo, index); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xref != nil {
|
||||
xreflist = ctx.OrigIssue.updateCrossReferenceList(xreflist, xref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issues in other repositories
|
||||
matches = crossReferenceIssueNumericPattern.FindAllStringSubmatch(content, -1)
|
||||
for _, match := range matches {
|
||||
if index, err := strconv.ParseInt(match[3], 10, 64); err == nil {
|
||||
repo, err := getRepositoryByOwnerAndName(e, match[1], match[2])
|
||||
refRepo = ctx.OrigIssue.Repo
|
||||
} else {
|
||||
// Issues in other repositories
|
||||
refRepo, err = getRepositoryByOwnerAndName(e, ref.Owner, ref.Name)
|
||||
if err != nil {
|
||||
if IsErrRepoNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err = ctx.OrigIssue.loadRepo(e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xref, err = issue.isValidCommentReference(e, ctx, repo, index); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xref != nil {
|
||||
xreflist = issue.updateCrossReferenceList(xreflist, xref)
|
||||
}
|
||||
}
|
||||
if refIssue, err = ctx.OrigIssue.findReferencedIssue(e, ctx, refRepo, ref.Index); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if refIssue != nil {
|
||||
xreflist = ctx.OrigIssue.updateCrossReferenceList(xreflist, &crossReference{
|
||||
Issue: refIssue,
|
||||
// FIXME: currently ignore keywords
|
||||
// Action: ref.Action,
|
||||
Action: references.XRefActionNone,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +149,7 @@ func (issue *Issue) updateCrossReferenceList(list []*crossReference, xref *cross
|
||||
}
|
||||
for i, r := range list {
|
||||
if r.Issue.ID == xref.Issue.ID {
|
||||
if xref.Action != XRefActionNone {
|
||||
if xref.Action != references.XRefActionNone {
|
||||
list[i].Action = xref.Action
|
||||
}
|
||||
return list
|
||||
@@ -188,7 +158,7 @@ func (issue *Issue) updateCrossReferenceList(list []*crossReference, xref *cross
|
||||
return append(list, xref)
|
||||
}
|
||||
|
||||
func (issue *Issue) isValidCommentReference(e Engine, ctx *crossReferencesContext, repo *Repository, index int64) (*crossReference, error) {
|
||||
func (issue *Issue) findReferencedIssue(e Engine, ctx *crossReferencesContext, repo *Repository, index int64) (*Issue, error) {
|
||||
refIssue := &Issue{RepoID: repo.ID, Index: index}
|
||||
if has, _ := e.Get(refIssue); !has {
|
||||
return nil, nil
|
||||
@@ -206,10 +176,7 @@ func (issue *Issue) isValidCommentReference(e Engine, ctx *crossReferencesContex
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return &crossReference{
|
||||
Issue: refIssue,
|
||||
Action: XRefActionNone,
|
||||
}, nil
|
||||
return refIssue, nil
|
||||
}
|
||||
|
||||
func (issue *Issue) neuterCrossReferences(e Engine) error {
|
||||
@@ -237,7 +204,7 @@ func (comment *Comment) addCrossReferences(e *xorm.Session, doer *User) error {
|
||||
OrigIssue: comment.Issue,
|
||||
OrigComment: comment,
|
||||
}
|
||||
return comment.Issue.createCrossReferences(e, ctx, comment.Content)
|
||||
return comment.Issue.createCrossReferences(e, ctx, "", comment.Content)
|
||||
}
|
||||
|
||||
func (comment *Comment) neuterCrossReferences(e Engine) error {
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// LFSLock represents a git lfs lock of repository.
|
||||
|
||||
@@ -21,9 +21,9 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// LoginType represents an login type.
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
package models
|
||||
|
||||
import "github.com/go-xorm/xorm"
|
||||
import "xorm.io/xorm"
|
||||
|
||||
// InsertMilestones creates milestones of repository.
|
||||
func InsertMilestones(ms ...*Milestone) (err error) {
|
||||
|
||||
@@ -21,10 +21,10 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
"github.com/unknwon/com"
|
||||
ini "gopkg.in/ini.v1"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
const minDBVersion = 4
|
||||
@@ -253,6 +253,18 @@ var migrations = []Migration{
|
||||
// v98 -> v99
|
||||
NewMigration("add original author name and id on migrated release", addOriginalAuthorOnMigratedReleases),
|
||||
// v99 -> v100
|
||||
NewMigration("add task table and status column for repository table", addTaskTable),
|
||||
// v100 -> v101
|
||||
NewMigration("update migration repositories' service type", updateMigrationServiceTypes),
|
||||
// v101 -> v102
|
||||
NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser),
|
||||
// v102 -> v103
|
||||
NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest),
|
||||
// v103 -> v104
|
||||
NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches),
|
||||
// v104 -> v105
|
||||
NewMigration("remove unnecessary columns from label", removeLabelUneededCols),
|
||||
// v105 -> v106
|
||||
NewMigration("add includes_all_repositories to teams", addTeamIncludesAllRepositories),
|
||||
}
|
||||
|
||||
@@ -406,9 +418,11 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
|
||||
}
|
||||
for _, index := range res {
|
||||
indexName := index["column_name"]
|
||||
_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
|
||||
if err != nil {
|
||||
return err
|
||||
if len(indexName) > 0 {
|
||||
_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2019 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 migrations
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func updateMigrationServiceTypes(x *xorm.Engine) error {
|
||||
type Repository struct {
|
||||
ID int64
|
||||
OriginalServiceType int `xorm:"index default(0)"`
|
||||
OriginalURL string `xorm:"VARCHAR(2048)"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var last int
|
||||
const batchSize = 50
|
||||
for {
|
||||
var results = make([]Repository, 0, batchSize)
|
||||
err := x.Where("original_url <> '' AND original_url IS NOT NULL").
|
||||
And("original_service_type = 0 OR original_service_type IS NULL").
|
||||
OrderBy("id").
|
||||
Limit(batchSize, last).
|
||||
Find(&results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
break
|
||||
}
|
||||
last += len(results)
|
||||
|
||||
const PlainGitService = 1 // 1 plain git service
|
||||
const GithubService = 2 // 2 github.com
|
||||
|
||||
for _, res := range results {
|
||||
u, err := url.Parse(res.OriginalURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var serviceType = PlainGitService
|
||||
if strings.EqualFold(u.Host, "github.com") {
|
||||
serviceType = GithubService
|
||||
}
|
||||
_, err = x.Exec("UPDATE repository SET original_service_type = ? WHERE id = ?", serviceType, res.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ExternalLoginUser struct {
|
||||
ExternalID string `xorm:"pk NOT NULL"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
LoginSourceID int64 `xorm:"pk NOT NULL"`
|
||||
RawData map[string]interface{} `xorm:"TEXT JSON"`
|
||||
Provider string `xorm:"index VARCHAR(25)"`
|
||||
Email string
|
||||
Name string
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
Description string
|
||||
AvatarURL string
|
||||
Location string
|
||||
AccessToken string
|
||||
AccessTokenSecret string
|
||||
RefreshToken string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
return x.Sync2(new(ExternalLoginUser))
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019 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 migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func changeSomeColumnsLengthOfExternalLoginUser(x *xorm.Engine) error {
|
||||
type ExternalLoginUser struct {
|
||||
AccessToken string `xorm:"TEXT"`
|
||||
AccessTokenSecret string `xorm:"TEXT"`
|
||||
RefreshToken string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(ExternalLoginUser))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 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 migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func dropColumnHeadUserNameOnPullRequest(x *xorm.Engine) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
return dropTableColumns(sess, "pull_request", "head_user_name")
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2019 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 migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addWhitelistDeployKeysToBranches(x *xorm.Engine) error {
|
||||
type ProtectedBranch struct {
|
||||
ID int64
|
||||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(ProtectedBranch))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2019 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 migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeLabelUneededCols(x *xorm.Engine) error {
|
||||
|
||||
// Make sure the columns exist before dropping them
|
||||
type Label struct {
|
||||
QueryString string
|
||||
IsSelected bool
|
||||
}
|
||||
if err := x.Sync2(new(Label)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dropTableColumns(sess, "label", "query_string"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dropTableColumns(sess, "label", "is_selected"); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 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 migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addTeamIncludesAllRepositories(x *xorm.Engine) error {
|
||||
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(Team)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
|
||||
true, "Owners")
|
||||
return err
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func ldapUseSSLToSecurityProtocol(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func setCommentUpdatedWithCreated(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func createAllowCreateOrganizationColumn(x *xorm.Engine) error {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Enumerate all the unit types
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func setProtectedBranchUpdatedWithCreated(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func useNewNameAvatars(x *xorm.Engine) error {
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func generateAndMigrateWikiGitHooks(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// UserOpenID is the list of all OpenID identities of a user.
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func changeGPGKeysColumns(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addUserOpenIDShow(x *xorm.Engine) error {
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func generateAndMigrateGitHookChains(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func convertIntervalToDuration(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addRepoSize(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CommitStatus see models/status.go
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addExternalLoginUserPK(x *xorm.Engine) error {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addLoginSourceSyncEnabledColumn(x *xorm.Engine) error {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
package migrations
|
||||
|
||||
import "github.com/go-xorm/xorm"
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func addUnitsToRepoTeam(x *xorm.Engine) error {
|
||||
type Team struct {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeActionColumns(x *xorm.Engine) error {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Team see models/team.go
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addCommentIDToAction(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func regenerateGitHooks36(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"html"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func unescapeUserFullNames(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeCommitsUnitType(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// ReleaseV39 describes the added field for Release
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeDuplicateUnitTypes(x *xorm.Engine) error {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeIndexColumnFromRepoUnitTable(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeOrganizationWatchRepo(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addDeletedBranch(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addRepoIndexerStatus(x *xorm.Engine) error {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addTimetracking(x *xorm.Engine) error {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func migrateProtectedBranchStruct(x *xorm.Engine) error {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addLFSLock(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addReactions(x *xorm.Engine) error {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addPullRequestOptions(x *xorm.Engine) error {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addModeToDeploKeys(x *xorm.Engine) error {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addIssueClosedTime(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addLabelsDescriptions(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addProtectedBranchMergeWhitelist(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addFsckEnabledToRepo(x *xorm.Engine) error {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addSizeToAttachment(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addLastUsedPasscodeTOTP(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addLanguageSetting(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addMultipleAssignees(x *xorm.Engine) error {
|
||||
|
||||
@@ -3,7 +3,7 @@ package migrations
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addU2FReg(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addLoginSourceIDToPublicKeyTable(x *xorm.Engine) error {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func removeStaleWatches(x *xorm.Engine) error {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func moveTeamUnitsToTeamUnitTable(x *xorm.Engine) error {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addIssueDependencies(x *xorm.Engine) (err error) {
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addScratchHash(x *xorm.Engine) error {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addReview(x *xorm.Engine) error {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addMustChangePassword(x *xorm.Engine) error {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
package migrations
|
||||
|
||||
import "github.com/go-xorm/xorm"
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func addApprovalWhitelistsToProtectedBranches(x *xorm.Engine) error {
|
||||
type ProtectedBranch struct {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func clearNonusedData(x *xorm.Engine) error {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addPullRequestRebaseWithMerge(x *xorm.Engine) error {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addUserDefaultTheme(x *xorm.Engine) error {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func renameRepoIsBareToIsEmpty(x *xorm.Engine) error {
|
||||
|
||||
@@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addCanCloseIssuesViaCommitInAnyBranch(x *xorm.Engine) error {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user