Browse Source

update less and revert css to dev branch state

strings via locale

try to add action

ajax comment form loading

code review backend

new css & comments view

little ajax form loading fix

fix plus for commit line

return pre tag. This is bad? but so on

js changes

tmpl fixes

js fixes

new js

new html

trash for development

more trash for development

new comments style

comments is really works

fix plus

validation with i18n and line num fixes

Migration for commit comments

Commit comment markdown support

js fixes and new button

fix migration and cancel btn

locale
pull/946/head
Alexey Makhov 10 years ago
parent
commit
c04e40a1ac
  1. 2
      cmd/web.go
  2. 3
      conf/locale/locale_de-DE.ini
  3. 3
      conf/locale/locale_en-US.ini
  4. 3
      conf/locale/locale_fr-CA.ini
  5. 3
      conf/locale/locale_ja-JP.ini
  6. 3
      conf/locale/locale_lv-LV.ini
  7. 3
      conf/locale/locale_nl-NL.ini
  8. 3
      conf/locale/locale_ru-RU.ini
  9. 3
      conf/locale/locale_zh-CN.ini
  10. 5
      models/action.go
  11. 13
      models/issue.go
  12. 26
      models/migrations/migrations.go
  13. 5
      modules/base/template.go
  14. 57
      modules/mailer/mail.go
  15. 129
      public/ng/css/gogs.css
  16. 74
      public/ng/js/gogs.js
  17. 3
      public/ng/less/gogs/base.less
  18. 11
      public/ng/less/gogs/issue.less
  19. 59
      public/ng/less/gogs/repository.less
  20. 139
      routers/repo/commit.go
  21. 4
      routers/repo/issue.go
  22. 1
      templates/ng/base/head.tmpl
  23. 34
      templates/repo/comment_form.tmpl
  24. 50
      templates/repo/diff.tmpl
  25. 2
      templates/repo/issue/create.tmpl
  26. 2
      templates/repo/issue/milestone_edit.tmpl
  27. 2
      templates/repo/issue/milestone_new.tmpl
  28. 6
      templates/repo/issue/view.tmpl

2
cmd/web.go

@ -373,6 +373,7 @@ func runWeb(ctx *cli.Context) {
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Get("/settings", repo.Settings) m.Get("/settings", repo.Settings)
m.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) m.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Post("/commit/comment/:action/:commitId", repo.CreateCommitComment)
m.Group("/settings", func() { m.Group("/settings", func() {
m.Route("/collaboration", "GET,POST", repo.SettingsCollaboration) m.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
m.Get("/hooks", repo.Webhooks) m.Get("/hooks", repo.Webhooks)
@ -439,6 +440,7 @@ func runWeb(ctx *cli.Context) {
m.Get("/src/*", repo.Home) m.Get("/src/*", repo.Home)
m.Get("/raw/*", repo.SingleDownload) m.Get("/raw/*", repo.SingleDownload)
m.Get("/commits/*", repo.RefCommits) m.Get("/commits/*", repo.RefCommits)
m.Get("/commit/comment/*", repo.GetCommentForm)
m.Get("/commit/*", repo.Diff) m.Get("/commit/*", repo.Diff)
}, middleware.RepoRef()) }, middleware.RepoRef())

3
conf/locale/locale_de-DE.ini

@ -323,6 +323,9 @@ commits.message=Mitteilung
commits.date=Datum commits.date=Datum
commits.older=Älter commits.older=Älter
commits.newer=Neuer commits.newer=Neuer
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings=Einstellungen settings=Einstellungen
settings.options=Optionen settings.options=Optionen

3
conf/locale/locale_en-US.ini

@ -325,6 +325,9 @@ commits.message = Message
commits.date = Date commits.date = Date
commits.older = Older commits.older = Older
commits.newer = Newer commits.newer = Newer
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings = Settings settings = Settings
settings.options = Options settings.options = Options

3
conf/locale/locale_fr-CA.ini

@ -323,6 +323,9 @@ commits.message=Message
commits.date=Date commits.date=Date
commits.older=Précédemment commits.older=Précédemment
commits.newer=Récemment commits.newer=Récemment
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings=Paramètres settings=Paramètres
settings.options=Options settings.options=Options

3
conf/locale/locale_ja-JP.ini

@ -323,6 +323,9 @@ commits.message=メッセージ
commits.date=日付 commits.date=日付
commits.older=古い commits.older=古い
commits.newer=新しい commits.newer=新しい
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings=設定 settings=設定
settings.options=オプション settings.options=オプション

3
conf/locale/locale_lv-LV.ini

@ -323,6 +323,9 @@ commits.message=Ziņojums
commits.date=Datums commits.date=Datums
commits.older=Vecāki commits.older=Vecāki
commits.newer=Jaunāki commits.newer=Jaunāki
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings=Iestatījumi settings=Iestatījumi
settings.options=Opcijas settings.options=Opcijas

3
conf/locale/locale_nl-NL.ini

@ -323,6 +323,9 @@ commits.message=Bericht
commits.date=Datum commits.date=Datum
commits.older=Ouder commits.older=Ouder
commits.newer=Nieuwer commits.newer=Nieuwer
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings=Instellingen settings=Instellingen
settings.options=Opties settings.options=Opties

3
conf/locale/locale_ru-RU.ini

@ -323,6 +323,9 @@ commits.message=Сообщение
commits.date=Дата commits.date=Дата
commits.older=Раньше commits.older=Раньше
commits.newer=Новее commits.newer=Новее
commits.comment.add = Отправить комментарий
commits.comment.comment_line = Комментировать строку
commits.comment.required_field = Не заполнено обязательное поле
settings=Настройки settings=Настройки
settings.options=Опции settings.options=Опции

3
conf/locale/locale_zh-CN.ini

@ -323,6 +323,9 @@ commits.message=备注
commits.date=提交日期 commits.date=提交日期
commits.older=更旧的提交 commits.older=更旧的提交
commits.newer=更新的提交 commits.newer=更新的提交
commits.comment.add = Comment
commits.comment.comment_line = Comment line
commits.comment.required_field = Missing required field
settings=仓库设置 settings=仓库设置
settings.options=基本设置 settings.options=基本设置

5
models/action.go

@ -33,6 +33,7 @@ const (
TRANSFER_REPO // 8 TRANSFER_REPO // 8
PUSH_TAG // 9 PUSH_TAG // 9
COMMENT_ISSUE // 10 COMMENT_ISSUE // 10
COMMENT_COMMIT // 11
) )
var ( var (
@ -143,7 +144,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1) url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message) message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil { if _, err = CreateComment(userId, issue.RepoId, issue.Id, "", "", COMMIT, message, nil); err != nil {
return err return err
} }
} }
@ -194,7 +195,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
} }
// If commit happened in the referenced repository, it means the issue can be closed. // If commit happened in the referenced repository, it means the issue can be closed.
if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, CLOSE, "", nil); err != nil { if _, err = CreateComment(userId, repoId, issue.Id, "", "", CLOSE, "", nil); err != nil {
return err return err
} }
} }

13
models/issue.go

@ -884,14 +884,14 @@ type Comment struct {
PosterId int64 PosterId int64
Poster *User `xorm:"-"` Poster *User `xorm:"-"`
IssueId int64 IssueId int64
CommitId int64 CommitId string
Line int64 Line string
Content string `xorm:"TEXT"` Content string `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"` Created time.Time `xorm:"CREATED"`
} }
// CreateComment creates comment of issue or commit. // CreateComment creates comment of issue or commit.
func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType CommentType, content string, attachments []int64) (*Comment, error) { func CreateComment(userId, repoId, issueId int64, commitId, line string, cmtType CommentType, content string, attachments []int64) (*Comment, error) {
sess := x.NewSession() sess := x.NewSession()
defer sess.Close() defer sess.Close()
if err := sess.Begin(); err != nil { if err := sess.Begin(); err != nil {
@ -971,6 +971,13 @@ func (c *Comment) Attachments() []*Attachment {
return a return a
} }
// GetCommitComments returns list of comment by given commit id.
func GetCommitComments(commitId string) ([]Comment, error) {
comments := make([]Comment, 0, 10)
err := x.Asc("created").Find(&comments, &Comment{CommitId: commitId})
return comments, err
}
func (c *Comment) AfterDelete() { func (c *Comment) AfterDelete() {
_, err := DeleteAttachmentsByComment(c.Id, true) _, err := DeleteAttachmentsByComment(c.Id, true)

26
models/migrations/migrations.go

@ -2,6 +2,7 @@ package migrations
import ( import (
"errors" "errors"
"time"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
@ -16,7 +17,9 @@ type Version struct {
// This is a sequence of migrations. Add new migrations to the bottom of the list. // This is a sequence of migrations. Add new migrations to the bottom of the list.
// If you want to "retire" a migration, replace it with "expiredMigration" // If you want to "retire" a migration, replace it with "expiredMigration"
var migrations = []migration{} var migrations = []migration{
prepareToCommitComments,
}
// Migrate database to current version // Migrate database to current version
func Migrate(x *xorm.Engine) error { func Migrate(x *xorm.Engine) error {
@ -51,3 +54,24 @@ func Migrate(x *xorm.Engine) error {
func expiredMigration(x *xorm.Engine) error { func expiredMigration(x *xorm.Engine) error {
return errors.New("You are migrating from a too old gogs version") return errors.New("You are migrating from a too old gogs version")
} }
func prepareToCommitComments(x *xorm.Engine) error {
type Comment struct {
Id int64
Type int64
PosterId int64
IssueId int64
CommitId string
Line string
Content string `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"`
}
x.Sync(new(Comment))
sql := `UPDATE comment SET commit_id = '', line = '' WHERE commit_id = '0' AND line = '0'`
_, err := x.Exec(sql)
if err != nil {
return err
}
return nil
}

5
modules/base/template.go

@ -155,6 +155,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
}, },
"DiffTypeToStr": DiffTypeToStr, "DiffTypeToStr": DiffTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr, "DiffLineTypeToStr": DiffLineTypeToStr,
"DiffLinePosToStr": DiffLinePosToStr,
"ShortSha": ShortSha, "ShortSha": ShortSha,
"Md5": EncodeMd5, "Md5": EncodeMd5,
"ActionContent2Commits": ActionContent2Commits, "ActionContent2Commits": ActionContent2Commits,
@ -234,6 +235,10 @@ func DiffLineTypeToStr(diffType int) string {
return "same" return "same"
} }
func DiffLinePosToStr(file int, section int, line int) string {
return fmt.Sprintf("%vL%v%v", file + 1, section, line)
}
func Oauth2Icon(t int) string { func Oauth2Icon(t int) string {
switch t { switch t {
case 1: case 1:

57
modules/mailer/mail.go

@ -154,8 +154,33 @@ func SendResetPasswdMail(r macaron.Render, u *models.User) {
SendAsync(&msg) SendAsync(&msg)
} }
// SendIssueNotifyMail sends mail notification of all watchers of repository. // SendIssueNotifyMail sends mail notification to all watchers of repository.
func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) { func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) {
subject := fmt.Sprintf("[%s] %s(#%d)", repo.Name, issue.Name, issue.Index)
content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.",
base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name),
setting.AppUrl, owner.Name, repo.Name, issue.Index)
msgInfo := fmt.Sprintf("Subject: %s, send issue notify emails", subject)
return SendNotifyMail(u, repo, subject, content, msgInfo)
}
// SendCommentNotifyMail sends mail notification to all watchers of repository.
func SendCommentNotifyMail(u, owner *models.User, repo *models.Repository, comment *models.Comment) ([]string, error) {
subject := fmt.Sprintf("[%s] New comment to commit %s", repo.Name, comment.CommitId)
content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/commit/%s\">View it on Gogs</a>.",
base.RenderSpecialLink([]byte(comment.Content), owner.Name+"/"+repo.Name),
setting.AppUrl, owner.Name, repo.Name, comment.CommitId)
msgInfo := fmt.Sprintf("Subject: %s, send issue notify emails", subject)
return SendNotifyMail(u, repo, subject, content, msgInfo)
}
func SendNotifyMail(u *models.User, repo *models.Repository, subject, content, msgInfo string) ([]string, error) {
ws, err := models.GetWatchers(repo.Id) ws, err := models.GetWatchers(repo.Id)
if err != nil { if err != nil {
return nil, errors.New("mail.NotifyWatchers(GetWatchers): " + err.Error()) return nil, errors.New("mail.NotifyWatchers(GetWatchers): " + err.Error())
@ -178,12 +203,8 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *
return tos, nil return tos, nil
} }
subject := fmt.Sprintf("[%s] %s(#%d)", repo.Name, issue.Name, issue.Index)
content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.",
base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name),
setting.AppUrl, owner.Name, repo.Name, issue.Index)
msg := NewMailMessageFrom(tos, u.Email, subject, content) msg := NewMailMessageFrom(tos, u.Email, subject, content)
msg.Info = fmt.Sprintf("Subject: %s, send issue notify emails", subject) msg.Info = msgInfo
SendAsync(&msg) SendAsync(&msg)
return tos, nil return tos, nil
} }
@ -213,6 +234,30 @@ func SendIssueMentionMail(r macaron.Render, u, owner *models.User,
return nil return nil
} }
func SendCommentMentionMail(r macaron.Render, u, owner *models.User,
repo *models.Repository, comment *models.Comment, tos []string) error {
if len(tos) == 0 {
return nil
}
subject := fmt.Sprintf("[%s] Commit %s", repo.Name, comment.CommitId)
data := GetMailTmplData(nil)
data["IssueLink"] = fmt.Sprintf("%s/%s/commit/%s", owner.Name, repo.Name, comment.CommitId)
data["Subject"] = subject
body, err := r.HTMLString(string(NOTIFY_MENTION), data)
if err != nil {
return fmt.Errorf("mail.SendCommentMentionMail(fail to render): %v", err)
}
msg := NewMailMessageFrom(tos, u.Email, subject, body)
msg.Info = fmt.Sprintf("Subject: %s, send comment mention emails", subject)
SendAsync(&msg)
return nil
}
// SendCollaboratorMail sends mail notification to new collaborator. // SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(r macaron.Render, u, owner *models.User, func SendCollaboratorMail(r macaron.Render, u, owner *models.User,
repo *models.Repository) error { repo *models.Repository) error {

129
public/ng/css/gogs.css

@ -278,6 +278,9 @@ img.avatar-100 {
padding-left: 0; padding-left: 0;
list-style: none; list-style: none;
} }
.text-danger {
color: #a94442;
}
.markdown { .markdown {
background-color: white; background-color: white;
font-size: 16px; font-size: 16px;
@ -1449,42 +1452,9 @@ The register and sign-in page style
cursor: pointer; cursor: pointer;
display: block; display: block;
} }
.code-view .lines-code b:hover {
width: 22px; .code-view .comment {
height: 22px; background-color: #fafafa;
line-height: 22px;
font-size: 22px;
margin: -1px -12px -2px -11px;
}
.code-view .lines-code b {
position: relative;
z-index: 5;
float: left;
width: 20px;
height: 20px;
margin: 0px -10px -1px -10px;
line-height: 20px;
color: #fff;
text-align: center;
text-indent: 0;
cursor: pointer;
background-color: #4183c4;
background-color: #4183c4;
background-image: -webkit-linear-gradient(#5490ca, #4183c4);
background-image: linear-gradient(#5490ca, #4183c4);
background-repeat: repeat-x;
border-radius: 3px;
box-shadow: 0 1px 4px rgba(0,0,0,0.15);
opacity: 0;
-webkit-transform: scale(0.8, 0.8);
-ms-transform: scale(0.8, 0.8);
transform: scale(0.8, 0.8);
-webkit-transition: -webkit-transform 0.1s ease-in-out;
transition: transform 0.1s ease-in-out;
font-size: 20px;
}
.code-view .lines-code b.ishovered {
opacity:1;
} }
.code-view .lines-code > pre { .code-view .lines-code > pre {
border: none; border: none;
@ -1499,6 +1469,85 @@ The register and sign-in page style
.code-view .lines-code > pre > ol.linenums > li.active { .code-view .lines-code > pre > ol.linenums > li.active {
background: #ffffdd; background: #ffffdd;
} }
.code-view .lines-code > b {
position: relative;
float: left;
width: 16px;
height: 16px;
margin: 2px -8px 0px -8px;
line-height: 16px;
color: #fff;
text-align: center;
cursor: pointer;
background-color: #428bca;
border-radius: 2px;
opacity: 0;
font-size: 16px;
}
.code-view .lines-code > b.ishovered {
opacity: 1;
}
.code-view .lines-code > b:hover {
width: 18px;
height: 18px;
line-height: 18px;
font-size: 20px;
margin: 1px -10px 0px -9px;
}
.code-view .lines-code .commit-comment {
margin: 5px;
max-width: 800px;
}
.code-view .lines-code .commit-comment .label-default {
background-color: #999;
font-size: 80%;
padding: 5px;
line-height: 10px;
margin-right: 5px;
}
.code-view .lines-code .commit-comment .commit-content {
border-bottom-width: 1px;
}
.code-view .lines-code .commit-comment .user.pull-left {
margin: 5px;
}
.code-view .lines-code .commit-comment .user .avatar {
width: 26px;
height: 26px;
margin-right: 3px;
}
.code-view .lines-code .commit-comment .panel-heading {
padding-top: 4px;
padding-bottom: 0px;
font-weight: normal;
background-color: #FAFAFA;
border-bottom: 1px solid #DDD;
}
.code-view .lines-code .commit-comment .panel-default .panel-heading {
font-weight: bold;
}
.code-view .lines-code .commit-comment .markdown {
font-size: 14px;
padding: 12px 15px;
}
.code-view .panel-default {
border-color: #d8d8d8;
}
.code-view .alert {
padding: 8px;
margin-bottom: 18px;
border-radius: 2px;
border: 1px solid transparent;
margin: 5px;
}
.code-view .alert-warning {
background-color: #f9edbe;
border-color: #f0c36d;
color: #333;
}
.code-view #commit-conversation {
margin: 10px;
}
.repo-setting-zone { .repo-setting-zone {
padding: 30px; padding: 30px;
} }
@ -1664,6 +1713,10 @@ The register and sign-in page style
background-color: #ffe2dd !important; background-color: #ffe2dd !important;
border-color: #e9aeae !important; border-color: #e9aeae !important;
} }
.diff-file-box .code-diff tbody tr.del-code td.selected-line,
.diff-file-box .code-diff tbody tr.del-code td.selected-line pre {
background-color: #ffffdd !important;
}
.diff-file-box .code-diff tbody tr.add-code td, .diff-file-box .code-diff tbody tr.add-code td,
.diff-file-box .code-diff tbody tr.add-code pre { .diff-file-box .code-diff tbody tr.add-code pre {
background-color: #d1ffd6 !important; background-color: #d1ffd6 !important;
@ -1678,6 +1731,10 @@ The register and sign-in page style
background-color: #FFF8D2 !important; background-color: #FFF8D2 !important;
border-color: #F0DB88 !important; border-color: #F0DB88 !important;
} }
.diff-file-box .code-diff tbody tr.add-comment:hover td,
.diff-file-box .code-diff tbody tr.add-comment:hover pre {
background-color: #FFFFFF !important;
}
.compare-head-box { .compare-head-box {
margin-top: 10px; margin-top: 10px;
} }

74
public/ng/js/gogs.js

@ -238,6 +238,11 @@ var Gogs = {};
$.changeHash('#' + $select.attr('rel')); $.changeHash('#' + $select.attr('rel'));
} }
function prepareToForm() {
$('.add-comment').hide('fast', function(){ $(this).remove(); });
$('button.answer').show();
}
$(document).on('click', '.code-diff .lines-num span', function (e) { $(document).on('click', '.code-diff .lines-num span', function (e) {
var $select = $(this); var $select = $(this);
var $list = $select.parent().siblings('.lines-code').parents().find('td.lines-num > span'); var $list = $select.parent().siblings('.lines-code').parents().find('td.lines-num > span');
@ -249,6 +254,57 @@ var Gogs = {};
$.deSelect(); $.deSelect();
}); });
$('.code-diff .lines-code > b, .code-diff .lines-code > button.answer').click(function () {
prepareToForm();
var commit = document.location.href.match(/([a-zA-Z0-9:\/\/]+)\/commit\/([a-z0-9]+)/);
var lineNum;
if ($(this).prop("tagName") == "BUTTON") {
lineNum = $(this).attr('rel');
} else {
lineNum = $(this).parent().prev().find('span').attr('rel');
}
$('button[rel='+lineNum+']').fadeOut();
lineNum = lineNum.substr(5);
var commentTr = $(".comment-"+lineNum);
if (commit) {
var elem = (commentTr.length > 0) ? commentTr : $(this).parents('tr');
var url = commit[1] + '/commit/comment/' + commit[2];
elem.after(
$('<tr class="add-comment">').load(url + '?line=' + lineNum, function () {
$('.menu-line.add-nav').tabs();
$('#pull-commit-preview').markdown_preview(".commit-add-comment");
$('body').animate({
scrollTop: $(this).offset().top - 33 // height of button
}, 1000);
})
);
}
});
$('.code-diff').on('click', '#cancel-commit-conversation', function () {
prepareToForm();
return false;
});
$('.code-diff').on('submit', '#commit-add-comment-form', function () {
var url = $(this).attr('action');
$.ajax({
url: url,
data: $(this).serialize(),
dataType: "json",
method: "post",
success: function (json) {
if (json.ok && json.data.length) {
window.location.href = json.data;
location.reload();
} else {
$('#submit-error').html(json.error);
}
}
});
return false;
});
$('.code-diff .lines-code > pre').each(function () { $('.code-diff .lines-code > pre').each(function () {
var $pre = $(this); var $pre = $(this);
var $lineCode = $pre.parent(); var $lineCode = $pre.parent();
@ -261,10 +317,16 @@ var Gogs = {};
} }
}); });
$('.code-diff .lines-code > pre').hover(function () { $('.code-diff .add-code .lines-code > pre, \
var $b = $(this).prev(); .code-diff .del-code .lines-code > pre, \
.code-diff .add-code .lines-code > b, \
.code-diff .del-code .lines-code > b, \
.code-diff .add-code .lines-num, \
.code-diff .del-code .lines-num').hover(function () {
var $b = $(this).parents('tr').find('b');
$b.addClass('ishovered'); $b.addClass('ishovered');
}); });
$('.code-diff tr').mouseleave(function () { $('.code-diff tr').mouseleave(function () {
$('.code-diff .lines-code > b').removeClass('ishovered'); $('.code-diff .lines-code > b').removeClass('ishovered');
}); });
@ -284,6 +346,14 @@ var Gogs = {};
$first = $list.filter('[rel=diff-' + m[1] + m[2] + ']'); $first = $list.filter('[rel=diff-' + m[1] + m[2] + ']');
selectRange($list, $first); selectRange($list, $first);
$("html, body").scrollTop($first.offset().top - 200); $("html, body").scrollTop($first.offset().top - 200);
return;
}
m = window.location.hash.match(/^#comment-(\d+)$/);
if (m) {
$("html, body").animate({
scrollTop: $('a[name=comment-'+m[1]+']').offset().top
}, 1000);
return;
} }
}).trigger('hashchange'); }).trigger('hashchange');
}; };

3
public/ng/less/gogs/base.less

@ -298,4 +298,7 @@ clear: both;
.list-unstyled { .list-unstyled {
padding-left: 0; padding-left: 0;
list-style: none; list-style: none;
}
.text-danger {
color: #a94442;
} }

11
public/ng/less/gogs/issue.less

@ -86,6 +86,7 @@
} }
#pr-commit, #pr-commit,
#pr-file-diff, #pr-file-diff,
#commit-add-comment-preview,
#issue-add-comment-preview { #issue-add-comment-preview {
display: none; display: none;
} }
@ -93,14 +94,16 @@
padding-right: 30px; padding-right: 30px;
box-sizing: border-box; box-sizing: border-box;
} }
.commit-comment,
.issue-comment, .issue-comment,
.issue-commit, .issue-commit,
.issue-line, .issue-line,
.issue-merge, .issue-merge,
.commit-add-comment,
.issue-add-comment { .issue-add-comment {
margin-bottom: 24px; margin-bottom: 24px;
} }
.issue-comment { .commit-comment, .issue-comment {
.author-avatar { .author-avatar {
img { img {
margin-right: 12px; margin-right: 12px;
@ -184,7 +187,8 @@
height: 4px; height: 4px;
background-color: #E6E6E6; background-color: #E6E6E6;
} }
.issue-add-comment { .issue-add-comment,
.commit-add-comment {
.panel { .panel {
margin-left: 60px; margin-left: 60px;
margin-top: -40px; margin-top: -40px;
@ -215,7 +219,8 @@
} }
} }
} }
textarea#issue-add-content { textarea#issue-add-content,
textarea#commit-add-content {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
height: 120px; height: 120px;

59
public/ng/less/gogs/repository.less

@ -477,16 +477,51 @@
display: block; display: block;
} }
} }
.lines-code > pre { .lines-code {
border: none; > pre {
border-left: 1px solid #ddd; border: none;
> ol.linenums > li { border-left: 1px solid #ddd;
padding: 0 10px; border-radius: 0;
line-height: 20px; padding-left: 15px;
&.active { > ol.linenums > li {
background: #ffffdd; padding: 0 10px;
line-height: 20px;
&.active {
background: #ffffdd;
}
} }
} }
> b {
position: relative;
float: left;
width: 16px;
height: 16px;
margin: 2px -8px 0px -8px;
line-height: 16px;
color: #fff;
text-align: center;
cursor: pointer;
background-color: #428bca;
border-radius: 2px;
opacity: 0;
font-size: 16px;
&.ishovered {
opacity: 1;
}
&:hover {
width: 18px;
height: 18px;
line-height: 18px;
font-size: 20px;
margin: 1px -10px 0px -9px;
}
}
button.answer {
margin: 0 0 10px 70px;
}
}
.commit-comment {
margin: 10px;
} }
} }
.repo-setting-zone { .repo-setting-zone {
@ -659,6 +694,9 @@
background-color: #ffe2dd !important; background-color: #ffe2dd !important;
border-color: #e9aeae !important; border-color: #e9aeae !important;
} }
td.selected-line, td.selected-line pre {
background-color: #ffffdd !important;
}
} }
&.add-code { &.add-code {
td, pre { td, pre {
@ -675,6 +713,11 @@
border-color: #F0DB88 !important; border-color: #F0DB88 !important;
} }
} }
&.add-comment:hover, &.comment:hover {
td, pre {
background-color: #FFFFFF !important;
}
}
} }
} }
} }

139
routers/repo/commit.go

@ -6,20 +6,27 @@ package repo
import ( import (
"container/list" "container/list"
"errors"
"fmt"
"path" "path"
"regexp"
"strings"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git" "github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const ( const (
COMMITS base.TplName = "repo/commits" COMMITS base.TplName = "repo/commits"
DIFF base.TplName = "repo/diff" DIFF base.TplName = "repo/diff"
COMMENT_FORM base.TplName = "repo/comment_form"
) )
func RefCommits(ctx *middleware.Context) { func RefCommits(ctx *middleware.Context) {
@ -243,6 +250,33 @@ func Diff(ctx *middleware.Context) {
} }
} }
// Get comments.
comments, err := models.GetCommitComments(commitId)
if err != nil {
ctx.Handle(500, "commit.GetDiffCommit(GetCommitComments): %v", err)
return
}
// Get posters.
commentsMap := make(map[string]map[int]models.Comment)
for i := range comments {
u, err := models.GetUserById(comments[i].PosterId)
if err != nil {
ctx.Handle(500, "issue.ViewIssue(GetUserById.2): %v", err)
return
}
comments[i].Poster = u
if comments[i].Type == models.COMMENT {
comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), ctx.Repo.RepoLink))
}
if _, ok := commentsMap[comments[i].Line]; !ok {
commentsMap[comments[i].Line] = make(map[int]models.Comment)
}
commentsMap[comments[i].Line][i] = comments[i]
}
ctx.Data["Username"] = userName ctx.Data["Username"] = userName
ctx.Data["Reponame"] = repoName ctx.Data["Reponame"] = repoName
ctx.Data["IsImageFile"] = isImageFile ctx.Data["IsImageFile"] = isImageFile
@ -251,6 +285,7 @@ func Diff(ctx *middleware.Context) {
ctx.Data["Author"] = models.ValidateCommitWithEmail(commit) ctx.Data["Author"] = models.ValidateCommitWithEmail(commit)
ctx.Data["Diff"] = diff ctx.Data["Diff"] = diff
ctx.Data["Parents"] = parents ctx.Data["Parents"] = parents
ctx.Data["Comments"] = commentsMap
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(userName, repoName, "src", commitId) ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(userName, repoName, "src", commitId)
ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(userName, repoName, "raw", commitId) ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(userName, repoName, "raw", commitId)
@ -319,3 +354,103 @@ func CompareDiff(ctx *middleware.Context) {
ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(userName, repoName, "raw", afterCommitId) ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(userName, repoName, "raw", afterCommitId)
ctx.HTML(200, DIFF) ctx.HTML(200, DIFF)
} }
func GetCommentForm(ctx *middleware.Context) {
ctx.Data["Repo"] = ctx.Repo
ctx.Data["Line"] = ctx.Query("line")
ctx.HTML(200, COMMENT_FORM)
}
func CreateCommitComment(ctx *middleware.Context) {
send := func(status int, data interface{}, err error) {
if err != nil {
log.Error(4, "commit.Comment(?): %s", err)
ctx.JSON(status, map[string]interface{}{
"ok": false,
"status": status,
"error": err.Error(),
})
} else {
ctx.JSON(status, map[string]interface{}{
"ok": true,
"status": status,
"data": data,
})
}
}
var comment *models.Comment
commitId := ctx.ParamsEscape(":commitId")
content := ctx.Query("content")
line := ctx.Query("line")
lineRe, err := regexp.Compile("[0-9]+L[0-9]+")
fmt.Println(ctx.Locale.Tr("repo.commits.comment.required_field"))
if len(content) > 0 && lineRe.MatchString(line) {
switch ctx.Params(":action") {
case "new":
if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, 0, commitId, line, models.COMMENT, content, nil); err != nil {
send(500, nil, err)
return
}
log.Trace("%s Comment created: %s", ctx.Req.RequestURI, commitId)
default:
ctx.Handle(404, "commit.Comment", err)
return
}
} else {
err := errors.New(ctx.Locale.Tr("repo.commits.comment.required_field"))
send(200, err.Error(), err)
return
}
// Update mentions.
ms := base.MentionPattern.FindAllString(comment.Content, -1)
if len(ms) > 0 {
for i := range ms {
ms[i] = ms[i][1:]
}
}
// Notify watchers.
act := &models.Action{
ActUserId: ctx.User.Id,
ActUserName: ctx.User.LowerName,
ActEmail: ctx.User.Email,
OpType: models.COMMENT_COMMIT,
Content: fmt.Sprintf("%s|%s", commitId, strings.Split(content, "\n")[0]),
RepoId: ctx.Repo.Repository.Id,
RepoUserName: ctx.Repo.Owner.LowerName,
RepoName: ctx.Repo.Repository.LowerName,
}
if err = models.NotifyWatchers(act); err != nil {
send(500, nil, err)
return
}
// Mail watchers and mentions.
if setting.Service.EnableNotifyMail {
comment.Content = content
tos, err := mailer.SendCommentNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, comment)
if err != nil {
send(500, nil, err)
return
}
tos = append(tos, ctx.User.LowerName)
newTos := make([]string, 0, len(ms))
for _, m := range ms {
if com.IsSliceContainsStr(tos, m) {
continue
}
newTos = append(newTos, m)
}
if err = mailer.SendCommentMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner,
ctx.Repo.Repository, comment, models.GetUserEmailsByNames(newTos)); err != nil {
send(500, nil, err)
return
}
}
send(200, fmt.Sprintf("%s/commit/%s#comment-%d", ctx.Repo.RepoLink, commitId, comment.Id), nil)
}

4
routers/repo/issue.go

@ -779,7 +779,7 @@ func Comment(ctx *middleware.Context) {
cmtType = models.REOPEN cmtType = models.REOPEN
} }
if _, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, cmtType, "", nil); err != nil { if _, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, "", "", cmtType, "", nil); err != nil {
send(200, nil, err) send(200, nil, err)
return return
} }
@ -795,7 +795,7 @@ func Comment(ctx *middleware.Context) {
if len(content) > 0 || len(ctx.Req.MultipartForm.File["attachments"]) > 0 { if len(content) > 0 || len(ctx.Req.MultipartForm.File["attachments"]) > 0 {
switch ctx.Params(":action") { switch ctx.Params(":action") {
case "new": case "new":
if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.COMMENT, content, nil); err != nil { if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, "", "", models.COMMENT, content, nil); err != nil {
send(500, nil, err) send(500, nil, err)
return return
} }

1
templates/ng/base/head.tmpl

@ -27,6 +27,7 @@
<link rel="stylesheet" href="{{AppSubUrl}}/ng/css/magnific-popup.css"> <link rel="stylesheet" href="{{AppSubUrl}}/ng/css/magnific-popup.css">
<link rel="stylesheet" href="{{AppSubUrl}}/ng/fonts/octicons.css"> <link rel="stylesheet" href="{{AppSubUrl}}/ng/fonts/octicons.css">
<link rel="stylesheet" href="{{AppSubUrl}}/css/github.min.css"> <link rel="stylesheet" href="{{AppSubUrl}}/css/github.min.css">
<link rel="stylesheet" href="{{AppSubUrl}}/css/markdown.css">
<!-- JavaScript --> <!-- JavaScript -->
<script src="{{AppSubUrl}}/ng/js/lib/lib.js"></script> <script src="{{AppSubUrl}}/ng/js/lib/lib.js"></script>

34
templates/repo/comment_form.tmpl

@ -0,0 +1,34 @@
<td colspan="3">
{{if .SignedUser}}
<div id="commit-conversation" class="js-tab-show clear commit-comment">
<div id="commit-conversation-list" class="left grid-5-6">
<div class="commit-add-comment clear">
<img class="avatar-40 radius" src="{{.SignedUser.AvatarLink}}" alt=""/>
<div class="panel panel-radius">
<div class="panel-header">
<ul class="menu menu-line add-nav">
<li class="js-tab-nav js-tab-nav-show" data-tab-target="#commit-add-comment-form"><a href="#">{{$.i18n.Tr "repo.release.write"}}</a></li>
<li class="js-tab-nav" data-tab-target="#commit-add-comment-preview"><a href="#" id="pull-commit-preview">{{$.i18n.Tr "repo.release.preview"}}</a></li>
</ul>
</div>
<div class="panel-content content">
<div id="submit-error" class="text-danger"></div>
<form id="commit-add-comment-form" action="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Repository.Name}}/commit/comment/new/{{.Repo.CommitId}}" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="line" value="{{.Line}}" >
<textarea class="ipt ipt-radius js-preview-input" name="content" id="commit-add-content"></textarea>
<p class="submit text-right">
<button class="btn btn-gray btn-radius text-bold" id="cancel-commit-conversation" name="submit" value="close">{{$.i18n.Tr "cancel"}}</button>&nbsp;&nbsp;
<button class="btn btn-green btn-radius text-bold" name="submit" value="comment">{{$.i18n.Tr "repo.commits.comment.add"}}</button>
</p>
</form>
<div id="commit-add-comment-preview" class="js-preview-container markdown">
preview
</div>
</div>
</div>
</div>
</div>
</div>
{{else}}<div class="alert alert-warning"><a class="btn btn-success btn-lg" href="{{AppSubUrl}}/user/sign_up">Sign up for free</a> to join this conversation. Already have an account? <a href="{{AppSubUrl}}/user/login">Sign in to comment</a></div>{{end}}
</td>

50
templates/repo/diff.tmpl

@ -37,7 +37,7 @@
<img class="avatar-30" src="{{AvatarLink .Commit.Author.Email}}" /> <img class="avatar-30" src="{{AvatarLink .Commit.Author.Email}}" />
<strong>{{.Commit.Author.Name}}</strong> <strong>{{.Commit.Author.Name}}</strong>
{{end}} {{end}}
<span class="text-grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span> <span class="text-grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span>
</p> </p>
</div> </div>
</div> </div>
@ -102,19 +102,57 @@
<table> <table>
<tbody> <tbody>
{{range $j, $section := $file.Sections}} {{range $j, $section := $file.Sections}}
{{range $k, $line := $section.Lines}} {{range $k, $line := $section.Lines}}
{{$lineNum := (DiffLinePosToStr $i $j $k)}}
<tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$i}} ol-{{$i}}"> <tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$i}} ol-{{$i}}">
<td class="lines-num lines-num-old"> <td class="lines-num lines-num-old">
<span rel="diff-{{Add $i 1}}L{{$j}}{{$k}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span> <span rel="diff-{{$lineNum}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
</td> </td>
<td class="lines-num lines-num-new"> <td class="lines-num lines-num-new">
<span rel="diff-{{Add $i 1}}L{{$j}}{{$k}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span> <span rel="diff-{{$lineNum}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
</td> </td>
<td class="lines-code"> <td class="lines-code">
<b>+</b> {{if $.SignedUser}}{{if $k}}<b class="octicon octicon-plus" aria-label="Add line comment"></b>{{end}}{{end}}
<pre>{{$line.Content}}</pre> <pre>{{$line.Content}}</pre>
</td> </td>
</tr> </tr>
{{if index $.Comments $lineNum}}
<tr class="comment comment-{{$lineNum}}">
<td class="lines-code" colspan="3">
{{range (index $.Comments $lineNum)}}
<a name="comment-{{.Id}}"></a>
<div class="commit-comment clear" id="commit-comment-{{.Id}}">
<a class="author-avatar" href="{{AppSubUrl}}/{{.Poster.Name}}">
<img class="avatar-40 radius"
src="{{.Poster.AvatarLink}}"
alt=""></a>
<div class="panel panel-radius">
<p class="panel-header clear"><a class="author-name" href="#">{{.Poster.Name}}</a>
<span class="date">{{TimeSince .Created $.Lang}}</span>
<span class="action right">
{{if $.IsRepositoryOwner}}
<span class="label label-black label-radius">{{$.i18n.Tr "repo.owner"}}</span>
{{end}}
<!--<a href="#"><i class="octicon octicon-pencil"></i></a>-->
<!--<a href="#"><i class="octicon octicon-x"></i></a>-->
</span>
</p>
<div class="panel-content content markdown">
{{if len .Content}}
{{Str2html .Content}}
{{else}}
<i>No comment entered</i>
{{end}}
</div>
</div>
</div>
{{end}}
<button rel="diff-{{$lineNum}}" class="btn btn-gray btn-radius text-bold answer" value="close">{{$.i18n.Tr "repo.commits.comment.comment_line"}}</button>
</td>
</tr>
{{end}}
{{end}} {{end}}
{{end}} {{end}}
</tbody> </tbody>
@ -122,7 +160,7 @@
{{end}} {{end}}
</div> </div>
</div> </div>
<br> <br>
{{end}} {{end}}
{{end}} {{end}}
</div> </div>

2
templates/repo/issue/create.tmpl

@ -86,7 +86,7 @@
{{end}} {{end}}
<div class="form-group panel-body"> <div class="form-group panel-body">
<div class="md-help pull-right"><!-- todo help link --> <div class="md-help pull-right"><!-- todo help link -->
Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> {{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}}
</div> </div>
<ul class="nav nav-tabs" data-init="tabs"> <ul class="nav nav-tabs" data-init="tabs">
<li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li> <li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li>

2
templates/repo/issue/milestone_edit.tmpl

@ -16,7 +16,7 @@
</div> </div>
<div class="form-group panel-body"> <div class="form-group panel-body">
<div class="md-help pull-right"><!-- todo help link --> <div class="md-help pull-right"><!-- todo help link -->
Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> {{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}}
</div> </div>
<ul class="nav nav-tabs" data-init="tabs"> <ul class="nav nav-tabs" data-init="tabs">
<li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li> <li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li>

2
templates/repo/issue/milestone_new.tmpl

@ -16,7 +16,7 @@
</div> </div>
<div class="form-group panel-body"> <div class="form-group panel-body">
<div class="md-help pull-right"><!-- todo help link --> <div class="md-help pull-right"><!-- todo help link -->
Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> {{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}}
</div> </div>
<ul class="nav nav-tabs" data-init="tabs"> <ul class="nav nav-tabs" data-init="tabs">
<li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li> <li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li>

6
templates/repo/issue/view.tmpl

@ -29,7 +29,8 @@
</div> </div>
<div class="issue-edit-content hidden"> <div class="issue-edit-content hidden">
<div class="form-group"> <div class="form-group">
<div class="md-help pull-right">Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> <div class="md-help pull-right">
{{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}}
</div> </div>
<ul class="nav nav-tabs" data-init="tabs"> <ul class="nav nav-tabs" data-init="tabs">
<li class="issue-write active"><a href="#issue-edit-textarea" data-toggle="tab">Write</a></li> <li class="issue-write active"><a href="#issue-edit-textarea" data-toggle="tab">Write</a></li>
@ -125,7 +126,8 @@
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
<div class="md-help pull-right">Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> <div class="md-help pull-right">
{{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}}
</div> </div>
<ul class="nav nav-tabs" data-init="tabs"> <ul class="nav nav-tabs" data-init="tabs">
<li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li> <li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li>

Loading…
Cancel
Save