## License | ## License | ||||
Gogs is under the MIT License. | |||||
See the [LICENSE file](https://github.com/gogits/gogs/blob/master/LICENSE) for the full license text. | |||||
Gogs is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text. |
## 贡献成员 | ## 贡献成员 | ||||
本项目最初由 [Unknown](https://github.com/Unknwon) 和 [lunny](https://github.com/lunny) 发起,随后 [fuxiaohei](https://github.com/fuxiaohei) 与 [slene](https://github.com/slene) 加入到开发团队。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。 | |||||
本项目最初由 [Unknown](https://github.com/Unknwon) 和 [lunny](https://github.com/lunny) 发起,随后 [fuxiaohei](https://github.com/fuxiaohei) 与 [slene](https://github.com/slene) 加入到开发团队。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。 | |||||
## 授权许可 | |||||
Gogs 采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 |
// CommitRepoAction adds new action for committing repository. | // CommitRepoAction adds new action for committing repository. | ||||
func CommitRepoAction(userId int64, userName string, | func CommitRepoAction(userId int64, userName string, | ||||
repoId int64, repoName string, refName string, commits *base.PushCommits) error { | |||||
repoId int64, repoName string, refName string, commit *base.PushCommits) error { | |||||
log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) | log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) | ||||
bs, err := json.Marshal(commits) | |||||
bs, err := json.Marshal(commit) | |||||
if err != nil { | if err != nil { | ||||
log.Error("action.CommitRepoAction(json): %d/%s", userId, repoName) | log.Error("action.CommitRepoAction(json): %d/%s", userId, repoName) | ||||
return err | return err | ||||
return nil | return nil | ||||
} | } | ||||
// NewRepoAction records action for create repository. | |||||
func NewRepoAction(user *User, repo *Repository) error { | |||||
_, err := orm.InsertOne(&Action{ | |||||
UserId: user.Id, | |||||
ActUserId: user.Id, | |||||
ActUserName: user.Name, | |||||
OpType: OP_CREATE_REPO, | |||||
RepoId: repo.Id, | |||||
RepoName: repo.Name, | |||||
}) | |||||
// NewRepoAction adds new action for creating repository. | |||||
func NewRepoAction(user *User, repo *Repository) (err error) { | |||||
if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, OpType: OP_CREATE_REPO, | |||||
RepoId: repo.Id, RepoName: repo.Name}); err != nil { | |||||
log.Error("action.NewRepoAction(notify watchers): %d/%s", user.Id, repo.Name) | |||||
return err | |||||
} | |||||
log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) | log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) | ||||
return err | return err |
} | } | ||||
// CreateIssue creates new issue for repository. | // CreateIssue creates new issue for repository. | ||||
func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, content string, isPull bool) (*Issue, error) { | |||||
func CreateIssue(userId, repoId, milestoneId, assigneeId int64, issueCount int, name, labels, content string, isPull bool) (*Issue, error) { | |||||
count, err := GetIssueCount(repoId) | count, err := GetIssueCount(repoId) | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
// TODO: find out mentions | // TODO: find out mentions | ||||
mentions := "" | mentions := "" | ||||
sess := orm.NewSession() | |||||
defer sess.Close() | |||||
sess.Begin() | |||||
issue := &Issue{ | issue := &Issue{ | ||||
Index: count + 1, | Index: count + 1, | ||||
Name: name, | Name: name, | ||||
Mentions: mentions, | Mentions: mentions, | ||||
Content: content, | Content: content, | ||||
} | } | ||||
_, err = orm.Insert(issue) | |||||
return issue, err | |||||
if _, err = sess.Insert(issue); err != nil { | |||||
sess.Rollback() | |||||
return nil, err | |||||
} | |||||
rawSql := "UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?" | |||||
if _, err = sess.Exec(rawSql, repoId); err != nil { | |||||
sess.Rollback() | |||||
return nil, err | |||||
} | |||||
if err = sess.Commit(); err != nil { | |||||
sess.Rollback() | |||||
return nil, err | |||||
} | |||||
return issue, nil | |||||
} | } | ||||
// GetIssueCount returns count of issues in the repository. | // GetIssueCount returns count of issues in the repository. |
"github.com/lunny/xorm" | "github.com/lunny/xorm" | ||||
_ "github.com/mattn/go-sqlite3" | _ "github.com/mattn/go-sqlite3" | ||||
. "github.com/smartystreets/goconvey/convey" | |||||
"github.com/gogits/gogs/modules/base" | |||||
) | ) | ||||
func init() { | func init() { | ||||
LoadModelsConfig() | |||||
NewEngine() | |||||
var err error | var err error | ||||
orm, err = xorm.NewEngine("sqlite3", "./test.db") | orm, err = xorm.NewEngine("sqlite3", "./test.db") | ||||
if err != nil { | if err != nil { | ||||
orm.ShowSQL = true | orm.ShowSQL = true | ||||
orm.ShowDebug = true | orm.ShowDebug = true | ||||
err = orm.Sync(&User{}, &Repo{}) | |||||
err = orm.Sync(&User{}, &Repository{}) | |||||
if err != nil { | if err != nil { | ||||
fmt.Println(err) | fmt.Println(err) | ||||
} | } | ||||
root = "test" | |||||
base.RepoRootPath = "test" | |||||
} | } | ||||
func TestCreateRepository(t *testing.T) { | func TestCreateRepository(t *testing.T) { | ||||
user := User{Id: 1, Type: Individual} | |||||
_, err := CreateRepository(&user, "test") | |||||
user := User{Id: 1, Name: "foobar", Type: UT_INDIVIDUAL} | |||||
_, err := CreateRepository(&user, "test", "", "", "test repo desc", false, false) | |||||
if err != nil { | if err != nil { | ||||
t.Error(err) | t.Error(err) | ||||
} | } | ||||
} | } | ||||
func TestDeleteRepository(t *testing.T) { | func TestDeleteRepository(t *testing.T) { | ||||
user := User{Id: 1, Type: Individual} | |||||
err := DeleteRepository(&user, "test") | |||||
err := DeleteRepository(1, 1, "foobar") | |||||
if err != nil { | if err != nil { | ||||
t.Error(err) | t.Error(err) | ||||
} | } | ||||
} | } | ||||
func TestCommitRepoAction(t *testing.T) { | |||||
Convey("Create a commit repository action", t, func() { | |||||
}) | |||||
} |
// Repository represents a git repository. | // Repository represents a git repository. | ||||
type Repository struct { | type Repository struct { | ||||
Id int64 | |||||
OwnerId int64 `xorm:"unique(s)"` | |||||
ForkId int64 | |||||
LowerName string `xorm:"unique(s) index not null"` | |||||
Name string `xorm:"index not null"` | |||||
Description string | |||||
Website string | |||||
NumWatches int | |||||
NumStars int | |||||
NumForks int | |||||
IsPrivate bool | |||||
IsBare bool | |||||
Created time.Time `xorm:"created"` | |||||
Updated time.Time `xorm:"updated"` | |||||
Id int64 | |||||
OwnerId int64 `xorm:"unique(s)"` | |||||
ForkId int64 | |||||
LowerName string `xorm:"unique(s) index not null"` | |||||
Name string `xorm:"index not null"` | |||||
Description string | |||||
Website string | |||||
NumWatches int | |||||
NumStars int | |||||
NumForks int | |||||
NumIssues int | |||||
NumClosedIssues int | |||||
IsPrivate bool | |||||
IsBare bool | |||||
Created time.Time `xorm:"created"` | |||||
Updated time.Time `xorm:"updated"` | |||||
} | } | ||||
// IsRepositoryExist returns true if the repository with given name under user has already existed. | // IsRepositoryExist returns true if the repository with given name under user has already existed. | ||||
if err != nil { | if err != nil { | ||||
return errors.New("repo.NotifyWatchers(get watches): " + err.Error()) | return errors.New("repo.NotifyWatchers(get watches): " + err.Error()) | ||||
} | } | ||||
watches = append(watches, Watch{UserId: act.ActUserId}) | |||||
// Add feed for actioner. | |||||
act.UserId = act.ActUserId | |||||
if _, err = orm.InsertOne(act); err != nil { | |||||
return errors.New("repo.NotifyWatchers(create action): " + err.Error()) | |||||
} | |||||
for i := range watches { | for i := range watches { | ||||
if act.ActUserId == watches[i].UserId && i > 0 { | |||||
continue // Do not add twice in case author watches his/her repository. | |||||
if act.ActUserId == watches[i].UserId { | |||||
continue | |||||
} | } | ||||
act.UserId = watches[i].UserId | act.UserId = watches[i].UserId |
"github.com/gogits/gogs/modules/middleware" | "github.com/gogits/gogs/modules/middleware" | ||||
) | ) | ||||
func Issues(ctx *middleware.Context, params martini.Params) { | |||||
func Issues(ctx *middleware.Context) { | |||||
ctx.Data["Title"] = "Issues" | ctx.Data["Title"] = "Issues" | ||||
ctx.Data["IsRepoToolbarIssues"] = true | ctx.Data["IsRepoToolbarIssues"] = true | ||||
ctx.Data["IsRepoToolbarIssuesList"] = true | ctx.Data["IsRepoToolbarIssuesList"] = true | ||||
milestoneId, _ := base.StrTo(params["milestone"]).Int() | |||||
page, _ := base.StrTo(params["page"]).Int() | |||||
milestoneId, _ := base.StrTo(ctx.Query("milestone")).Int() | |||||
page, _ := base.StrTo(ctx.Query("page")).Int() | |||||
// Get issues. | // Get issues. | ||||
issues, err := models.GetIssues(0, ctx.Repo.Repository.Id, 0, | issues, err := models.GetIssues(0, ctx.Repo.Repository.Id, 0, | ||||
int64(milestoneId), page, params["state"] == "closed", false, params["labels"], params["sortType"]) | |||||
int64(milestoneId), page, ctx.Query("state") == "closed", false, ctx.Query("labels"), ctx.Query("sortType")) | |||||
if err != nil { | if err != nil { | ||||
ctx.Handle(200, "issue.Issues: %v", err) | ctx.Handle(200, "issue.Issues: %v", err) | ||||
return | return | ||||
} | } | ||||
var closedCount int | |||||
// Get posters. | // Get posters. | ||||
for i := range issues { | for i := range issues { | ||||
u, err := models.GetUserById(issues[i].PosterId) | u, err := models.GetUserById(issues[i].PosterId) | ||||
ctx.Handle(200, "issue.Issues(get poster): %v", err) | ctx.Handle(200, "issue.Issues(get poster): %v", err) | ||||
return | return | ||||
} | } | ||||
if issues[i].IsClosed { | |||||
closedCount++ | |||||
} | |||||
issues[i].Poster = u | issues[i].Poster = u | ||||
} | } | ||||
ctx.Data["Issues"] = issues | ctx.Data["Issues"] = issues | ||||
ctx.Data["IssueCount"] = len(issues) | |||||
ctx.Data["OpenCount"] = len(issues) - closedCount | |||||
ctx.Data["ClosedCount"] = closedCount | |||||
ctx.Data["IssueCount"] = ctx.Repo.Repository.NumIssues | |||||
ctx.Data["OpenCount"] = ctx.Repo.Repository.NumIssues - ctx.Repo.Repository.NumClosedIssues | |||||
ctx.Data["ClosedCount"] = ctx.Repo.Repository.NumClosedIssues | |||||
ctx.Data["IsShowClosed"] = ctx.Query("state") == "closed" | |||||
ctx.HTML(200, "issue/list") | ctx.HTML(200, "issue/list") | ||||
} | } | ||||
} | } | ||||
issue, err := models.CreateIssue(ctx.User.Id, ctx.Repo.Repository.Id, form.MilestoneId, form.AssigneeId, | issue, err := models.CreateIssue(ctx.User.Id, ctx.Repo.Repository.Id, form.MilestoneId, form.AssigneeId, | ||||
form.IssueName, form.Labels, form.Content, false) | |||||
ctx.Repo.Repository.NumIssues, form.IssueName, form.Labels, form.Content, false) | |||||
if err != nil { | if err != nil { | ||||
ctx.Handle(200, "issue.CreateIssue", err) | ctx.Handle(200, "issue.CreateIssue", err) | ||||
return | return |
<div class="col-md-3 filter-list"> | <div class="col-md-3 filter-list"> | ||||
<ul class="list-unstyled"> | <ul class="list-unstyled"> | ||||
<li><a href="#" class="active">All Issues <strong class="pull-right">{{.IssueCount}}</strong></a></li> | <li><a href="#" class="active">All Issues <strong class="pull-right">{{.IssueCount}}</strong></a></li> | ||||
<li><a href="#">My Issues</a></li> | |||||
<li><a href="#">Mentioned</a></li> | |||||
<!-- <li><a href="#">My Issues</a></li> | |||||
<li><a href="#">Mentioned</a></li> --> | |||||
</ul> | </ul> | ||||
</div> | </div> | ||||
<div class="col-md-9"> | <div class="col-md-9"> | ||||
<div class="filter-option"> | <div class="filter-option"> | ||||
<div class="btn-group"> | <div class="btn-group"> | ||||
<a class="btn btn-default active issue-open" href="#">{{.OpenCount}} Open</a> | |||||
<a class="btn btn-default issue-close" href="#">{{.ClosedCount}} Closed</a> | |||||
<a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues">{{.OpenCount}} Open</a> | |||||
<a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues?state=closed">{{.ClosedCount}} Closed</a> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="issues list-group"> | <div class="issues list-group"> |