diff options
Diffstat (limited to 'models')
-rw-r--r-- | models/access.go | 27 | ||||
-rw-r--r-- | models/action.go | 4 | ||||
-rw-r--r-- | models/error.go | 18 | ||||
-rw-r--r-- | models/fixtures/access_token.yml | 23 | ||||
-rw-r--r-- | models/fixtures/update_task.yml | 20 | ||||
-rw-r--r-- | models/git_diff.go | 16 | ||||
-rw-r--r-- | models/git_diff_test.go | 16 | ||||
-rw-r--r-- | models/graph.go | 14 | ||||
-rw-r--r-- | models/graph_test.go | 30 | ||||
-rw-r--r-- | models/issue.go | 4 | ||||
-rw-r--r-- | models/issue_label.go | 2 | ||||
-rw-r--r-- | models/issue_mail.go | 2 | ||||
-rw-r--r-- | models/migrations/migrations.go | 4 | ||||
-rw-r--r-- | models/migrations/v15.go | 30 | ||||
-rw-r--r-- | models/models.go | 31 | ||||
-rw-r--r-- | models/notification.go | 2 | ||||
-rw-r--r-- | models/org.go | 6 | ||||
-rw-r--r-- | models/pull.go | 16 | ||||
-rw-r--r-- | models/release.go | 74 | ||||
-rw-r--r-- | models/repo.go | 13 | ||||
-rw-r--r-- | models/repo_mirror.go | 2 | ||||
-rw-r--r-- | models/setup_for_test.go | 47 | ||||
-rw-r--r-- | models/ssh_key.go | 4 | ||||
-rw-r--r-- | models/token_test.go | 107 | ||||
-rw-r--r-- | models/update_test.go | 124 | ||||
-rw-r--r-- | models/user.go | 32 | ||||
-rw-r--r-- | models/user_mail.go | 4 | ||||
-rw-r--r-- | models/webhook_slack.go | 4 |
28 files changed, 571 insertions, 105 deletions
diff --git a/models/access.go b/models/access.go index c77e7f0a16..49a8838ea6 100644 --- a/models/access.go +++ b/models/access.go @@ -96,28 +96,31 @@ func HasAccess(user *User, repo *Repository, testMode AccessMode) (bool, error) return hasAccess(x, user, repo, testMode) } +type repoAccess struct { + Access `xorm:"extends"` + Repository `xorm:"extends"` +} + +func (repoAccess) TableName() string { + return "access" +} + // GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own. func (user *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) { - accesses := make([]*Access, 0, 10) - type RepoAccess struct { - Access `xorm:"extends"` - Repository `xorm:"extends"` - } - rows, err := x. - Join("INNER", "repository", "respository.id = access.repo_id"). + Join("INNER", "repository", "repository.id = access.repo_id"). Where("access.user_id = ?", user.ID). And("repository.owner_id <> ?", user.ID). - Rows(new(RepoAccess)) + Rows(new(repoAccess)) if err != nil { return nil, err } defer rows.Close() - var repos = make(map[*Repository]AccessMode, len(accesses)) - var ownerCache = make(map[int64]*User, len(accesses)) + var repos = make(map[*Repository]AccessMode, 10) + var ownerCache = make(map[int64]*User, 10) for rows.Next() { - var repo RepoAccess + var repo repoAccess err = rows.Scan(&repo) if err != nil { return nil, err @@ -163,7 +166,7 @@ func maxAccessMode(modes ...AccessMode) AccessMode { return max } -// FIXME: do corss-comparison so reduce deletions and additions to the minimum? +// FIXME: do cross-comparison so reduce deletions and additions to the minimum? func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) { minMode := AccessModeRead if !repo.IsPrivate { diff --git a/models/action.go b/models/action.go index f84d42d0cb..8b3d82b4d6 100644 --- a/models/action.go +++ b/models/action.go @@ -145,7 +145,7 @@ func (a *Action) GetRepoPath() string { } // ShortRepoPath returns the virtual path to the action repository -// trimed to max 20 + 1 + 33 chars. +// trimmed to max 20 + 1 + 33 chars. func (a *Action) ShortRepoPath() string { return path.Join(a.ShortRepoUserName(), a.ShortRepoName()) } @@ -418,7 +418,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err } } - // It is conflict to have close and reopen at same time, so refsMarkd doesn't need to reinit here. + // It is conflict to have close and reopen at same time, so refsMarked doesn't need to reinit here. for _, ref := range issueReopenKeywordsPat.FindAllString(c.Message, -1) { ref = ref[strings.IndexByte(ref, byte(' '))+1:] ref = strings.TrimRightFunc(ref, issueIndexTrimRight) diff --git a/models/error.go b/models/error.go index d11a9eeb1f..edbb6003e9 100644 --- a/models/error.go +++ b/models/error.go @@ -123,6 +123,20 @@ func (err ErrUserHasOrgs) Error() string { return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID) } +// ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error. +type ErrUserNotAllowedCreateOrg struct { +} + +// IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg. +func IsErrUserNotAllowedCreateOrg(err error) bool { + _, ok := err.(ErrUserNotAllowedCreateOrg) + return ok +} + +func (err ErrUserNotAllowedCreateOrg) Error() string { + return fmt.Sprintf("user is not allowed to create organizations") +} + // ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error. type ErrReachLimitOfRepo struct { Limit int @@ -569,7 +583,7 @@ type ErrPullRequestNotExist struct { IssueID int64 HeadRepoID int64 BaseRepoID int64 - HeadBarcnh string + HeadBranch string BaseBranch string } @@ -581,7 +595,7 @@ func IsErrPullRequestNotExist(err error) bool { func (err ErrPullRequestNotExist) Error() string { return fmt.Sprintf("pull request does not exist [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", - err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBarcnh, err.BaseBranch) + err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) } // ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error diff --git a/models/fixtures/access_token.yml b/models/fixtures/access_token.yml new file mode 100644 index 0000000000..9572709dd9 --- /dev/null +++ b/models/fixtures/access_token.yml @@ -0,0 +1,23 @@ +- + id: 1 + uid: 1 + name: Token A + sha1: hash1 + created_unix: 946687980 + updated_unix: 946687980 + +- + id: 2 + uid: 1 + name: Token B + sha1: hash2 + created_unix: 946687980 + updated_unix: 946687980 + +- + id: 3 + uid: 2 + name: Token A + sha1: hash3 + created_unix: 946687980 + updated_unix: 946687980 diff --git a/models/fixtures/update_task.yml b/models/fixtures/update_task.yml new file mode 100644 index 0000000000..ffcd5fd6fa --- /dev/null +++ b/models/fixtures/update_task.yml @@ -0,0 +1,20 @@ +- + id: 1 + uuid: uuid1 + ref_name: refName1 + old_commit_id: oldCommitId1 + new_commit_id: newCommitId1 + +- + id: 2 + uuid: uuid2 + ref_name: refName2 + old_commit_id: oldCommitId2 + new_commit_id: newCommitId2 + +- + id: 3 + uuid: uuid3 + ref_name: refName3 + old_commit_id: oldCommitId3 + new_commit_id: newCommitId3 diff --git a/models/git_diff.go b/models/git_diff.go index f796f286d6..eb88d210d4 100644 --- a/models/git_diff.go +++ b/models/git_diff.go @@ -78,7 +78,7 @@ var ( func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML { buf := bytes.NewBuffer(nil) - // Reproduce signs which are cutted for inline diff before. + // Reproduce signs which are cut for inline diff before. switch lineType { case DiffLineAdd: buf.WriteByte('+') @@ -234,7 +234,7 @@ const cmdDiffHead = "diff --git " // ParsePatch builds a Diff object from a io.Reader and some // parameters. // TODO: move this function to gogits/git-module -func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) { +func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*Diff, error) { var ( diff = &Diff{Files: make([]*DiffFile, 0)} @@ -295,8 +295,8 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (* curFileLinesCount++ lineCount++ - // Diff data too large, we only show the first about maxlines lines - if curFileLinesCount >= maxLines || len(line) >= maxLineCharacteres { + // Diff data too large, we only show the first about maxLines lines + if curFileLinesCount >= maxLines || len(line) >= maxLineCharacters { curFile.IsIncomplete = true } @@ -447,7 +447,7 @@ func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (* // GetDiffRange builds a Diff between two commits of a repository. // passing the empty string as beforeCommitID returns a diff from the // parent commit. -func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) { +func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) { gitRepo, err := git.OpenRepository(repoPath) if err != nil { return nil, err @@ -486,7 +486,7 @@ func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxL pid := process.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd) defer process.Remove(pid) - diff, err := ParsePatch(maxLines, maxLineCharacteres, maxFiles, stdout) + diff, err := ParsePatch(maxLines, maxLineCharacters, maxFiles, stdout) if err != nil { return nil, fmt.Errorf("ParsePatch: %v", err) } @@ -554,6 +554,6 @@ func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Write } // GetDiffCommit builds a Diff representing the given commitID. -func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) { - return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles) +func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) { + return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacters, maxFiles) } diff --git a/models/git_diff_test.go b/models/git_diff_test.go index cf98f65399..f88e6fb610 100644 --- a/models/git_diff_test.go +++ b/models/git_diff_test.go @@ -20,16 +20,16 @@ func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) { func TestDiffToHTML(t *testing.T) { assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{ - dmp.Diff{dmp.DiffEqual, "foo "}, - dmp.Diff{dmp.DiffInsert, "bar"}, - dmp.Diff{dmp.DiffDelete, " baz"}, - dmp.Diff{dmp.DiffEqual, " biz"}, + {dmp.DiffEqual, "foo "}, + {dmp.DiffInsert, "bar"}, + {dmp.DiffDelete, " baz"}, + {dmp.DiffEqual, " biz"}, }, DiffLineAdd)) assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{ - dmp.Diff{dmp.DiffEqual, "foo "}, - dmp.Diff{dmp.DiffDelete, "bar"}, - dmp.Diff{dmp.DiffInsert, " baz"}, - dmp.Diff{dmp.DiffEqual, " biz"}, + {dmp.DiffEqual, "foo "}, + {dmp.DiffDelete, "bar"}, + {dmp.DiffInsert, " baz"}, + {dmp.DiffEqual, " biz"}, }, DiffLineDel)) } diff --git a/models/graph.go b/models/graph.go index 973476a746..7413f409de 100644 --- a/models/graph.go +++ b/models/graph.go @@ -31,7 +31,7 @@ type GraphItems []GraphItem // GetCommitGraph return a list of commit (GraphItems) from all branches func GetCommitGraph(r *git.Repository) (GraphItems, error) { - var Commitgraph []GraphItem + var CommitGraph []GraphItem format := "DATA:|%d|%H|%ad|%an|%ae|%h|%s" @@ -47,19 +47,19 @@ func GetCommitGraph(r *git.Repository) (GraphItems, error) { ) graph, err := graphCmd.RunInDir(r.Path) if err != nil { - return Commitgraph, err + return CommitGraph, err } - Commitgraph = make([]GraphItem, 0, 100) + CommitGraph = make([]GraphItem, 0, 100) for _, s := range strings.Split(graph, "\n") { GraphItem, err := graphItemFromString(s, r) if err != nil { - return Commitgraph, err + return CommitGraph, err } - Commitgraph = append(Commitgraph, GraphItem) + CommitGraph = append(CommitGraph, GraphItem) } - return Commitgraph, nil + return CommitGraph, nil } func graphItemFromString(s string, r *git.Repository) (GraphItem, error) { @@ -102,7 +102,7 @@ func graphItemFromString(s string, r *git.Repository) (GraphItem, error) { rows[5], rows[6], rows[7], - len(rows[2]) == 0, // no commits refered to, only relation in current line. + len(rows[2]) == 0, // no commits referred to, only relation in current line. } return gi, nil } diff --git a/models/graph_test.go b/models/graph_test.go index 23d8aa8492..47c9dbb084 100644 --- a/models/graph_test.go +++ b/models/graph_test.go @@ -17,25 +17,29 @@ func BenchmarkGetCommitGraph(b *testing.B) { b.Error("Could not open repository") } - graph, err := GetCommitGraph(currentRepo) - if err != nil { - b.Error("Could get commit graph") - } - - if len(graph) < 100 { - b.Error("Should get 100 log lines.") + for i := 0; i < b.N; i++ { + graph, err := GetCommitGraph(currentRepo) + if err != nil { + b.Error("Could get commit graph") + } + + if len(graph) < 100 { + b.Error("Should get 100 log lines.") + } } } func BenchmarkParseCommitString(b *testing.B) { testString := "* DATA:||4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|Kjell Kvinge|kjell@kvinge.biz|4e61bac|Add route for graph" - graphItem, err := graphItemFromString(testString, nil) - if err != nil { - b.Error("could not parse teststring") - } + for i := 0; i < b.N; i++ { + graphItem, err := graphItemFromString(testString, nil) + if err != nil { + b.Error("could not parse teststring") + } - if graphItem.Author != "Kjell Kvinge" { - b.Error("Did not get expected data") + if graphItem.Author != "Kjell Kvinge" { + b.Error("Did not get expected data") + } } } diff --git a/models/issue.go b/models/issue.go index 6303911808..315c91e159 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1202,8 +1202,8 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats { if opts.MentionedID > 0 { sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id"). - And("issue_user.uid = ?", opts.MentionedID). - And("issue_user.is_mentioned = ?", true) + And("issue_user.uid = ?", opts.MentionedID). + And("issue_user.is_mentioned = ?", true) } return sess diff --git a/models/issue_label.go b/models/issue_label.go index f06f0f97c3..0e1c6d6c4e 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -259,7 +259,7 @@ func DeleteLabel(repoID, labelID int64) error { // |___/____ >____ >____/ \___ >_______ (____ /___ /\___ >____/ // \/ \/ \/ \/ \/ \/ \/ -// IssueLabel represetns an issue-lable relation. +// IssueLabel represents an issue-label relation. type IssueLabel struct { ID int64 `xorm:"pk autoincr"` IssueID int64 `xorm:"UNIQUE(s)"` diff --git a/models/issue_mail.go b/models/issue_mail.go index 88adda5619..4b076606bf 100644 --- a/models/issue_mail.go +++ b/models/issue_mail.go @@ -24,7 +24,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) return nil } - // Mail wahtcers. + // Mail watchers. watchers, err := GetWatchers(issue.RepoID) if err != nil { return fmt.Errorf("GetWatchers [%d]: %v", issue.RepoID, err) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 66f80dac30..69408a071d 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -76,8 +76,10 @@ var migrations = []Migration{ // v13 -> v14:v0.9.87 NewMigration("set comment updated with created", setCommentUpdatedWithCreated), - + // v14 NewMigration("create user column diff view style", createUserColumnDiffViewStyle), + // v15 + NewMigration("create user column allow create organization", createAllowCreateOrganizationColumn), } // Migrate database to current version diff --git a/models/migrations/v15.go b/models/migrations/v15.go new file mode 100644 index 0000000000..90fc22c6d3 --- /dev/null +++ b/models/migrations/v15.go @@ -0,0 +1,30 @@ +// Copyright 2016 Gitea. 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 ( + "fmt" + + "github.com/go-xorm/xorm" +) + +// UserV15 describes the added field for User +type UserV15 struct { + AllowCreateOrganization bool +} + +// TableName will be invoked by XORM to customrize the table name +func (*UserV15) TableName() string { + return "user" +} + +func createAllowCreateOrganizationColumn(x *xorm.Engine) error { + if err := x.Sync2(new(UserV15)); err != nil { + return fmt.Errorf("Sync2: %v", err) + } else if _, err = x.Where("type=0").Cols("allow_create_organization").Update(&UserV15{AllowCreateOrganization: true}); err != nil { + return fmt.Errorf("set allow_create_organization: %v", err) + } + return nil +} diff --git a/models/models.go b/models/models.go index 1bebaa602c..131fd4fdb2 100644 --- a/models/models.go +++ b/models/models.go @@ -156,15 +156,15 @@ func parsePostgreSQLHostPort(info string) (string, string) { func parseMSSQLHostPort(info string) (string, string) { host, port := "127.0.0.1", "1433" - if strings.Contains(info, ":") { - host = strings.Split(info, ":")[0] - port = strings.Split(info, ":")[1] - } else if strings.Contains(info, ",") { - host = strings.Split(info, ",")[0] - port = strings.TrimSpace(strings.Split(info, ",")[1]) - } else if len(info) > 0 { - host = info - } + if strings.Contains(info, ":") { + host = strings.Split(info, ":")[0] + port = strings.Split(info, ":")[1] + } else if strings.Contains(info, ",") { + host = strings.Split(info, ",")[0] + port = strings.TrimSpace(strings.Split(info, ",")[1]) + } else if len(info) > 0 { + host = info + } return host, port } @@ -319,7 +319,14 @@ func Ping() error { return x.Ping() } -// DumpDatabase dumps all data from database to file system. -func DumpDatabase(filePath string) error { - return x.DumpAllToFile(filePath) +// DumpDatabase dumps all data from database according the special database SQL syntax to file system. +func DumpDatabase(filePath string, dbType string) error { + var tbs []*core.Table + for _, t := range tables { + tbs = append(tbs, x.TableInfo(t).Table) + } + if len(dbType) > 0 { + return x.DumpTablesToFile(tbs, filePath, core.DbType(dbType)) + } + return x.DumpTablesToFile(tbs, filePath) } diff --git a/models/notification.go b/models/notification.go index 188c987536..e2460e8369 100644 --- a/models/notification.go +++ b/models/notification.go @@ -66,7 +66,7 @@ func (n *Notification) BeforeInsert() { n.UpdatedUnix = nowUnix } -// BeforeUpdate runs while updateing a record +// BeforeUpdate runs while updating a record func (n *Notification) BeforeUpdate() { var ( now = time.Now() diff --git a/models/org.go b/models/org.go index 172295e7ae..7fbd790f5b 100644 --- a/models/org.go +++ b/models/org.go @@ -97,6 +97,10 @@ func (org *User) RemoveOrgRepo(repoID int64) error { // CreateOrganization creates record of a new organization. func CreateOrganization(org, owner *User) (err error) { + if !owner.CanCreateOrganization() { + return ErrUserNotAllowedCreateOrg{} + } + if err = IsUsableUsername(org.Name); err != nil { return err } @@ -555,7 +559,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos } repos := make([]*Repository, 0, pageSize) - if err := x. + if err := x.Select("`repository`.*"). Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). Where(cond). GroupBy("`repository`.id"). diff --git a/models/pull.go b/models/pull.go index 36194d8660..6231b71cb7 100644 --- a/models/pull.go +++ b/models/pull.go @@ -372,7 +372,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error // TODO: when squash commits, no need to append merge commit. // It is possible that head branch is not fully sync with base branch for merge commits, - // so we need to get latest head commit and append merge commit manully + // so we need to get latest head commit and append merge commit manually // to avoid strange diff commits produced. mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch) if err != nil { @@ -419,9 +419,9 @@ func (pr *PullRequest) testPatch() (err error) { return fmt.Errorf("BaseRepo.PatchPath: %v", err) } - // Fast fail if patch does not exist, this assumes data is cruppted. + // Fast fail if patch does not exist, this assumes data is corrupted. if !com.IsFile(patchPath) { - log.Trace("PullRequest[%d].testPatch: ignored cruppted data", pr.ID) + log.Trace("PullRequest[%d].testPatch: ignored corrupted data", pr.ID) return nil } @@ -573,7 +573,7 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest, return prs, maxResults, findSession.Find(&prs) } -// GetUnmergedPullRequest returnss a pull request that is open and has not been merged +// GetUnmergedPullRequest returns a pull request that is open and has not been merged // by given head/base and repo/branch. func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) { pr := new(PullRequest) @@ -591,7 +591,7 @@ func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch return pr, nil } -// GetUnmergedPullRequestsByHeadInfo returnss all pull requests that are open and has not been merged +// GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged // by given head information (repo and branch). func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) { prs := make([]*PullRequest, 0, 2) @@ -602,7 +602,7 @@ func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequ Find(&prs) } -// GetUnmergedPullRequestsByBaseInfo returnss all pull requests that are open and has not been merged +// GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged // by given base information (repo and branch). func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) { prs := make([]*PullRequest, 0, 2) @@ -885,7 +885,7 @@ func ChangeUsernameInPullRequests(oldUserName, newUserName string) error { return err } -// checkAndUpdateStatus checks if pull request is possible to levaing checking status, +// checkAndUpdateStatus checks if pull request is possible to leaving checking status, // and set to be either conflict or mergeable. func (pr *PullRequest) checkAndUpdateStatus() { // Status is not changed to conflict means mergeable. @@ -893,7 +893,7 @@ func (pr *PullRequest) checkAndUpdateStatus() { pr.Status = PullRequestStatusMergeable } - // Make sure there is no waiting test to process before levaing the checking status. + // Make sure there is no waiting test to process before leaving the checking status. if !pullRequestQueue.Exist(pr.ID) { if err := pr.UpdateCols("status"); err != nil { log.Error(4, "Update[%d]: %v", pr.ID, err) diff --git a/models/release.go b/models/release.go index 41fd145bea..428b79e45f 100644 --- a/models/release.go +++ b/models/release.go @@ -15,12 +15,16 @@ import ( "code.gitea.io/git" "code.gitea.io/gitea/modules/process" + "code.gitea.io/gitea/modules/setting" + + api "code.gitea.io/sdk/gitea" ) // Release represents a release of repository. type Release struct { ID int64 `xorm:"pk autoincr"` RepoID int64 + Repo *Repository `xorm:"-"` PublisherID int64 Publisher *User `xorm:"-"` TagName string @@ -53,6 +57,62 @@ func (r *Release) AfterSet(colName string, _ xorm.Cell) { } } +func (r *Release) loadAttributes(e Engine) error { + var err error + if r.Repo == nil { + r.Repo, err = GetRepositoryByID(r.RepoID) + if err != nil { + return err + } + } + if r.Publisher == nil { + r.Publisher, err = GetUserByID(r.PublisherID) + if err != nil { + return err + } + } + return nil +} + +// LoadAttributes load repo and publisher attributes for a realease +func (r *Release) LoadAttributes() error { + return r.loadAttributes(x) +} + +// APIURL the api url for a release. release must have attributes loaded +func (r *Release) APIURL() string { + return fmt.Sprintf("%sapi/v1/%s/releases/%d", + setting.AppURL, r.Repo.FullName(), r.ID) +} + +// ZipURL the zip url for a release. release must have attributes loaded +func (r *Release) ZipURL() string { + return fmt.Sprintf("%s/archive/%s.zip", r.Repo.HTMLURL(), r.TagName) +} + +// TarURL the tar.gz url for a release. release must have attributes loaded +func (r *Release) TarURL() string { + return fmt.Sprintf("%s/archive/%s.tar.gz", r.Repo.HTMLURL(), r.TagName) +} + +// APIFormat convert a Release to api.Release +func (r *Release) APIFormat() *api.Release { + return &api.Release{ + ID: r.ID, + TagName: r.TagName, + Target: r.Target, + Note: r.Note, + URL: r.APIURL(), + TarURL: r.TarURL(), + ZipURL: r.ZipURL(), + IsDraft: r.IsDraft, + IsPrerelease: r.IsPrerelease, + CreatedAt: r.Created, + PublishedAt: r.Created, + Publisher: r.Publisher.APIFormat(), + } +} + // IsReleaseExist returns true if release with given tag name already exists. func IsReleaseExist(repoID int64, tagName string) (bool, error) { if len(tagName) == 0 { @@ -189,7 +249,7 @@ func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) { } // DeleteReleaseByID deletes a release and corresponding Git tag by given ID. -func DeleteReleaseByID(id int64, u *User) error { +func DeleteReleaseByID(id int64, u *User, delTag bool) error { rel, err := GetReleaseByID(id) if err != nil { return fmt.Errorf("GetReleaseByID: %v", err) @@ -207,11 +267,13 @@ func DeleteReleaseByID(id int64, u *User) error { return fmt.Errorf("DeleteReleaseByID: permission denied") } - _, stderr, err := process.ExecDir(-1, repo.RepoPath(), - fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID), - "git", "tag", "-d", rel.TagName) - if err != nil && !strings.Contains(stderr, "not found") { - return fmt.Errorf("git tag -d: %v - %s", err, stderr) + if delTag { + _, stderr, err := process.ExecDir(-1, repo.RepoPath(), + fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID), + "git", "tag", "-d", rel.TagName) + if err != nil && !strings.Contains(stderr, "not found") { + return fmt.Errorf("git tag -d: %v - %s", err, stderr) + } } if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil { diff --git a/models/repo.go b/models/repo.go index ce9740f8c4..85807e3a4b 100644 --- a/models/repo.go +++ b/models/repo.go @@ -259,7 +259,7 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { // MustOwner always returns a valid *User object to avoid // conceptually impossible error handling. -// It creates a fake object that contains error deftail +// It creates a fake object that contains error details // when error occurs. func (repo *Repository) MustOwner() *User { return repo.mustOwner(x) @@ -691,7 +691,6 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { wikiRemotePath := wikiRemoteURL(opts.RemoteAddr) if len(wikiRemotePath) > 0 { - if err := os.RemoveAll(wikiPath); err != nil { return repo, fmt.Errorf("Fail to remove %s: %v", wikiPath, err) } @@ -700,8 +699,12 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { Mirror: true, Quiet: true, Timeout: migrateTimeout, + Branch: "master", }); err != nil { - log.Info("Clone wiki: %v", err) + log.Warn("Clone wiki: %v", err) + if err := os.RemoveAll(wikiPath); err != nil { + return repo, fmt.Errorf("Fail to remove %s: %v", wikiPath, err) + } } } @@ -851,7 +854,7 @@ func getRepoInitFile(tp, name string) ([]byte, error) { } func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { - // Clone to temprory path and do the init commit. + // Clone to temporary path and do the init commit. _, stderr, err := process.Exec( fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { @@ -1324,7 +1327,7 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e return fmt.Errorf("getOwner: %v", err) } if repo.Owner.IsOrganization() { - // Organization repository need to recalculate access table when visivility is changed. + // Organization repository need to recalculate access table when visibility is changed. if err = repo.recalculateTeamAccesses(e, 0); err != nil { return fmt.Errorf("recalculateTeamAccesses: %v", err) } diff --git a/models/repo_mirror.go b/models/repo_mirror.go index b834a6e5cd..2cedf69d70 100644 --- a/models/repo_mirror.go +++ b/models/repo_mirror.go @@ -247,7 +247,7 @@ func SyncMirrors() { } } -// InitSyncMirrors initializes a go routine to sync the mirros +// InitSyncMirrors initializes a go routine to sync the mirrors func InitSyncMirrors() { go SyncMirrors() } diff --git a/models/setup_for_test.go b/models/setup_for_test.go new file mode 100644 index 0000000000..b585a7490e --- /dev/null +++ b/models/setup_for_test.go @@ -0,0 +1,47 @@ +// Copyright 2016 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 models + +import ( + "fmt" + "os" + "testing" + + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" + _ "github.com/mattn/go-sqlite3" // for the test engine + "gopkg.in/testfixtures.v2" +) + +func TestMain(m *testing.M) { + if err := CreateTestEngine(); err != nil { + fmt.Printf("Error creating test engine: %v\n", err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +var fixtures *testfixtures.Context + +// CreateTestEngine create an xorm engine for testing +func CreateTestEngine() error { + testfixtures.SkipDatabaseNameCheck(true) + var err error + x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared") + if err != nil { + return err + } + x.SetMapper(core.GonicMapper{}) + if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil { + return err + } + fixtures, err = testfixtures.NewFolder(x.DB().DB, &testfixtures.SQLite{}, "fixtures/") + return err +} + +// PrepareTestDatabase load test fixtures into test database +func PrepareTestDatabase() error { + return fixtures.Load() +} diff --git a/models/ssh_key.go b/models/ssh_key.go index d7d71f68cb..cdb83b44e0 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -354,7 +354,7 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error { return nil } -// checkKeyContent onlys checks if key content has been used as public key, +// checkKeyContent only checks if key content has been used as public key, // it is OK to use same key as deploy key for multiple repositories/users. func checkKeyContent(content string) error { has, err := x.Get(&PublicKey{ @@ -526,7 +526,7 @@ func DeletePublicKey(doer *User, id int64) (err error) { // RewriteAllPublicKeys removes any authorized key and rewrite all keys from database again. // Note: x.Iterate does not get latest data after insert/delete, so we have to call this function -// outsite any session scope independently. +// outside any session scope independently. func RewriteAllPublicKeys() error { sshOpLocker.Lock() defer sshOpLocker.Unlock() diff --git a/models/token_test.go b/models/token_test.go new file mode 100644 index 0000000000..15207ceb65 --- /dev/null +++ b/models/token_test.go @@ -0,0 +1,107 @@ +// Copyright 2016 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 models + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewAccessToken(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + token := &AccessToken{ + UID: 3, + Name: "Token C", + } + assert.NoError(t, NewAccessToken(token)) + sess := x.NewSession() + defer sess.Close() + has, err := sess.Get(*token) + assert.NoError(t, err) + assert.True(t, has) + assert.Equal(t, int64(3), token.UID) + assert.Equal(t, "Token C", token.Name) + + invalidToken := &AccessToken{ + ID: token.ID, // duplicate + UID: 2, + Name: "Token F", + } + assert.Error(t, NewAccessToken(invalidToken)) +} + +func TestGetAccessTokenBySHA(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + token, err := GetAccessTokenBySHA("hash1") + assert.NoError(t, err) + assert.Equal(t, int64(1), token.UID) + assert.Equal(t, "Token A", token.Name) + assert.Equal(t, "hash1", token.Sha1) + + token, err = GetAccessTokenBySHA("notahash") + assert.Error(t, err) + assert.True(t, IsErrAccessTokenNotExist(err)) + + token, err = GetAccessTokenBySHA("") + assert.Error(t, err) + assert.True(t, IsErrAccessTokenEmpty(err)) +} + +func TestListAccessTokens(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + tokens, err := ListAccessTokens(1) + assert.NoError(t, err) + assert.Len(t, tokens, 2) + assert.Equal(t, int64(1), tokens[0].UID) + assert.Equal(t, int64(1), tokens[1].UID) + assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token A") + assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B") + + tokens, err = ListAccessTokens(2) + assert.NoError(t, err) + assert.Len(t, tokens, 1) + assert.Equal(t, int64(2), tokens[0].UID) + assert.Equal(t, "Token A", tokens[0].Name) + + tokens, err = ListAccessTokens(100) + assert.NoError(t, err) + assert.Empty(t, tokens) +} + +func TestUpdateAccessToken(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + token, err := GetAccessTokenBySHA("hash2") + assert.NoError(t, err) + token.Name = "Token Z" + + assert.NoError(t, UpdateAccessToken(token)) + + sess := x.NewSession() + defer sess.Close() + has, err := sess.Get(token) + assert.NoError(t, err) + assert.True(t, has) + assert.Equal(t, token.Name, "Token Z") +} + +func TestDeleteAccessTokenByID(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + token, err := GetAccessTokenBySHA("hash2") + assert.NoError(t, err) + assert.Equal(t, int64(1), token.UID) + + assert.NoError(t, DeleteAccessTokenByID(token.ID, 1)) + sess := x.NewSession() + defer sess.Close() + has, err := sess.Get(token) + assert.NoError(t, err) + assert.False(t, has) + + err = DeleteAccessTokenByID(100, 100) + assert.Error(t, err) + assert.True(t, IsErrAccessTokenNotExist(err)) +} diff --git a/models/update_test.go b/models/update_test.go new file mode 100644 index 0000000000..141eb2fd51 --- /dev/null +++ b/models/update_test.go @@ -0,0 +1,124 @@ +// Copyright 2016 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 models + +import ( + "container/list" + "testing" + "time" + + "code.gitea.io/git" + + "github.com/stretchr/testify/assert" +) + +func TestAddUpdateTask(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + task := &UpdateTask{ + UUID: "uuid4", + RefName: "refName4", + OldCommitID: "oldCommitId4", + NewCommitID: "newCommitId4", + } + assert.NoError(t, AddUpdateTask(task)) + + sess := x.NewSession() + defer sess.Close() + has, err := sess.Get(task) + assert.NoError(t, err) + assert.True(t, has) + assert.Equal(t, "uuid4", task.UUID) +} + +func TestGetUpdateTaskByUUID(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + task, err := GetUpdateTaskByUUID("uuid1") + assert.NoError(t, err) + assert.Equal(t, "uuid1", task.UUID) + assert.Equal(t, "refName1", task.RefName) + assert.Equal(t, "oldCommitId1", task.OldCommitID) + assert.Equal(t, "newCommitId1", task.NewCommitID) +} + +func TestDeleteUpdateTaskByUUID(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + assert.NoError(t, DeleteUpdateTaskByUUID("uuid1")) + sess := x.NewSession() + defer sess.Close() + has, err := sess.Get(&UpdateTask{UUID: "uuid1"}) + assert.NoError(t, err) + assert.False(t, has) + + assert.NoError(t, DeleteUpdateTaskByUUID("invalid")) +} + +func TestCommitToPushCommit(t *testing.T) { + now := time.Now() + sig := &git.Signature{ + Email: "example@example.com", + Name: "John Doe", + When: now, + } + const hexString = "0123456789abcdef0123456789abcdef01234567" + sha1, err := git.NewIDFromString(hexString) + assert.NoError(t, err) + pushCommit := CommitToPushCommit(&git.Commit{ + ID: sha1, + Author: sig, + Committer: sig, + CommitMessage: "Commit Message", + }) + assert.Equal(t, hexString, pushCommit.Sha1) + assert.Equal(t, "Commit Message", pushCommit.Message) + assert.Equal(t, "example@example.com", pushCommit.AuthorEmail) + assert.Equal(t, "John Doe", pushCommit.AuthorName) + assert.Equal(t, "example@example.com", pushCommit.CommitterEmail) + assert.Equal(t, "John Doe", pushCommit.CommitterName) + assert.Equal(t, now, pushCommit.Timestamp) +} + +func TestListToPushCommits(t *testing.T) { + now := time.Now() + sig := &git.Signature{ + Email: "example@example.com", + Name: "John Doe", + When: now, + } + + const hexString1 = "0123456789abcdef0123456789abcdef01234567" + hash1, err := git.NewIDFromString(hexString1) + assert.NoError(t, err) + const hexString2 = "fedcba9876543210fedcba9876543210fedcba98" + hash2, err := git.NewIDFromString(hexString2) + assert.NoError(t, err) + + l := list.New() + l.PushBack(&git.Commit{ + ID: hash1, + Author: sig, + Committer: sig, + CommitMessage: "Message1", + }) + l.PushBack(&git.Commit{ + ID: hash2, + Author: sig, + Committer: sig, + CommitMessage: "Message2", + }) + + pushCommits := ListToPushCommits(l) + assert.Equal(t, 2, pushCommits.Len) + assert.Equal(t, 2, len(pushCommits.Commits)) + + assert.Equal(t, "Message1", pushCommits.Commits[0].Message) + assert.Equal(t, hexString1, pushCommits.Commits[0].Sha1) + assert.Equal(t, "example@example.com", pushCommits.Commits[0].AuthorEmail) + assert.Equal(t, now, pushCommits.Commits[0].Timestamp) + + assert.Equal(t, "Message2", pushCommits.Commits[1].Message) + assert.Equal(t, hexString2, pushCommits.Commits[1].Sha1) + assert.Equal(t, "example@example.com", pushCommits.Commits[1].AuthorEmail) + assert.Equal(t, now, pushCommits.Commits[1].Timestamp) +} diff --git a/models/user.go b/models/user.go index d48397ef7e..1dbd63dce0 100644 --- a/models/user.go +++ b/models/user.go @@ -102,11 +102,12 @@ type User struct { MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"` // Permissions - IsActive bool // Activate primary email - IsAdmin bool - AllowGitHook bool - AllowImportLocal bool // Allow migrate repository by local path - ProhibitLogin bool + IsActive bool // Activate primary email + IsAdmin bool + AllowGitHook bool + AllowImportLocal bool // Allow migrate repository by local path + AllowCreateOrganization bool `xorm:"DEFAULT true"` + ProhibitLogin bool // Avatar Avatar string `xorm:"VARCHAR(2048) NOT NULL"` @@ -210,6 +211,11 @@ func (u *User) CanCreateRepo() bool { return u.NumRepos < u.MaxRepoCreation } +// CanCreateOrganization returns true if user can create organisation. +func (u *User) CanCreateOrganization() bool { + return u.IsAdmin || u.AllowCreateOrganization +} + // CanEditGitHook returns true if user can edit Git hooks. func (u *User) CanEditGitHook() bool { return u.IsAdmin || u.AllowGitHook @@ -594,6 +600,15 @@ func CreateUser(u *User) (err error) { } u.Email = strings.ToLower(u.Email) + has, err := x. + Where("email=?", u.Email). + Get(new(User)) + if err != nil { + return err + } else if has { + return ErrEmailAlreadyUsed{u.Email} + } + isExist, err = IsEmailUsed(u.Email) if err != nil { return err @@ -611,6 +626,7 @@ func CreateUser(u *User) (err error) { return err } u.EncodePasswd() + u.AllowCreateOrganization = true u.MaxRepoCreation = -1 sess := x.NewSession() @@ -656,7 +672,7 @@ func Users(opts *SearchUserOptions) ([]*User, error) { Find(&users) } -// get user by erify code +// get user by verify code func getVerifyUser(code string) (user *User) { if len(code) <= base.TimeLimitCodeLength { return nil @@ -1050,7 +1066,7 @@ type UserCommit struct { *git.Commit } -// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user. +// ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user. func ValidateCommitWithEmail(c *git.Commit) *User { u, err := GetUserByEmail(c.Author.Email) if err != nil { @@ -1209,7 +1225,7 @@ func FollowUser(userID, followID int64) (err error) { return sess.Commit() } -// UnfollowUser unmarks someone be another's follower. +// UnfollowUser unmarks someone as another's follower. func UnfollowUser(userID, followID int64) (err error) { if userID == followID || !IsFollowing(userID, followID) { return nil diff --git a/models/user_mail.go b/models/user_mail.go index ce29354771..4ccf08e9d9 100644 --- a/models/user_mail.go +++ b/models/user_mail.go @@ -49,8 +49,8 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) { } } - // We alway want the primary email address displayed, even if it's not in - // the emailaddress table (yet). + // We always want the primary email address displayed, even if it's not in + // the email address table (yet). if !isPrimaryFound { emails = append(emails, &EmailAddress{ Email: u.Email, diff --git a/models/webhook_slack.go b/models/webhook_slack.go index aaeef6a001..b297fefd66 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -16,7 +16,7 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// SlackMeta contains the slack metdata +// SlackMeta contains the slack metadata type SlackMeta struct { Channel string `json:"channel"` Username string `json:"username"` @@ -75,7 +75,7 @@ func SlackShortTextFormatter(s string) string { return s } -// SlackLinkFormatter creates a link compatablie with slack +// SlackLinkFormatter creates a link compatible with slack func SlackLinkFormatter(url string, text string) string { return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text)) } |