Merge branch 'master' of github.com:gogits/gogs
This commit is contained in:
commit
bce8586bc6
|
@ -27,7 +27,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Activity timeline
|
- Activity timeline
|
||||||
- SSH/HTTPS protocol support.
|
- SSH/HTTPS(Clone only) protocol support.
|
||||||
- Register/delete account.
|
- Register/delete account.
|
||||||
- Create/delete/watch public repository.
|
- Create/delete/watch public repository.
|
||||||
- User profile page.
|
- User profile page.
|
||||||
|
|
|
@ -23,7 +23,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
- 活动时间线
|
- 活动时间线
|
||||||
- SSH/HTTPS 协议支持
|
- SSH/HTTPS(仅限 Clone) 协议支持
|
||||||
- 注册/删除用户
|
- 注册/删除用户
|
||||||
- 创建/删除/关注公开仓库
|
- 创建/删除/关注公开仓库
|
||||||
- 用户个人信息页面
|
- 用户个人信息页面
|
||||||
|
|
2
gogs.go
2
gogs.go
|
@ -19,7 +19,7 @@ import (
|
||||||
// Test that go1.2 tag above is included in builds. main.go refers to this definition.
|
// Test that go1.2 tag above is included in builds. main.go refers to this definition.
|
||||||
const go12tag = true
|
const go12tag = true
|
||||||
|
|
||||||
const APP_VER = "0.1.8.0326 Alpha"
|
const APP_VER = "0.1.8.0327 Alpha"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
base.AppVer = APP_VER
|
base.AppVer = APP_VER
|
||||||
|
|
|
@ -15,7 +15,7 @@ const (
|
||||||
AU_WRITABLE
|
AU_WRITABLE
|
||||||
)
|
)
|
||||||
|
|
||||||
// Access represents the accessibility of user and repository.
|
// Access represents the accessibility of user to repository.
|
||||||
type Access struct {
|
type Access struct {
|
||||||
Id int64
|
Id int64
|
||||||
UserName string `xorm:"unique(s)"`
|
UserName string `xorm:"unique(s)"`
|
||||||
|
@ -30,7 +30,7 @@ func AddAccess(access *Access) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAccess returns true if someone can read or write given repository.
|
// HasAccess returns true if someone can read or write to given repository.
|
||||||
func HasAccess(userName, repoName string, mode int) (bool, error) {
|
func HasAccess(userName, repoName string, mode int) (bool, error) {
|
||||||
return orm.Get(&Access{
|
return orm.Get(&Access{
|
||||||
Id: 0,
|
Id: 0,
|
||||||
|
|
|
@ -23,7 +23,8 @@ const (
|
||||||
OP_PULL_REQUEST
|
OP_PULL_REQUEST
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action represents user operation type and information to the repository.
|
// Action represents user operation type and other information to repository.,
|
||||||
|
// it implemented interface base.Actioner so that can be used in template render.
|
||||||
type Action struct {
|
type Action struct {
|
||||||
Id int64
|
Id int64
|
||||||
UserId int64 // Receiver user id.
|
UserId int64 // Receiver user id.
|
||||||
|
@ -57,7 +58,7 @@ func (a Action) GetContent() string {
|
||||||
return a.Content
|
return a.Content
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitRepoAction records action for commit 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, commits *base.PushCommits) error {
|
||||||
log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName)
|
log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName)
|
||||||
|
@ -68,12 +69,13 @@ func CommitRepoAction(userId int64, userName string,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = NotifyWatchers(userId, repoId, OP_COMMIT_REPO, userName, repoName, refName, string(bs)); err != nil {
|
if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, OpType: OP_COMMIT_REPO,
|
||||||
|
Content: string(bs), RepoId: repoId, RepoName: repoName, RefName: refName}); err != nil {
|
||||||
log.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName)
|
log.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update repository last update time.
|
// Change repository bare status and update last updated time.
|
||||||
repo, err := GetRepositoryByName(userId, repoName)
|
repo, err := GetRepositoryByName(userId, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("action.CommitRepoAction(GetRepositoryByName): %d/%s", userId, repoName)
|
log.Error("action.CommitRepoAction(GetRepositoryByName): %d/%s", userId, repoName)
|
||||||
|
|
|
@ -485,30 +485,21 @@ func GetWatches(repoId int64) ([]Watch, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyWatchers creates batch of actions for every watcher.
|
// NotifyWatchers creates batch of actions for every watcher.
|
||||||
func NotifyWatchers(userId, repoId int64, opType int, userName, repoName, refName, content string) error {
|
func NotifyWatchers(act *Action) error {
|
||||||
// Add feeds for user self and all watchers.
|
// Add feeds for user self and all watchers.
|
||||||
watches, err := GetWatches(repoId)
|
watches, err := GetWatches(act.RepoId)
|
||||||
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: userId})
|
watches = append(watches, Watch{UserId: act.ActUserId})
|
||||||
|
|
||||||
for i := range watches {
|
for i := range watches {
|
||||||
if userId == watches[i].UserId && i > 0 {
|
if act.ActUserId == watches[i].UserId && i > 0 {
|
||||||
continue // Do not add twice in case author watches his/her repository.
|
continue // Do not add twice in case author watches his/her repository.
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = orm.InsertOne(&Action{
|
act.UserId = watches[i].UserId
|
||||||
UserId: watches[i].UserId,
|
if _, err = orm.InsertOne(act); err != nil {
|
||||||
ActUserId: userId,
|
|
||||||
ActUserName: userName,
|
|
||||||
OpType: opType,
|
|
||||||
Content: content,
|
|
||||||
RepoId: repoId,
|
|
||||||
RepoName: repoName,
|
|
||||||
RefName: refName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("repo.NotifyWatchers(create action): " + err.Error())
|
return errors.New("repo.NotifyWatchers(create action): " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -854,6 +854,10 @@ html, body {
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.commit-list .sha a {
|
||||||
|
font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
.guide-box pre, .guide-box .input-group {
|
.guide-box pre, .guide-box .input-group {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
@ -1119,7 +1123,7 @@ html, body {
|
||||||
#issue .issue-head .info {
|
#issue .issue-head .info {
|
||||||
width: 99%;
|
width: 99%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding-left: 64px;
|
padding-left: 74px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
border-bottom: 1px solid #CCC;
|
border-bottom: 1px solid #CCC;
|
||||||
|
@ -1169,6 +1173,21 @@ html, body {
|
||||||
border-color: #CCC;
|
border-color: #CCC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#issue .issue-head .info .btn {
|
||||||
|
margin-top: -8px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#issue .issue-action {
|
||||||
|
padding-left: 8px;
|
||||||
|
color: #888;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#issue-edit-title {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
/* wrapper and footer */
|
/* wrapper and footer */
|
||||||
|
|
||||||
#wrapper {
|
#wrapper {
|
||||||
|
|
|
@ -50,6 +50,14 @@ var Gogits = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$.fn.extend({
|
||||||
|
toggleHide: function () {
|
||||||
|
$(this).addClass("hidden");
|
||||||
|
},
|
||||||
|
toggleShow: function () {
|
||||||
|
$(this).removeClass("hidden");
|
||||||
|
}
|
||||||
|
})
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
@ -353,6 +361,7 @@ function initRepository() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function initInstall() {
|
function initInstall() {
|
||||||
|
// database type change
|
||||||
$('#install-database').on("change", function () {
|
$('#install-database').on("change", function () {
|
||||||
var val = $(this).val();
|
var val = $(this).val();
|
||||||
if (val != "sqlite") {
|
if (val != "sqlite") {
|
||||||
|
@ -370,6 +379,35 @@ function initInstall(){
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initIssue() {
|
||||||
|
// close button
|
||||||
|
(function () {
|
||||||
|
var $closeBtn = $('#issue-close-btn');
|
||||||
|
var $openBtn = $('#issue-open-btn');
|
||||||
|
$('#issue-reply-content').on("keyup", function () {
|
||||||
|
if ($(this).val().length) {
|
||||||
|
$closeBtn.text($closeBtn.data("text"));
|
||||||
|
$openBtn.text($openBtn.data("text"));
|
||||||
|
} else {
|
||||||
|
$closeBtn.text($closeBtn.data("origin"));
|
||||||
|
$openBtn.text($openBtn.data("origin"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
|
||||||
|
// issue edit mode
|
||||||
|
(function () {
|
||||||
|
$("#issue-edit-btn").on("click", function () {
|
||||||
|
$('#issue h1.title,#issue .issue-main > .issue-content .content,#issue-edit-btn').toggleHide();
|
||||||
|
$('#issue-edit-title,#issue-edit-content,.issue-edit-cancel,.issue-edit-save').toggleShow();
|
||||||
|
});
|
||||||
|
$('.issue-edit-cancel').on("click", function () {
|
||||||
|
$('#issue h1.title,#issue .issue-main > .issue-content .content,#issue-edit-btn').toggleShow();
|
||||||
|
$('#issue-edit-title,#issue-edit-content,.issue-edit-cancel,.issue-edit-save').toggleHide();
|
||||||
|
})
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
$(function () {
|
$(function () {
|
||||||
initCore();
|
initCore();
|
||||||
|
@ -386,5 +424,8 @@ function initInstall(){
|
||||||
if ($('#install-card').length) {
|
if ($('#install-card').length) {
|
||||||
initInstall();
|
initInstall();
|
||||||
}
|
}
|
||||||
|
if ($('#issue').length) {
|
||||||
|
initIssue();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -78,8 +78,9 @@ func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify watchers.
|
// Notify watchers.
|
||||||
if err = models.NotifyWatchers(ctx.User.Id, ctx.Repo.Repository.Id, models.OP_CREATE_ISSUE,
|
if err = models.NotifyWatchers(&models.Action{ActUserId: ctx.User.Id, ActUserName: ctx.User.Name,
|
||||||
ctx.User.Name, ctx.Repo.Repository.Name, "", fmt.Sprintf("%d|%s", issue.Index, issue.Name)); err != nil {
|
OpType: models.OP_CREATE_ISSUE, Content: fmt.Sprintf("%d|%s", issue.Index, issue.Name),
|
||||||
|
RepoId: ctx.Repo.Repository.Id, RepoName: ctx.Repo.Repository.Name, RefName: ""}); err != nil {
|
||||||
ctx.Handle(200, "issue.CreateIssue", err)
|
ctx.Handle(200, "issue.CreateIssue", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -120,6 +121,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
issue.Poster = u
|
issue.Poster = u
|
||||||
|
issue.Content = string(base.RenderMarkdown([]byte(issue.Content), ""))
|
||||||
|
|
||||||
// Get comments.
|
// Get comments.
|
||||||
comments, err := models.GetIssueComments(issue.Id)
|
comments, err := models.GetIssueComments(issue.Id)
|
||||||
|
@ -136,6 +138,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
comments[i].Poster = u
|
comments[i].Poster = u
|
||||||
|
comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Title"] = issue.Name
|
ctx.Data["Title"] = issue.Name
|
||||||
|
|
|
@ -4,12 +4,16 @@
|
||||||
{{template "repo/toolbar" .}}
|
{{template "repo/toolbar" .}}
|
||||||
<div id="body" class="container">
|
<div id="body" class="container">
|
||||||
<div id="issue">
|
<div id="issue">
|
||||||
<div id="issue-id" class="issue-whole">
|
<div id="issue-{issue.id}" class="issue-whole">
|
||||||
<div class="issue-head clearfix">
|
<div class="issue-head clearfix">
|
||||||
<div class="number pull-right">#{{.Issue.Index}}</div>
|
<div class="number pull-right">#{{.Issue.Index}}</div>
|
||||||
<a class="author pull-left" href="/user/{{.Issue.Poster.Name}}"><img class="avatar" src="{{.Issue.Poster.AvatarLink}}" alt="" width="30"/></a>
|
<a class="author pull-left" href="/user/{{.Issue.Poster.Name}}"><img class="avatar" src="{{.Issue.Poster.AvatarLink}}" alt="" width="30"/></a>
|
||||||
<h1 class="title pull-left">{{.Issue.Name}}</h1>
|
<h1 class="title pull-left">{{.Issue.Name}}</h1>
|
||||||
|
<input id="issue-edit-title" class="form-control input-lg pull-left hidden" type="text" value="{issue.title}" data-ajax-rel="issue-save"/>
|
||||||
<p class="info pull-left">
|
<p class="info pull-left">
|
||||||
|
<a class="btn btn-default pull-right issue-edit" href="#" id="issue-edit-btn">Edit</a>
|
||||||
|
<a class="btn btn-danger pull-right issue-edit-cancel hidden" href="#">Cancel</a>
|
||||||
|
<a class="btn btn-primary pull-right issue-edit-save hidden" href="#" data-ajax="{issue.save.link}" data-ajax-name="issue-save">Save</a>
|
||||||
<span class="status label label-{{if .Issue.IsClosed}}danger{{else}}success{{end}}">{{if .Issue.IsClosed}}Closed{{else}}Open{{end}}</span>
|
<span class="status label label-{{if .Issue.IsClosed}}danger{{else}}success{{end}}">{{if .Issue.IsClosed}}Closed{{else}}Open{{end}}</span>
|
||||||
<a href="/user/{{.Issue.Poster.Name}}" class="author"><strong>{{.Issue.Poster.Name}}</strong></a> opened this issue
|
<a href="/user/{{.Issue.Poster.Name}}" class="author"><strong>{{.Issue.Poster.Name}}</strong></a> opened this issue
|
||||||
<span class="time">{{TimeSince .Issue.Created}}</span> · {{.Issue.NumComments}} comments
|
<span class="time">{{TimeSince .Issue.Created}}</span> · {{.Issue.NumComments}} comments
|
||||||
|
@ -18,18 +22,24 @@
|
||||||
<div class="issue-main">
|
<div class="issue-main">
|
||||||
<div class="panel panel-default issue-content">
|
<div class="panel panel-default issue-content">
|
||||||
<div class="panel-body markdown">
|
<div class="panel-body markdown">
|
||||||
<p>{{.Issue.Content}}</p>
|
<div class="content">
|
||||||
|
{{str2html .Issue.Content}}
|
||||||
|
</div>
|
||||||
|
<textarea class="form-control hidden" name="content" id="issue-edit-content" rows="10" data-ajax-rel="issue-save">content</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{range .Comments}}
|
{{range .Comments}}
|
||||||
<div class="issue-child">
|
<div class="issue-child" id="issue-comment-{issue.comment.id}">
|
||||||
<a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt=""/></a>
|
<a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt=""/></a>
|
||||||
<div class="issue-content panel panel-default">
|
<div class="issue-content panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<a href="/user/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created}}</span>
|
<a href="/user/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created}}</span>
|
||||||
|
<a class="issue-comment-del pull-right issue-action" href="#" title="Edit Comment"><i class="fa fa-times-circle"></i></a>
|
||||||
|
<a class="issue-comment-edit pull-right issue-action" href="#" title="Remove Comment" data-url="{remove-link}"><i class="fa fa-edit"></i></a>
|
||||||
|
<span class="role label label-default pull-right">Owner</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body markdown">
|
<div class="panel-body markdown">
|
||||||
<p>{{.Content}}</p>
|
{{str2html .Content}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,7 +62,7 @@
|
||||||
<div class="tab-pane" id="issue-textarea">
|
<div class="tab-pane" id="issue-textarea">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="hidden" value="{{.Issue.Index}}" name="issueIndex"/>
|
<input type="hidden" value="{{.Issue.Index}}" name="issueIndex"/>
|
||||||
<textarea class="form-control" name="content" id="issue-content" rows="10" placeholder="Write some content">{{.content}}</textarea>
|
<textarea class="form-control" name="content" id="issue-reply-content" rows="10" placeholder="Write some content">{{.content}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="issue-preview">preview</div>
|
<div class="tab-pane" id="issue-preview">preview</div>
|
||||||
|
@ -61,7 +71,9 @@
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="hidden" value="id" name="repo-id"/>
|
<input type="hidden" value="id" name="repo-id"/>
|
||||||
<button class="btn-success btn">Comment</button>
|
<button class="btn-default btn issue-open" id="issue-open-btn" data-origin="Open" data-text="Open & Comment">Open</button>
|
||||||
|
<button class="btn-default btn issue-close" id="issue-close-btn" data-origin="Close" data-text="Close & Comment">Close</button>
|
||||||
|
<button class="btn-success btn" id="issue-reply-btn">Comment</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
8
web.go
8
web.go
|
@ -138,6 +138,10 @@ func runWeb(*cli.Context) {
|
||||||
r.Any("/:userid/delete", admin.DeleteUser)
|
r.Any("/:userid/delete", admin.DeleteUser)
|
||||||
}, adminReq)
|
}, adminReq)
|
||||||
|
|
||||||
|
if martini.Env == martini.Dev {
|
||||||
|
m.Get("/template/**", dev.TemplatePreview)
|
||||||
|
}
|
||||||
|
|
||||||
m.Group("/:username/:reponame", func(r martini.Router) {
|
m.Group("/:username/:reponame", func(r martini.Router) {
|
||||||
r.Post("/settings", repo.SettingPost)
|
r.Post("/settings", repo.SettingPost)
|
||||||
r.Get("/settings", repo.Setting)
|
r.Get("/settings", repo.Setting)
|
||||||
|
@ -168,10 +172,6 @@ func runWeb(*cli.Context) {
|
||||||
r.Any("/:reponame/**", repo.Http)
|
r.Any("/:reponame/**", repo.Http)
|
||||||
}, ignSignIn)
|
}, ignSignIn)
|
||||||
|
|
||||||
if martini.Env == martini.Dev {
|
|
||||||
m.Get("/template/**", dev.TemplatePreview)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not found handler.
|
// Not found handler.
|
||||||
m.NotFound(routers.NotFound)
|
m.NotFound(routers.NotFound)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue