diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index aa1de2f50ef..aff7011edd0 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -74,6 +74,7 @@ module GitlabMarkdownHelper
end
end
+ # TODO (rspeicher): This should be its own filter
def create_relative_links(text)
paths = extract_paths(text)
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index ad4a7612724..c3b4731dff3 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -108,4 +108,7 @@ module IssuesHelper
xml.summary issue.title
end
end
+
+ # Required for Gitlab::Markdown::IssueReferenceFilter
+ module_function :url_for_issue, :title_for_issue
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 32ef2e7ca84..8272c177d59 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -1,4 +1,6 @@
module LabelsHelper
+ include ActionView::Helpers::TagHelper
+
def project_label_names
@project.labels.pluck(:title)
end
@@ -7,9 +9,13 @@ module LabelsHelper
label_color = label.color || Label::DEFAULT_COLOR
text_color = text_color_for_bg(label_color)
- content_tag :span, class: 'label color-label', style: "background-color:#{label_color};color:#{text_color}" do
- label.name
- end
+ # Intentionally not using content_tag here so that this method can be called
+ # by LabelReferenceFilter
+ span = %() +
+ escape_once(label.name) + ''
+
+ span.html_safe
end
def suggested_colors
@@ -42,13 +48,16 @@ module LabelsHelper
r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex)
if (r + g + b) > 500
- "#333"
+ '#333333'
else
- "#FFF"
+ '#FFFFFF'
end
end
def project_labels_options(project)
options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
end
+
+ # Required for Gitlab::Markdown::LabelReferenceFilter
+ module_function :render_colored_label, :text_color_for_bg, :escape_once
end
diff --git a/app/models/label.rb b/app/models/label.rb
index 1f22ed23d42..eee28acefc1 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -27,7 +27,7 @@ class Label < ActiveRecord::Base
# Don't allow '?', '&', and ',' for label titles
validates :title,
presence: true,
- format: { with: /\A[^&\?,&]+\z/ },
+ format: { with: /\A[^&\?,]+\z/ },
uniqueness: { scope: :project_id }
default_scope { order(title: :asc) }
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 1d5fd4c8b0d..8ec5a20035f 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -163,7 +163,7 @@ Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported
## Special GitLab References
-GFM recognized special references.
+GFM recognizes special references.
You can easily reference e.g. an issue, a commit, a team member or even the whole team within a project.
@@ -171,19 +171,30 @@ GFM will turn that reference into a link so you can navigate between them easily
GFM will recognize the following:
-- @foo : for specific team members or groups
-- @all : for the whole team
-- #123 : for issues
-- !123 : for merge requests
-- $123 : for snippets
-- 1234567 : for commits
-- \[file\](path/to/file) : for file references
+| input | references |
+|-----------------------:|:---------------------------|
+| `@user_name` | specific user |
+| `@group_name` | specific group |
+| `@all` | entire team |
+| `#123` | issue |
+| `!123` | merge request |
+| `$123` | snippet |
+| `~123` | label by ID |
+| `~bug` | one-word label by name |
+| `~"feature request"` | multi-word label by name |
+| `9ba12248` | specific commit |
+| `9ba12248...b19a04f5` | commit range comparison |
+| `[README](doc/README)` | repository file references |
-GFM also recognizes references to commits, issues, and merge requests in other projects:
+GFM also recognizes certain cross-project references:
-- namespace/project#123 : for issues
-- namespace/project!123 : for merge requests
-- namespace/project@1234567 : for commits
+| input | references |
+|----------------------------------------:|:------------------------|
+| `namespace/project#123` | issue |
+| `namespace/project!123` | merge request |
+| `namespace/project$123` | snippet |
+| `namespace/project@9ba12248` | specific commit |
+| `namespace/project@9ba12248...b19a04f5` | commit range comparison |
## Task Lists
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 47c456d8dc7..f2302015437 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -10,11 +10,11 @@ module Gitlab
# Supported reference formats are:
# * @foo for team members
# * #123 for issues
- # * #JIRA-123 for Jira issues
+ # * JIRA-123 for Jira issues
# * !123 for merge requests
# * $123 for snippets
- # * 123456 for commits
- # * 123456...7890123 for commit ranges (comparisons)
+ # * 1c002d for specific commit
+ # * 1c002d...35cfb2 for commit ranges (comparisons)
#
# It also parses Emoji codes to insert images. See
# http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
@@ -30,10 +30,6 @@ module Gitlab
# >> gfm(":trollface:")
# => "
module Markdown
- include IssuesHelper
-
- attr_reader :options, :html_options
-
# Public: Parse the provided text with GitLab-Flavored Markdown
#
# text - the source text
@@ -65,42 +61,14 @@ module Gitlab
reference_only_path: true
)
- @options = options
- @html_options = html_options
-
- # TODO: add popups with additional information
-
- # Used markdown pipelines in GitLab:
- # GitlabEmojiFilter - performs emoji replacement.
- # SanitizationFilter - remove unsafe HTML tags and attributes
- #
- # see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
- filters = [
- HTML::Pipeline::Gitlab::GitlabEmojiFilter,
- HTML::Pipeline::SanitizationFilter
- ]
-
- whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
- whitelist[:attributes][:all].push('class', 'id')
- whitelist[:elements].push('span')
-
- # Remove the rel attribute that the sanitize gem adds, and remove the
- # href attribute if it contains inline javascript
- fix_anchors = lambda do |env|
- name, node = env[:node_name], env[:node]
- if name == 'a'
- node.remove_attribute('rel')
- if node['href'] && node['href'].match('javascript:')
- node.remove_attribute('href')
- end
- end
- end
- whitelist[:transformers].push(fix_anchors)
-
markdown_context = {
- asset_root: Gitlab.config.gitlab.url,
- asset_host: Gitlab::Application.config.asset_host,
- whitelist: whitelist
+ asset_root: Gitlab.config.gitlab.url,
+ asset_host: Gitlab::Application.config.asset_host,
+ whitelist: sanitization_whitelist,
+ reference_class: html_options[:class],
+ only_path: options[:reference_only_path],
+ current_user: current_user,
+ project: project
}
markdown_pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
@@ -114,21 +82,6 @@ module Gitlab
text = result[:output].to_html(save_with: save_options)
- # Extract pre blocks so they are not altered
- # from http://github.github.com/github-flavored-markdown/
- text.gsub!(%r{
.*?
|.*?}m) { |match| extract_piece(match) }
- # Extract links with probably parsable hrefs
- text.gsub!(%r{.*?}m) { |match| extract_piece(match) }
- # Extract images with probably parsable src
- text.gsub!(%r{}m) { |match| extract_piece(match) }
-
- text = parse(text, project)
-
- # Insert pre block extractions
- text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
- insert_piece($1)
- end
-
if options[:parse_tasks]
text = parse_tasks(text)
end
@@ -138,242 +91,53 @@ module Gitlab
private
- def extract_piece(text)
- @extractions ||= {}
-
- md5 = Digest::MD5.hexdigest(text)
- @extractions[md5] = text
- "{gfm-extraction-#{md5}}"
- end
-
- def insert_piece(id)
- @extractions[id]
- end
-
- # Private: Parses text for references
+ # Custom filters for html-pipeline:
#
- # text - Text to parse
+ # SanitizationFilter should come first so that all generated reference HTML
+ # goes through untouched.
#
- # Returns parsed text
- def parse(text, project = @project)
- parse_references(text, project) if project
+ # See https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
+ def filters
+ [
+ HTML::Pipeline::SanitizationFilter,
- text
+ Gitlab::Markdown::UserReferenceFilter,
+ Gitlab::Markdown::IssueReferenceFilter,
+ Gitlab::Markdown::ExternalIssueReferenceFilter,
+ Gitlab::Markdown::MergeRequestReferenceFilter,
+ Gitlab::Markdown::SnippetReferenceFilter,
+ Gitlab::Markdown::CommitRangeReferenceFilter,
+ Gitlab::Markdown::CommitReferenceFilter,
+ Gitlab::Markdown::LabelReferenceFilter,
+
+ HTML::Pipeline::Gitlab::GitlabEmojiFilter
+ ]
end
- NAME_STR = Gitlab::Regex::NAMESPACE_REGEX_STR
- PROJ_STR = "(?#{NAME_STR}/#{NAME_STR})"
-
- REFERENCE_PATTERN = %r{
- (?\W)? # Prefix
- ( # Reference
- @(?#{NAME_STR}) # User name
- |~(?