Commit 72c6be2d1a9794c19fa18148434f821d52e20018

Authored by Dmitriy Zaporozhets
2 parents c120457a 5443021a

Merge pull request #1183 from riyad/gitlab-flavored-markdown

Gitlab flavored markdown
app/controllers/refs_controller.rb
@@ -90,6 +90,7 @@ class RefsController < ApplicationController @@ -90,6 +90,7 @@ class RefsController < ApplicationController
90 90
91 @repo = project.repo 91 @repo = project.repo
92 @commit = project.commit(@ref) 92 @commit = project.commit(@ref)
  93 + @commit = CommitDecorator.decorate(@commit)
93 @tree = Tree.new(@commit.tree, project, @ref, params[:path]) 94 @tree = Tree.new(@commit.tree, project, @ref, params[:path])
94 @tree = TreeDecorator.new(@tree) 95 @tree = TreeDecorator.new(@tree)
95 @hex_path = Digest::SHA1.hexdigest(params[:path] || "/") 96 @hex_path = Digest::SHA1.hexdigest(params[:path] || "/")
app/helpers/application_helper.rb
@@ -42,8 +42,88 @@ module ApplicationHelper @@ -42,8 +42,88 @@ module ApplicationHelper
42 grouped_options_for_select(options, @ref || @project.default_branch) 42 grouped_options_for_select(options, @ref || @project.default_branch)
43 end 43 end
44 44
  45 + def gfm(text, html_options = {})
  46 + return text if text.nil?
  47 + raise "@project is not set" if @project.nil?
  48 +
  49 + # Extract pre blocks
  50 + # from http://github.github.com/github-flavored-markdown/
  51 + extractions = {}
  52 + text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
  53 + md5 = Digest::MD5.hexdigest(match)
  54 + extractions[md5] = match
  55 + "{gfm-extraction-#{md5}}"
  56 + end
  57 +
  58 + # match 1 2 3 4 5 6
  59 + text.gsub!(/(\W)?(@([\w\._]+)|[#!$](\d+)|([\h]{6,40}))(\W)?/) do |match|
  60 + prefix = $1
  61 + reference = $2
  62 + user_name = $3
  63 + issue_id = $4
  64 + merge_request_id = $4
  65 + snippet_id = $4
  66 + commit_id = $5
  67 + suffix = $6
  68 +
  69 + # TODO: add popups with additional information
  70 + ref_link = case reference
  71 +
  72 + # team member: @foo
  73 + when /^@/
  74 + user = @project.users.where(:name => user_name).first
  75 + member = @project.users_projects.where(:user_id => user).first if user
  76 + link_to("@#{user_name}", project_team_member_path(@project, member), html_options.merge(:class => "gfm gfm-team_member #{html_options[:class]}")) if member
  77 +
  78 + # issue: #123
  79 + when /^#/
  80 + # avoid HTML entities
  81 + unless prefix.try(:end_with?, "&") && suffix.try(:start_with?, ";")
  82 + issue = @project.issues.where(:id => issue_id).first
  83 + link_to("##{issue_id}", project_issue_path(@project, issue), html_options.merge(:title => "Issue: #{issue.title}", :class => "gfm gfm-issue #{html_options[:class]}")) if issue
  84 + end
  85 +
  86 + # merge request: !123
  87 + when /^!/
  88 + merge_request = @project.merge_requests.where(:id => merge_request_id).first
  89 + link_to("!#{merge_request_id}", project_merge_request_path(@project, merge_request), html_options.merge(:title => "Merge Request: #{merge_request.title}", :class => "gfm gfm-merge_request #{html_options[:class]}")) if merge_request
  90 +
  91 + # snippet: $123
  92 + when /^\$/
  93 + snippet = @project.snippets.where(:id => snippet_id).first
  94 + link_to("$#{snippet_id}", project_snippet_path(@project, snippet), html_options.merge(:title => "Snippet: #{snippet.title}", :class => "gfm gfm-snippet #{html_options[:class]}")) if snippet
  95 +
  96 + # commit: 123456...
  97 + when /^\h/
  98 + commit = @project.commit(commit_id)
  99 + link_to(commit_id, project_commit_path(@project, :id => commit.id), html_options.merge(:title => "Commit: #{commit.author_name} - #{CommitDecorator.new(commit).title}", :class => "gfm gfm-commit #{html_options[:class]}")) if commit
  100 +
  101 + end # case
  102 +
  103 + ref_link.nil? ? match : "#{prefix}#{ref_link}#{suffix}"
  104 + end # gsub
  105 +
  106 + # Insert pre block extractions
  107 + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
  108 + extractions[$1]
  109 + end
  110 +
  111 + text.html_safe
  112 + end
  113 +
  114 + # circumvents nesting links, which will behave bad in browsers
  115 + def link_to_gfm(body, url, html_options = {})
  116 + gfm_body = gfm(body, html_options)
  117 +
  118 + gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
  119 + "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
  120 + end
  121 +
  122 + link_to(gfm_body.html_safe, url, html_options)
  123 + end
  124 +
45 def markdown(text) 125 def markdown(text)
46 - @__renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::GitlabHTML.new({ filter_html: true, with_toc_data: true }), { 126 + @__renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::GitlabHTML.new(self, filter_html: true, with_toc_data: true), {
47 no_intra_emphasis: true, 127 no_intra_emphasis: true,
48 tables: true, 128 tables: true,
49 fenced_code_blocks: true, 129 fenced_code_blocks: true,
app/helpers/commits_helper.rb
1 module CommitsHelper 1 module CommitsHelper
2 - def commit_msg_with_link_to_issues(project, message)  
3 - return '' unless message  
4 - out = ''  
5 - message.split(/(#[0-9]+)/m).each do |m|  
6 - if m =~ /(#([0-9]+))/m  
7 - begin  
8 - issue = project.issues.find($2)  
9 - out += link_to($1, project_issue_path(project, $2))  
10 - rescue  
11 - out += $1  
12 - end  
13 - else  
14 - out += m  
15 - end  
16 - end  
17 - preserve out  
18 - end  
19 -  
20 def identification_type(line) 2 def identification_type(line)
21 if line[0] == "+" 3 if line[0] == "+"
22 "new" 4 "new"
app/mailers/notify.rb
@@ -16,59 +16,69 @@ class Notify &lt; ActionMailer::Base @@ -16,59 +16,69 @@ class Notify &lt; ActionMailer::Base
16 16
17 def new_issue_email(issue_id) 17 def new_issue_email(issue_id)
18 @issue = Issue.find(issue_id) 18 @issue = Issue.find(issue_id)
19 - mail(:to => @issue.assignee_email, :subject => "gitlab | New Issue was created") 19 + @project = @issue.project
  20 + mail(:to => @issue.assignee_email, :subject => "gitlab | new issue ##{@issue.id} | #{@issue.title} | #{@project.name}")
20 end 21 end
21 22
22 def note_wall_email(recipient_id, note_id) 23 def note_wall_email(recipient_id, note_id)
23 recipient = User.find(recipient_id) 24 recipient = User.find(recipient_id)
24 @note = Note.find(note_id) 25 @note = Note.find(note_id)
25 - mail(:to => recipient.email, :subject => "gitlab | #{@note.project_name} ") 26 + @project = @note.project
  27 + mail(:to => recipient.email, :subject => "gitlab | #{@project.name}")
26 end 28 end
27 29
28 def note_commit_email(recipient_id, note_id) 30 def note_commit_email(recipient_id, note_id)
29 recipient = User.find(recipient_id) 31 recipient = User.find(recipient_id)
30 @note = Note.find(note_id) 32 @note = Note.find(note_id)
31 @commit = @note.target 33 @commit = @note.target
32 - mail(:to => recipient.email, :subject => "gitlab | note for commit | #{@note.project_name} ") 34 + @commit = CommitDecorator.decorate(@commit)
  35 + @project = @note.project
  36 + mail(:to => recipient.email, :subject => "gitlab | note for commit #{@commit.short_id} | #{@commit.title} | #{@project.name}")
33 end 37 end
34 38
35 def note_merge_request_email(recipient_id, note_id) 39 def note_merge_request_email(recipient_id, note_id)
36 recipient = User.find(recipient_id) 40 recipient = User.find(recipient_id)
37 @note = Note.find(note_id) 41 @note = Note.find(note_id)
38 @merge_request = @note.noteable 42 @merge_request = @note.noteable
39 - mail(:to => recipient.email, :subject => "gitlab | note for merge request | #{@note.project_name} ") 43 + @project = @note.project
  44 + mail(:to => recipient.email, :subject => "gitlab | note for merge request !#{@merge_request.id} | #{@project.name}")
40 end 45 end
41 46
42 def note_issue_email(recipient_id, note_id) 47 def note_issue_email(recipient_id, note_id)
43 recipient = User.find(recipient_id) 48 recipient = User.find(recipient_id)
44 @note = Note.find(note_id) 49 @note = Note.find(note_id)
45 @issue = @note.noteable 50 @issue = @note.noteable
46 - mail(:to => recipient.email, :subject => "gitlab | note for issue #{@issue.id} | #{@note.project_name} ") 51 + @project = @note.project
  52 + mail(:to => recipient.email, :subject => "gitlab | note for issue ##{@issue.id} | #{@project.name}")
47 end 53 end
48 54
49 def note_wiki_email(recipient_id, note_id) 55 def note_wiki_email(recipient_id, note_id)
50 recipient = User.find(recipient_id) 56 recipient = User.find(recipient_id)
51 @note = Note.find(note_id) 57 @note = Note.find(note_id)
52 @wiki = @note.noteable 58 @wiki = @note.noteable
53 - mail(:to => recipient.email, :subject => "gitlab | note for wiki | #{@note.project_name}") 59 + @project = @note.project
  60 + mail(:to => recipient.email, :subject => "gitlab | note for wiki | #{@project.name}")
54 end 61 end
55 62
56 def new_merge_request_email(merge_request_id) 63 def new_merge_request_email(merge_request_id)
57 @merge_request = MergeRequest.find(merge_request_id) 64 @merge_request = MergeRequest.find(merge_request_id)
58 - mail(:to => @merge_request.assignee_email, :subject => "gitlab | new merge request | #{@merge_request.title} ") 65 + @project = @merge_request.project
  66 + mail(:to => @merge_request.assignee_email, :subject => "gitlab | new merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}")
59 end 67 end
60 68
61 def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) 69 def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
62 recipient = User.find(recipient_id) 70 recipient = User.find(recipient_id)
63 @merge_request = MergeRequest.find(merge_request_id) 71 @merge_request = MergeRequest.find(merge_request_id)
64 @previous_assignee ||= User.find(previous_assignee_id) 72 @previous_assignee ||= User.find(previous_assignee_id)
65 - mail(:to => recipient.email, :subject => "gitlab | merge request changed | #{@merge_request.title} ") 73 + @project = @merge_request.project
  74 + mail(:to => recipient.email, :subject => "gitlab | changed merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}")
66 end 75 end
67 76
68 def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) 77 def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
69 recipient = User.find(recipient_id) 78 recipient = User.find(recipient_id)
70 @issue = Issue.find(issue_id) 79 @issue = Issue.find(issue_id)
71 @previous_assignee ||= User.find(previous_assignee_id) 80 @previous_assignee ||= User.find(previous_assignee_id)
72 - mail(:to => recipient.email, :subject => "gitlab | changed issue | #{@issue.title} ") 81 + @project = @issue.project
  82 + mail(:to => recipient.email, :subject => "gitlab | changed issue ##{@issue.id} | #{@issue.title} | #{@project.name}")
73 end 83 end
74 end 84 end
app/views/commits/_commit.html.haml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 %strong.cgray= commit.author_name 7 %strong.cgray= commit.author_name
8 &ndash; 8 &ndash;
9 = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16 9 = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16
10 - = link_to truncate(commit.title, :length => 50), project_commit_path(@project, :id => commit.id), :class => "row_title" 10 + = link_to_gfm truncate(commit.title, :length => 50), project_commit_path(@project, :id => commit.id), :class => "row_title"
11 11
12 %span.committed_ago 12 %span.committed_ago
13 = time_ago_in_words(commit.committed_date) 13 = time_ago_in_words(commit.committed_date)
app/views/commits/_commit_box.html.haml
@@ -11,10 +11,10 @@ @@ -11,10 +11,10 @@
11 = link_to tree_project_ref_path(@project, @commit.id), :class => "browse-button primary grouped" do 11 = link_to tree_project_ref_path(@project, @commit.id), :class => "browse-button primary grouped" do
12 %strong Browse Code » 12 %strong Browse Code »
13 %h3.commit-title.page_title 13 %h3.commit-title.page_title
14 - = commit_msg_with_link_to_issues(@project, @commit.title) 14 + = gfm @commit.title
15 - if @commit.description.present? 15 - if @commit.description.present?
16 %pre.commit-description 16 %pre.commit-description
17 - = commit_msg_with_link_to_issues(@project, @commit.description) 17 + = gfm @commit.description
18 .commit-info 18 .commit-info
19 .row 19 .row
20 .span4 20 .span4
app/views/commits/index.atom.builder
@@ -17,7 +17,7 @@ xml.feed &quot;xmlns&quot; =&gt; &quot;http://www.w3.org/2005/Atom&quot;, &quot;xmlns:media&quot; =&gt; &quot;http://sear @@ -17,7 +17,7 @@ xml.feed &quot;xmlns&quot; =&gt; &quot;http://www.w3.org/2005/Atom&quot;, &quot;xmlns:media&quot; =&gt; &quot;http://sear
17 xml.name commit.author_name 17 xml.name commit.author_name
18 xml.email commit.author_email 18 xml.email commit.author_email
19 end 19 end
20 - xml.summary commit.description 20 + xml.summary gfm(commit.description)
21 end 21 end
22 end 22 end
23 end 23 end
app/views/dashboard/issues.html.haml
@@ -8,8 +8,8 @@ @@ -8,8 +8,8 @@
8 - if @issues.any? 8 - if @issues.any?
9 - @issues.group_by(&:project).each do |group| 9 - @issues.group_by(&:project).each do |group|
10 %div.ui-box 10 %div.ui-box
11 - - project = group[0]  
12 - %h5= project.name 11 + - @project = group[0]
  12 + %h5= @project.name
13 %ul.unstyled.issues_table 13 %ul.unstyled.issues_table
14 - group[1].each do |issue| 14 - group[1].each do |issue|
15 = render(:partial => 'issues/show', :locals => {:issue => issue}) 15 = render(:partial => 'issues/show', :locals => {:issue => issue})
app/views/dashboard/merge_requests.html.haml
@@ -7,8 +7,8 @@ @@ -7,8 +7,8 @@
7 - if @merge_requests.any? 7 - if @merge_requests.any?
8 - @merge_requests.group_by(&:project).each do |group| 8 - @merge_requests.group_by(&:project).each do |group|
9 %ul.unstyled.ui-box 9 %ul.unstyled.ui-box
10 - - project = group[0]  
11 - %h5= project.name 10 + - @project = group[0]
  11 + %h5= @project.name
12 - group[1].each do |merge_request| 12 - group[1].each do |merge_request|
13 = render(:partial => 'merge_requests/merge_request', :locals => {:merge_request => merge_request}) 13 = render(:partial => 'merge_requests/merge_request', :locals => {:merge_request => merge_request})
14 %hr 14 %hr
app/views/events/_commit.html.haml
@@ -5,5 +5,5 @@ @@ -5,5 +5,5 @@
5 %strong.cdark= commit.author_name 5 %strong.cdark= commit.author_name
6 &ndash; 6 &ndash;
7 = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16 7 = image_tag gravatar_icon(commit.author_email), :class => "avatar", :width => 16
8 - = truncate(commit.title, :length => 50) rescue "--broken encoding" 8 + = gfm truncate(commit.title, :length => 50), project_commit_path(project, :id => commit.id) rescue "--broken encoding"
9 9
app/views/issues/_show.html.haml
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 - else 25 - else
26 = image_tag "no_avatar.png", :class => "avatar" 26 = image_tag "no_avatar.png", :class => "avatar"
27 27
28 - %p= link_to truncate(issue.title, :length => 100), project_issue_path(issue.project, issue), :class => "row_title" 28 + %p= link_to_gfm truncate(issue.title, :length => 100), project_issue_path(issue.project, issue), :class => "row_title"
29 29
30 %span.update-author 30 %span.update-author
31 %small.cdark= "##{issue.id}" 31 %small.cdark= "##{issue.id}"
app/views/issues/show.html.haml
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 .alert-message.error.status_info Closed 31 .alert-message.error.status_info Closed
32 - else 32 - else
33 .alert-message.success.status_info Open 33 .alert-message.success.status_info Open
34 - = @issue.title 34 + = gfm @issue.title
35 35
36 .middle_box_content 36 .middle_box_content
37 %cite.cgray Created by 37 %cite.cgray Created by
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
46 - if @issue.milestone 46 - if @issue.milestone
47 - milestone = @issue.milestone 47 - milestone = @issue.milestone
48 %cite.cgray and attached to milestone 48 %cite.cgray and attached to milestone
49 - %strong= link_to truncate(milestone.title, :length => 20), project_milestone_path(milestone.project, milestone) 49 + %strong= link_to_gfm truncate(milestone.title, :length => 20), project_milestone_path(milestone.project, milestone)
50 50
51 .right 51 .right
52 - @issue.labels.each do |label| 52 - @issue.labels.each do |label|
app/views/merge_requests/_merge_request.html.haml
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 = merge_request.target_branch 16 = merge_request.target_branch
17 = image_tag gravatar_icon(merge_request.author_email), :class => "avatar" 17 = image_tag gravatar_icon(merge_request.author_email), :class => "avatar"
18 18
19 - %p= link_to truncate(merge_request.title, :length => 80), project_merge_request_path(merge_request.project, merge_request), :class => "row_title" 19 + %p= link_to_gfm truncate(merge_request.title, :length => 80), project_merge_request_path(merge_request.project, merge_request), :class => "row_title"
20 20
21 %span.update-author 21 %span.update-author
22 %small.cdark= "##{merge_request.id}" 22 %small.cdark= "##{merge_request.id}"
app/views/merge_requests/show/_mr_box.html.haml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 .alert-message.error.status_info Closed 5 .alert-message.error.status_info Closed
6 - else 6 - else
7 .alert-message.success.status_info Open 7 .alert-message.success.status_info Open
8 - = @merge_request.title 8 + = gfm @merge_request.title
9 9
10 .middle_box_content 10 .middle_box_content
11 %div 11 %div
app/views/milestones/_milestone.html.haml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 - if can? current_user, :admin_milestone, milestone.project 7 - if can? current_user, :admin_milestone, milestone.project
8 = link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), :class => "btn small edit-milestone-link grouped" 8 = link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), :class => "btn small edit-milestone-link grouped"
9 %h4 9 %h4
10 - = link_to truncate(milestone.title, :length => 100), project_milestone_path(milestone.project, milestone), :class => "row_title" 10 + = link_to_gfm truncate(milestone.title, :length => 100), project_milestone_path(milestone.project, milestone), :class => "row_title"
11 %small 11 %small
12 = milestone.expires_at 12 = milestone.expires_at
13 %br 13 %br
app/views/milestones/show.html.haml
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 .alert-message.error.status_info Closed 21 .alert-message.error.status_info Closed
22 - else 22 - else
23 .alert-message.success.status_info Open 23 .alert-message.success.status_info Open
24 - = @milestone.title 24 + = gfm @milestone.title
25 %small.right= @milestone.expires_at 25 %small.right= @milestone.expires_at
26 26
27 .middle_box_content 27 .middle_box_content
@@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
51 = link_to [@project, issue] do 51 = link_to [@project, issue] do
52 %span.badge.badge-info ##{issue.id} 52 %span.badge.badge-info ##{issue.id}
53 &ndash; 53 &ndash;
54 - = link_to truncate(issue.title, :length => 60), [@project, issue] 54 + = link_to_gfm truncate(issue.title, :length => 60), [@project, issue]
55 %br 55 %br
56 = paginate @issues, :theme => "gitlab" 56 = paginate @issues, :theme => "gitlab"
57 57
app/views/notify/new_issue_email.html.haml
@@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
10 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 10 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
11 %td{:align => "left", :style => "padding: 20px 0 0;"} 11 %td{:align => "left", :style => "padding: 20px 0 0;"}
12 %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 12 %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
13 - = link_to project_issue_url(@issue.project, @issue), :title => @issue.title do  
14 - = "Issue ##{@issue.id.to_s}"  
15 - = truncate(@issue.title, :length => 45) 13 + = "Issue ##{@issue.id}"
  14 + = link_to_gfm truncate(@issue.title, :length => 45), project_issue_url(@issue.project, @issue), :title => @issue.title
16 %br 15 %br
app/views/notify/new_merge_request_email.html.haml
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - New Merge Request  
8 - = link_to truncate(@merge_request.title, :length => 16), project_merge_request_url(@merge_request.project, @merge_request) 7 + = "New Merge Request !#{@merge_request.id}"
  8 + = link_to_gfm truncate(@merge_request.title, :length => 16), project_merge_request_url(@merge_request.project, @merge_request)
9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
10 %tr 10 %tr
11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/notify/note_commit_email.html.haml
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - New comment for commit  
8 - = link_to truncate(@commit.id.to_s, :length => 16), project_commit_url(@note.project, :id => @commit.id, :anchor => "note_#{@note.id}") 7 + = "New comment for Commit #{@commit.short_id}"
  8 + = link_to_gfm truncate(@commit.title, :length => 16), project_commit_url(@note.project, :id => @commit.id, :anchor => "note_#{@note.id}")
9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
10 %tr 10 %tr
11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/notify/note_issue_email.html.haml
@@ -4,10 +4,8 @@ @@ -4,10 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - New comment -  
8 - = link_to project_issue_url(@issue.project, @issue, :anchor => "note_#{@note.id}") do  
9 - = "Issue ##{@issue.id.to_s}"  
10 - = truncate(@issue.title, :length => 35) 7 + = "New comment for Issue ##{@issue.id}"
  8 + = link_to_gfm truncate(@issue.title, :length => 35), project_issue_url(@issue.project, @issue, :anchor => "note_#{@note.id}")
11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
12 %tr 10 %tr
13 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/notify/note_merge_request_email.html.haml
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - New comment for Merge Request  
8 - = link_to truncate(@merge_request.title, :length => 16), project_merge_request_url(@merge_request.project, @merge_request, :anchor => "note_#{@note.id}") 7 + = "New comment for Merge Request !#{@merge_request.id}"
  8 + = link_to_gfm truncate(@merge_request.title, :length => 16), project_merge_request_url(@merge_request.project, @merge_request, :anchor => "note_#{@note.id}")
9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
10 %tr 10 %tr
11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/notify/note_wiki_email.html.haml
@@ -4,9 +4,8 @@ @@ -4,9 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - New comment -  
8 - = link_to project_issue_url(@wiki.project, @wiki, :anchor => "note_#{@note.id}") do  
9 - = "Wiki ##{@wiki.title.to_s}" 7 + New comment for Wiki page
  8 + = link_to_gfm @wiki.title, project_issue_url(@wiki.project, @wiki, :anchor => "note_#{@note.id}")
10 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
11 %tr 10 %tr
12 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/notify/reassigned_issue_email.html.haml
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - Reassigned Issue  
8 - = link_to truncate(@issue.title, :length => 16), project_issue_url(@issue.project, @issue) 7 + = "Reassigned Issue ##{@issue.id}"
  8 + = link_to_gfm truncate(@issue.title, :length => 16), project_issue_url(@issue.project, @issue)
9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
10 %tr 10 %tr
11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/notify/reassigned_merge_request_email.html.haml
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 4 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
5 %td{:align => "left", :style => "padding: 20px 0 0;"} 5 %td{:align => "left", :style => "padding: 20px 0 0;"}
6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} 6 %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7 - Reassigned Merge Request  
8 - = link_to truncate(@merge_request.title, :length => 16), project_merge_request_url(@merge_request.project, @merge_request) 7 + = "Reassigned Merge Request !#{@merge_request.id}"
  8 + = link_to_gfm truncate(@merge_request.title, :length => 16), project_merge_request_url(@merge_request.project, @merge_request)
9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 9 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
10 %tr 10 %tr
11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} 11 %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"}
app/views/refs/_tree_commit.html.haml
1 - if tm 1 - if tm
2 %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) 2 %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
3 -= link_to truncate(content_commit.title, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link" 3 += link_to_gfm truncate(content_commit.title, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
app/views/refs/blame.html.haml
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 %td.blame_commit 33 %td.blame_commit
34 &nbsp; 34 &nbsp;
35 %code= link_to commit.short_id, project_commit_path(@project, :id => commit.id) 35 %code= link_to commit.short_id, project_commit_path(@project, :id => commit.id)
36 - = link_to truncate(commit.title, :length => 30), project_commit_path(@project, :id => commit.id), :class => "row_title" rescue "--broken encoding" 36 + = link_to_gfm truncate(commit.title, :length => 30), project_commit_path(@project, :id => commit.id), :class => "row_title" rescue "--broken encoding"
37 %td.lines 37 %td.lines
38 = preserve do 38 = preserve do
39 %pre 39 %pre
app/views/repositories/_branch.html.haml
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 %code= commit.short_id 11 %code= commit.short_id
12 12
13 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16 13 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16
14 - = truncate(commit.title, :length => 40) 14 + = gfm truncate(commit.title, :length => 40)
15 %td 15 %td
16 %span.update-author.right 16 %span.update-author.right
17 = time_ago_in_words(commit.committed_date) 17 = time_ago_in_words(commit.committed_date)
app/views/repositories/_feed.html.haml
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 = link_to project_commits_path(@project, commit.id) do 13 = link_to project_commits_path(@project, commit.id) do
14 %code= commit.short_id 14 %code= commit.short_id
15 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16 15 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16
16 - = truncate(commit.title, :length => 40) 16 + = gfm truncate(commit.title, :length => 40)
17 %td 17 %td
18 %span.right.cgray 18 %span.right.cgray
19 = time_ago_in_words(commit.committed_date) 19 = time_ago_in_words(commit.committed_date)
app/views/repositories/tags.html.haml
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 = link_to project_commit_path(@project, commit.id) do 17 = link_to project_commit_path(@project, commit.id) do
18 %code= commit.short_id 18 %code= commit.short_id
19 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16 19 = image_tag gravatar_icon(commit.author_email), :class => "", :width => 16
20 - = truncate(commit.title, :length => 40) 20 + = gfm truncate(commit.title, :length => 40)
21 %td 21 %td
22 %span.update-author.right 22 %span.update-author.right
23 = time_ago_in_words(commit.committed_date) 23 = time_ago_in_words(commit.committed_date)
lib/redcarpet/render/gitlab_html.rb
1 class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML 1 class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
  2 +
  3 + attr_reader :template
  4 + alias_method :h, :template
  5 +
  6 + def initialize(template, options = {})
  7 + @template = template
  8 + @project = @template.instance_variable_get("@project")
  9 + super options
  10 + end
  11 +
2 def block_code(code, language) 12 def block_code(code, language)
3 if Pygments::Lexer.find(language) 13 if Pygments::Lexer.find(language)
4 Pygments.highlight(code, :lexer => language, :options => {:encoding => 'utf-8'}) 14 Pygments.highlight(code, :lexer => language, :options => {:encoding => 'utf-8'})
@@ -6,4 +16,8 @@ class Redcarpet::Render::GitlabHTML &lt; Redcarpet::Render::HTML @@ -6,4 +16,8 @@ class Redcarpet::Render::GitlabHTML &lt; Redcarpet::Render::HTML
6 Pygments.highlight(code, :options => {:encoding => 'utf-8'}) 16 Pygments.highlight(code, :options => {:encoding => 'utf-8'})
7 end 17 end
8 end 18 end
  19 +
  20 + def postprocess(full_document)
  21 + h.gfm(full_document)
  22 + end
9 end 23 end
spec/helpers/commit_helper_spec.rb
@@ -1,67 +0,0 @@ @@ -1,67 +0,0 @@
1 -require "spec_helper"  
2 -include Haml::Helpers  
3 -  
4 -describe CommitsHelper do  
5 -  
6 - before do  
7 - @project = Factory :project  
8 - @other_project = Factory :project, :path => "OtherPath", :code => "OtherCode"  
9 - @fake_user = Factory :user  
10 - @valid_issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @project  
11 - @invalid_issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @other_project  
12 - end  
13 -  
14 - it "should provides return message untouched if no issue number present" do  
15 - message = "Dummy message without issue number"  
16 -  
17 - commit_msg_with_link_to_issues(@project, message).should eql message  
18 - end  
19 -  
20 - it "should returns message handled by preserve" do  
21 - message = "My brand new  
22 - Commit on multiple  
23 - lines !"  
24 -  
25 - #\n are converted to &#x000A as specified in preserve_rspec  
26 - expected = "My brand new&#x000A; Commit on multiple&#x000A; lines !"  
27 -  
28 - commit_msg_with_link_to_issues(@project, message).should eql expected  
29 - end  
30 -  
31 - it "should returns empty string if message undefined" do  
32 - commit_msg_with_link_to_issues(@project, nil).should eql ''  
33 - end  
34 -  
35 - it "should returns link_to issue for one valid issue in message" do  
36 - issue_id = @valid_issue.id  
37 - message = "One commit message ##{issue_id}"  
38 - expected = "One commit message <a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a>"  
39 -  
40 - commit_msg_with_link_to_issues(@project, message).should eql expected  
41 - end  
42 -  
43 - it "should returns message untouched for one invalid issue in message" do  
44 - issue_id = @invalid_issue.id  
45 - message = "One commit message ##{issue_id}"  
46 -  
47 - commit_msg_with_link_to_issues(@project, message).should eql message  
48 - end  
49 -  
50 - it "should handle multiple issue references in commit message" do  
51 - issue_id = @valid_issue.id  
52 - invalid_issue_id = @invalid_issue.id  
53 -  
54 - message = "One big commit message with a valid issue ##{issue_id} and an invalid one ##{invalid_issue_id}.  
55 - We reference valid ##{issue_id} multiple times (##{issue_id}) as the invalid ##{invalid_issue_id} is also  
56 - referenced another time (##{invalid_issue_id})"  
57 -  
58 - expected = "One big commit message with a valid issue <a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a>"+  
59 - " and an invalid one ##{invalid_issue_id}.&#x000A; "+  
60 - "We reference valid <a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a> multiple times "+  
61 - "(<a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a>) "+  
62 - "as the invalid ##{invalid_issue_id} is also&#x000A; referenced another time (##{invalid_issue_id})"  
63 -  
64 - commit_msg_with_link_to_issues(@project, message).should eql expected  
65 - end  
66 -  
67 -end  
68 \ No newline at end of file 0 \ No newline at end of file
spec/helpers/gitlab_flavored_markdown_spec.rb 0 → 100644
@@ -0,0 +1,232 @@ @@ -0,0 +1,232 @@
  1 +require "spec_helper"
  2 +
  3 +describe ApplicationHelper do
  4 + before do
  5 + @project = Project.find_by_path("gitlabhq") || Factory(:project)
  6 + @commit = @project.repo.commits.first.parents.first
  7 + @commit = CommitDecorator.decorate(Commit.new(@commit))
  8 + @other_project = Factory :project, :path => "OtherPath", :code => "OtherCode"
  9 + @fake_user = Factory :user, :name => "fred"
  10 + end
  11 +
  12 + describe "#gfm" do
  13 + it "should raiase an error if @project is not set" do
  14 + @project = nil
  15 +
  16 + expect { gfm("foo") }.to raise_error
  17 + end
  18 +
  19 + describe "referencing a commit" do
  20 + it "should link using a full id" do
  21 + gfm("Reverts changes from #{@commit.id}").should == "Reverts changes from #{link_to @commit.id, project_commit_path(@project, :id => @commit.id), :title => "Commit: #{@commit.author_name} - #{@commit.title}", :class => "gfm gfm-commit "}"
  22 + end
  23 +
  24 + it "should link using a short id" do
  25 + gfm("Backported from #{@commit.id[0, 6]}").should == "Backported from #{link_to @commit.id[0, 6], project_commit_path(@project, :id => @commit.id), :title => "Commit: #{@commit.author_name} - #{@commit.title}", :class => "gfm gfm-commit "}"
  26 + end
  27 +
  28 + it "should link with adjecent text" do
  29 + gfm("Reverted (see #{@commit.id})").should == "Reverted (see #{link_to @commit.id, project_commit_path(@project, :id => @commit.id), :title => "Commit: #{@commit.author_name} - #{@commit.title}", :class => "gfm gfm-commit "})"
  30 + end
  31 +
  32 + it "should not link with an invalid id" do
  33 + gfm("What happened in 12345678?").should == "What happened in 12345678?"
  34 + end
  35 + end
  36 +
  37 + describe "referencing a team member" do
  38 + it "should link using a simple name" do
  39 + user = Factory :user, name: "barry"
  40 + @project.users << user
  41 + member = @project.users_projects.where(:user_id => user).first
  42 +
  43 + gfm("@#{user.name} you are right").should == "#{link_to "@#{user.name}", project_team_member_path(@project, member), :class => "gfm gfm-team_member "} you are right"
  44 + end
  45 +
  46 + it "should link using a name with dots" do
  47 + user = Factory :user, name: "alphA.Beta"
  48 + @project.users << user
  49 + member = @project.users_projects.where(:user_id => user).first
  50 +
  51 + gfm("@#{user.name} you are right").should == "#{link_to "@#{user.name}", project_team_member_path(@project, member), :class => "gfm gfm-team_member "} you are right"
  52 + end
  53 +
  54 + it "should link using name with underscores" do
  55 + user = Factory :user, name: "ping_pong_king"
  56 + @project.users << user
  57 + member = @project.users_projects.where(:user_id => user).first
  58 +
  59 + gfm("@#{user.name} you are right").should == "#{link_to "@#{user.name}", project_team_member_path(@project, member), :class => "gfm gfm-team_member "} you are right"
  60 + end
  61 +
  62 + it "should link with adjecent text" do
  63 + user = Factory.create(:user, :name => "ace")
  64 + @project.users << user
  65 + member = @project.users_projects.where(:user_id => user).first
  66 +
  67 + gfm("Mail the Admin (@#{user.name})").should == "Mail the Admin (#{link_to "@#{user.name}", project_team_member_path(@project, member), :class => "gfm gfm-team_member "})"
  68 + end
  69 +
  70 + it "should add styles" do
  71 + user = Factory :user, name: "barry"
  72 + @project.users << user
  73 + gfm("@#{user.name} you are right").should have_selector(".gfm.gfm-team_member")
  74 + end
  75 +
  76 + it "should not link using a bogus name" do
  77 + gfm("What hapened to @foo?").should == "What hapened to @foo?"
  78 + end
  79 + end
  80 +
  81 + describe "referencing an issue" do
  82 + before do
  83 + @issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @project
  84 + @invalid_issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @other_project
  85 + end
  86 +
  87 + it "should link using a correct id" do
  88 + gfm("Fixes ##{@issue.id}").should == "Fixes #{link_to "##{@issue.id}", project_issue_path(@project, @issue), :title => "Issue: #{@issue.title}", :class => "gfm gfm-issue "}"
  89 + end
  90 +
  91 + it "should link with adjecent text" do
  92 + gfm("This has already been discussed (see ##{@issue.id})").should == "This has already been discussed (see #{link_to "##{@issue.id}", project_issue_path(@project, @issue), :title => "Issue: #{@issue.title}", :class => "gfm gfm-issue "})"
  93 + end
  94 +
  95 + it "should add styles" do
  96 + gfm("Fixes ##{@issue.id}").should have_selector(".gfm.gfm-issue")
  97 + end
  98 +
  99 + it "should not link using an invalid id" do
  100 + gfm("##{@invalid_issue.id} has been marked duplicate of this").should == "##{@invalid_issue.id} has been marked duplicate of this"
  101 + end
  102 + end
  103 +
  104 + describe "referencing a merge request" do
  105 + before do
  106 + @merge_request = Factory :merge_request, :assignee => @fake_user, :author => @fake_user, :project => @project
  107 + @invalid_merge_request = Factory :merge_request, :assignee => @fake_user, :author => @fake_user, :project => @other_project
  108 + end
  109 +
  110 + it "should link using a correct id" do
  111 + gfm("Fixed in !#{@merge_request.id}").should == "Fixed in #{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), :title => "Merge Request: #{@merge_request.title}", :class => "gfm gfm-merge_request "}"
  112 + end
  113 +
  114 + it "should link with adjecent text" do
  115 + gfm("This has been fixed already (see !#{@merge_request.id})").should == "This has been fixed already (see #{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), :title => "Merge Request: #{@merge_request.title}", :class => "gfm gfm-merge_request "})"
  116 + end
  117 +
  118 + it "should add styles" do
  119 + gfm("Fixed in !#{@merge_request.id}").should have_selector(".gfm.gfm-merge_request")
  120 + end
  121 +
  122 + it "should not link using an invalid id" do
  123 + gfm("!#{@invalid_merge_request.id} violates our coding guidelines")
  124 + end
  125 + end
  126 +
  127 + describe "referencing a snippet" do
  128 + before do
  129 + @snippet = Factory.create(:snippet,
  130 + :title => "Render asset to string",
  131 + :author => @fake_user,
  132 + :project => @project)
  133 + end
  134 +
  135 + it "should link using a correct id" do
  136 + gfm("Check out $#{@snippet.id}").should == "Check out #{link_to "$#{@snippet.id}", project_snippet_path(@project, @snippet), :title => "Snippet: #{@snippet.title}", :class => "gfm gfm-snippet "}"
  137 + end
  138 +
  139 + it "should link with adjecent text" do
  140 + gfm("I have created a snippet for that ($#{@snippet.id})").should == "I have created a snippet for that (#{link_to "$#{@snippet.id}", project_snippet_path(@project, @snippet), :title => "Snippet: #{@snippet.title}", :class => "gfm gfm-snippet "})"
  141 + end
  142 +
  143 + it "should add styles" do
  144 + gfm("Check out $#{@snippet.id}").should have_selector(".gfm.gfm-snippet")
  145 + end
  146 +
  147 + it "should not link using an invalid id" do
  148 + gfm("Don't use $1234").should == "Don't use $1234"
  149 + end
  150 + end
  151 +
  152 + it "should link to multiple things" do
  153 + user = Factory :user, name: "barry"
  154 + @project.users << user
  155 + member = @project.users_projects.where(:user_id => user).first
  156 +
  157 + gfm("Let @#{user.name} fix the *mess* in #{@commit.id}").should == "Let #{link_to "@#{user.name}", project_team_member_path(@project, member), :class => "gfm gfm-team_member "} fix the *mess* in #{link_to @commit.id, project_commit_path(@project, :id => @commit.id), :title => "Commit: #{@commit.author_name} - #{@commit.title}", :class => "gfm gfm-commit "}"
  158 + end
  159 +
  160 + it "should not trip over other stuff", :focus => true do
  161 + gfm("_Please_ *stop* 'helping' and all the other b*$#%' you do.").should == "_Please_ *stop* 'helping' and all the other b*$#%' you do."
  162 + end
  163 +
  164 + it "should not touch HTML entities" do
  165 + gfm("We&#39;ll accept good pull requests.").should == "We&#39;ll accept good pull requests."
  166 + end
  167 +
  168 + it "should forward HTML options to links" do
  169 + gfm("fixed in #{@commit.id}", :class => "foo").should have_selector("a.foo")
  170 + end
  171 + end
  172 +
  173 + describe "#link_to_gfm" do
  174 + let(:issue1) { Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @project }
  175 + let(:issue2) { Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @project }
  176 +
  177 + it "should handle references nested in links with all the text" do
  178 + link_to_gfm("This should finally fix ##{issue1.id} and ##{issue2.id} for real", project_commit_path(@project, :id => @commit.id)).should == "#{link_to "This should finally fix ", project_commit_path(@project, :id => @commit.id)}#{link_to "##{issue1.id}", project_issue_path(@project, issue1), :title => "Issue: #{issue1.title}", :class => "gfm gfm-issue "}#{link_to " and ", project_commit_path(@project, :id => @commit.id)}#{link_to "##{issue2.id}", project_issue_path(@project, issue2), :title => "Issue: #{issue2.title}", :class => "gfm gfm-issue "}#{link_to " for real", project_commit_path(@project, :id => @commit.id)}"
  179 + end
  180 +
  181 + it "should forward HTML options" do
  182 + link_to_gfm("This should finally fix ##{issue1.id} for real", project_commit_path(@project, :id => @commit.id), :class => "foo").should have_selector(".foo")
  183 + end
  184 + end
  185 +
  186 + describe "#markdown" do
  187 + before do
  188 + @issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @project
  189 + @merge_request = Factory :merge_request, :assignee => @fake_user, :author => @fake_user, :project => @project
  190 + @note = Factory.create(:note,
  191 + :note => "Screenshot of the new feature",
  192 + :project => @project,
  193 + :noteable_id => @commit.id,
  194 + :noteable_type => "Commit",
  195 + :attachment => "screenshot123.jpg")
  196 + @snippet = Factory.create(:snippet,
  197 + :title => "Render asset to string",
  198 + :author => @fake_user,
  199 + :project => @project)
  200 +
  201 + @other_user = Factory :user, name: "bill"
  202 + @project.users << @other_user
  203 + @member = @project.users_projects.where(:user_id => @other_user).first
  204 + end
  205 +
  206 + it "should handle references in paragraphs" do
  207 + markdown("\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. #{@commit.id} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.\n").should == "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. #{link_to @commit.id, project_commit_path(@project, :id => @commit.id), :title => "Commit: #{@commit.author_name} - #{@commit.title}", :class => "gfm gfm-commit "} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.</p>\n"
  208 + end
  209 +
  210 + it "should handle references in headers" do
  211 + markdown("\n# Working around ##{@issue.id} for now\n## Apply !#{@merge_request.id}").should == "<h1 id=\"toc_0\">Working around #{link_to "##{@issue.id}", project_issue_path(@project, @issue), :title => "Issue: #{@issue.title}", :class => "gfm gfm-issue "} for now</h1>\n\n<h2 id=\"toc_1\">Apply #{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), :title => "Merge Request: #{@merge_request.title}", :class => "gfm gfm-merge_request "}</h2>\n"
  212 + end
  213 +
  214 + it "should handle references in lists" do
  215 + markdown("\n* dark: ##{@issue.id}\n* light by @#{@other_user.name}\n").should == "<ul>\n<li>dark: #{link_to "##{@issue.id}", project_issue_path(@project, @issue), :title => "Issue: #{@issue.title}", :class => "gfm gfm-issue "}</li>\n<li>light by #{link_to "@#{@other_user.name}", project_team_member_path(@project, @member), :class => "gfm gfm-team_member "}</li>\n</ul>\n"
  216 + end
  217 +
  218 + it "should handle references in <em>" do
  219 + markdown("Apply _!#{@merge_request.id}_ ASAP").should == "<p>Apply <em>#{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), :title => "Merge Request: #{@merge_request.title}", :class => "gfm gfm-merge_request "}</em> ASAP</p>\n"
  220 + end
  221 +
  222 + it "should leave code blocks untouched" do
  223 + markdown("\n some code from $#{@snippet.id}\n here too\n").should == "<div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{@snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre>\n</div>\n"
  224 +
  225 + markdown("\n```\nsome code from $#{@snippet.id}\nhere too\n```\n").should == "<div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{@snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre>\n</div>\n"
  226 + end
  227 +
  228 + it "should leave inline code untouched" do
  229 + markdown("\nDon't use `$#{@snippet.id}` here.\n").should == "<p>Don&#39;t use <code>$#{@snippet.id}</code> here.</p>\n"
  230 + end
  231 + end
  232 +end
spec/mailers/notify_spec.rb
@@ -60,7 +60,7 @@ describe Notify do @@ -60,7 +60,7 @@ describe Notify do
60 it_behaves_like 'an assignee email' 60 it_behaves_like 'an assignee email'
61 61
62 it 'has the correct subject' do 62 it 'has the correct subject' do
63 - should have_subject /New Issue was created/ 63 + should have_subject /new issue ##{issue.id}/
64 end 64 end
65 65
66 it 'contains a link to the new issue' do 66 it 'contains a link to the new issue' do
@@ -102,7 +102,7 @@ describe Notify do @@ -102,7 +102,7 @@ describe Notify do
102 it_behaves_like 'an assignee email' 102 it_behaves_like 'an assignee email'
103 103
104 it 'has the correct subject' do 104 it 'has the correct subject' do
105 - should have_subject /new merge request/ 105 + should have_subject /new merge request !#{merge_request.id}/
106 end 106 end
107 107
108 it 'contains a link to the new merge request' do 108 it 'contains a link to the new merge request' do
@@ -126,7 +126,7 @@ describe Notify do @@ -126,7 +126,7 @@ describe Notify do
126 it_behaves_like 'a multiple recipients email' 126 it_behaves_like 'a multiple recipients email'
127 127
128 it 'has the correct subject' do 128 it 'has the correct subject' do
129 - should have_subject /merge request changed/ 129 + should have_subject /changed merge request !#{merge_request.id}/
130 end 130 end
131 131
132 it 'contains the name of the previous assignee' do 132 it 'contains the name of the previous assignee' do
@@ -188,6 +188,8 @@ describe Notify do @@ -188,6 +188,8 @@ describe Notify do
188 mock(:commit).tap do |commit| 188 mock(:commit).tap do |commit|
189 commit.stub(:id).and_return('fauxsha1') 189 commit.stub(:id).and_return('fauxsha1')
190 commit.stub(:project).and_return(project) 190 commit.stub(:project).and_return(project)
  191 + commit.stub(:short_id).and_return('fauxsha1')
  192 + commit.stub(:safe_message).and_return('some message')
191 end 193 end
192 end 194 end
193 before(:each) { note.stub(:target).and_return(commit) } 195 before(:each) { note.stub(:target).and_return(commit) }
@@ -197,7 +199,7 @@ describe Notify do @@ -197,7 +199,7 @@ describe Notify do
197 it_behaves_like 'a note email' 199 it_behaves_like 'a note email'
198 200
199 it 'has the correct subject' do 201 it 'has the correct subject' do
200 - should have_subject /note for commit/ 202 + should have_subject /note for commit #{commit.short_id}/
201 end 203 end
202 204
203 it 'contains a link to the commit' do 205 it 'contains a link to the commit' do
@@ -215,7 +217,7 @@ describe Notify do @@ -215,7 +217,7 @@ describe Notify do
215 it_behaves_like 'a note email' 217 it_behaves_like 'a note email'
216 218
217 it 'has the correct subject' do 219 it 'has the correct subject' do
218 - should have_subject /note for merge request/ 220 + should have_subject /note for merge request !#{merge_request.id}/
219 end 221 end
220 222
221 it 'contains a link to the merge request note' do 223 it 'contains a link to the merge request note' do
@@ -233,7 +235,7 @@ describe Notify do @@ -233,7 +235,7 @@ describe Notify do
233 it_behaves_like 'a note email' 235 it_behaves_like 'a note email'
234 236
235 it 'has the correct subject' do 237 it 'has the correct subject' do
236 - should have_subject /note for issue #{issue.id}/ 238 + should have_subject /note for issue ##{issue.id}/
237 end 239 end
238 240
239 it 'contains a link to the issue note' do 241 it 'contains a link to the issue note' do
spec/requests/gitlab_flavored_markdown_spec.rb 0 → 100644
@@ -0,0 +1,241 @@ @@ -0,0 +1,241 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Gitlab Flavored Markdown" do
  4 + let(:project) { Factory :project }
  5 + let(:issue) { Factory :issue, :project => project }
  6 + let(:merge_request) { Factory :merge_request, :project => project }
  7 + let(:fred) do
  8 + u = Factory :user, :name => "fred"
  9 + project.users << u
  10 + u
  11 + end
  12 +
  13 + before do
  14 + # add test branch
  15 + @branch_name = "gfm-test"
  16 + r = project.repo
  17 + i = r.index
  18 + # add test file
  19 + @test_file = "gfm_test_file"
  20 + i.add(@test_file, "foo\nbar\n")
  21 + # add commit with gfm
  22 + i.commit("fix ##{issue.id}\n\nask @#{fred.name} for details", :head => @branch_name)
  23 +
  24 + # add test tag
  25 + @tag_name = "gfm-test-tag"
  26 + r.git.native(:tag, {}, @tag_name, commit.id)
  27 + end
  28 + after do
  29 + # delete test branch and tag
  30 + project.repo.git.native(:branch, {:D => true}, @branch_name)
  31 + project.repo.git.native(:tag, {:d => true}, @tag_name)
  32 + project.repo.gc_auto
  33 + end
  34 +
  35 + let(:commit) { project.commits(@branch_name).first }
  36 +
  37 + before do
  38 + login_as :user
  39 + project.add_access(@user, :read, :write)
  40 + end
  41 +
  42 +
  43 + describe "for commits" do
  44 + it "should render title in commits#index" do
  45 + visit project_commits_path(project, :ref => @branch_name)
  46 +
  47 + page.should have_link("##{issue.id}")
  48 + end
  49 +
  50 + it "should render title in commits#show" do
  51 + visit project_commit_path(project, :id => commit.id)
  52 +
  53 + page.should have_link("##{issue.id}")
  54 + end
  55 +
  56 + it "should render description in commits#show" do
  57 + visit project_commit_path(project, :id => commit.id)
  58 +
  59 + page.should have_link("@#{fred.name}")
  60 + end
  61 +
  62 + it "should render title in refs#tree", :js => true do
  63 + visit tree_project_ref_path(project, :id => @branch_name)
  64 +
  65 + within(".tree_commit") do
  66 + page.should have_link("##{issue.id}")
  67 + end
  68 + end
  69 +
  70 + it "should render title in refs#blame" do
  71 + visit blame_file_project_ref_path(project, :id => @branch_name, :path => @test_file)
  72 +
  73 + within(".blame_commit") do
  74 + page.should have_link("##{issue.id}")
  75 + end
  76 + end
  77 +
  78 + it "should render title in repositories#branches" do
  79 + visit branches_project_repository_path(project)
  80 +
  81 + page.should have_link("##{issue.id}")
  82 + end
  83 +
  84 + it "should render title in repositories#tags" do
  85 + visit tags_project_repository_path(project)
  86 +
  87 + page.should have_link("##{issue.id}")
  88 + end
  89 + end
  90 +
  91 +
  92 + describe "for issues" do
  93 + before do
  94 + @other_issue = Factory :issue,
  95 + :author => @user,
  96 + :assignee => @user,
  97 + :project => project
  98 + @issue = Factory :issue,
  99 + :author => @user,
  100 + :assignee => @user,
  101 + :project => project,
  102 + :title => "fix ##{@other_issue.id}",
  103 + :description => "ask @#{fred.name} for details"
  104 + end
  105 +
  106 + it "should render subject in issues#index" do
  107 + visit project_issues_path(project)
  108 +
  109 + page.should have_link("##{@other_issue.id}")
  110 + end
  111 +
  112 + it "should render subject in issues#show" do
  113 + visit project_issue_path(project, @issue)
  114 +
  115 + page.should have_link("##{@other_issue.id}")
  116 + end
  117 +
  118 + it "should render details in issues#show" do
  119 + visit project_issue_path(project, @issue)
  120 +
  121 + page.should have_link("@#{fred.name}")
  122 + end
  123 + end
  124 +
  125 +
  126 + describe "for merge requests" do
  127 + before do
  128 + @merge_request = Factory :merge_request,
  129 + :project => project,
  130 + :title => "fix ##{issue.id}"
  131 + end
  132 +
  133 + it "should render title in merge_requests#index" do
  134 + visit project_merge_requests_path(project)
  135 +
  136 + page.should have_link("##{issue.id}")
  137 + end
  138 +
  139 + it "should render title in merge_requests#show" do
  140 + visit project_merge_request_path(project, @merge_request)
  141 +
  142 + page.should have_link("##{issue.id}")
  143 + end
  144 + end
  145 +
  146 +
  147 + describe "for milestones" do
  148 + before do
  149 + @milestone = Factory :milestone,
  150 + :project => project,
  151 + :title => "fix ##{issue.id}",
  152 + :description => "ask @#{fred.name} for details"
  153 + end
  154 +
  155 + it "should render title in milestones#index" do
  156 + visit project_milestones_path(project)
  157 +
  158 + page.should have_link("##{issue.id}")
  159 + end
  160 +
  161 + it "should render title in milestones#show" do
  162 + visit project_milestone_path(project, @milestone)
  163 +
  164 + page.should have_link("##{issue.id}")
  165 + end
  166 +
  167 + it "should render description in milestones#show" do
  168 + visit project_milestone_path(project, @milestone)
  169 +
  170 + page.should have_link("@#{fred.name}")
  171 + end
  172 + end
  173 +
  174 +
  175 + describe "for notes" do
  176 + it "should render in commits#show", :js => true do
  177 + visit project_commit_path(project, :id => commit.id)
  178 + fill_in "note_note", :with => "see ##{issue.id}"
  179 + click_button "Add Comment"
  180 +
  181 + page.should have_link("##{issue.id}")
  182 + end
  183 +
  184 + it "should render in issue#show", :js => true do
  185 + visit project_issue_path(project, issue)
  186 + fill_in "note_note", :with => "see ##{issue.id}"
  187 + click_button "Add Comment"
  188 +
  189 + page.should have_link("##{issue.id}")
  190 + end
  191 +
  192 + it "should render in merge_request#show", :js => true do
  193 + visit project_merge_request_path(project, merge_request)
  194 + fill_in "note_note", :with => "see ##{issue.id}"
  195 + click_button "Add Comment"
  196 +
  197 + page.should have_link("##{issue.id}")
  198 + end
  199 +
  200 + it "should render in projects#wall", :js => true do
  201 + visit wall_project_path(project)
  202 + fill_in "note_note", :with => "see ##{issue.id}"
  203 + click_button "Add Comment"
  204 +
  205 + page.should have_link("##{issue.id}")
  206 + end
  207 +
  208 + it "should render in wikis#index", :js => true do
  209 + visit project_wiki_path(project, :index)
  210 + fill_in "Title", :with => 'Test title'
  211 + fill_in "Content", :with => '[link test](test)'
  212 + click_on "Save"
  213 +
  214 + fill_in "note_note", :with => "see ##{issue.id}"
  215 + click_button "Add Comment"
  216 +
  217 + page.should have_link("##{issue.id}")
  218 + end
  219 + end
  220 +
  221 +
  222 + describe "for wikis" do
  223 + before do
  224 + visit project_wiki_path(project, :index)
  225 + fill_in "Title", :with => "Circumvent ##{issue.id}"
  226 + fill_in "Content", :with => "# Other pages\n\n* [Foo](foo)\n* [Bar](bar)\n\nAlso look at ##{issue.id} :-)"
  227 + click_on "Save"
  228 + end
  229 +
  230 + it "should NOT render title in wikis#show" do
  231 + within(".content h3") do # page title
  232 + page.should have_content("Circumvent ##{issue.id}")
  233 + page.should_not have_link("##{issue.id}")
  234 + end
  235 + end
  236 +
  237 + it "should render content in wikis#show" do
  238 + page.should have_link("##{issue.id}")
  239 + end
  240 + end
  241 +end