Commit f42cfa9e authored by Douwe Maan's avatar Douwe Maan

Refactor reference gathering to use a dedicated filter

parent 12626f02
...@@ -19,7 +19,8 @@ module GitlabMarkdownHelper ...@@ -19,7 +19,8 @@ module GitlabMarkdownHelper
escape_once(body) escape_once(body)
end end
gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: current_user) user = current_user if defined?(current_user)
gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: user)
fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body)
if fragment.children.size == 1 && fragment.children[0].name == 'a' if fragment.children.size == 1 && fragment.children[0].name == 'a'
...@@ -55,8 +56,10 @@ module GitlabMarkdownHelper ...@@ -55,8 +56,10 @@ module GitlabMarkdownHelper
ref: @ref ref: @ref
) )
user = current_user if defined?(current_user)
html = Gitlab::Markdown.render(text, context) html = Gitlab::Markdown.render(text, context)
Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], user: current_user) Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], user: user)
end end
# TODO (rspeicher): Remove all usages of this helper and just call `markdown` # TODO (rspeicher): Remove all usages of this helper and just call `markdown`
...@@ -72,8 +75,10 @@ module GitlabMarkdownHelper ...@@ -72,8 +75,10 @@ module GitlabMarkdownHelper
ref: @ref ref: @ref
) )
user = current_user if defined?(current_user)
html = Gitlab::Markdown.gfm(text, options) html = Gitlab::Markdown.gfm(text, options)
Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], user: current_user) Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], user: user)
end end
def asciidoc(text) def asciidoc(text)
......
...@@ -26,6 +26,18 @@ module Gitlab ...@@ -26,6 +26,18 @@ module Gitlab
end end
end end
def self.referenced_by(node)
project = Project.find(node.attr("data-project")) rescue nil
return unless project
id = node.attr("data-commit-range")
range = CommitRange.new(id, project)
return unless range.valid_commits?
{ commit_range: range }
end
def initialize(*args) def initialize(*args)
super super
...@@ -53,13 +65,11 @@ module Gitlab ...@@ -53,13 +65,11 @@ module Gitlab
range = CommitRange.new(id, project) range = CommitRange.new(id, project)
if range.valid_commits? if range.valid_commits?
push_result(:commit_range, range)
url = url_for_commit_range(project, range) url = url_for_commit_range(project, range)
title = range.reference_title title = range.reference_title
klass = reference_class(:commit_range) klass = reference_class(:commit_range)
data = data_attribute(project.id) data = data_attribute(project: project.id, commit_range: id)
project_ref += '@' if project_ref project_ref += '@' if project_ref
......
...@@ -26,6 +26,18 @@ module Gitlab ...@@ -26,6 +26,18 @@ module Gitlab
end end
end end
def self.referenced_by(node)
project = Project.find(node.attr("data-project")) rescue nil
return unless project
id = node.attr("data-commit")
commit = commit_from_ref(project, id)
return unless commit
{ commit: commit }
end
def call def call
replace_text_nodes_matching(Commit.reference_pattern) do |content| replace_text_nodes_matching(Commit.reference_pattern) do |content|
commit_link_filter(content) commit_link_filter(content)
...@@ -39,17 +51,15 @@ module Gitlab ...@@ -39,17 +51,15 @@ module Gitlab
# Returns a String with commit references replaced with links. All links # Returns a String with commit references replaced with links. All links
# have `gfm` and `gfm-commit` class names attached for styling. # have `gfm` and `gfm-commit` class names attached for styling.
def commit_link_filter(text) def commit_link_filter(text)
self.class.references_in(text) do |match, commit_ref, project_ref| self.class.references_in(text) do |match, id, project_ref|
project = self.project_from_ref(project_ref) project = self.project_from_ref(project_ref)
if commit = commit_from_ref(project, commit_ref) if commit = self.class.commit_from_ref(project, id)
push_result(:commit, commit)
url = url_for_commit(project, commit) url = url_for_commit(project, commit)
title = escape_once(commit.link_title) title = escape_once(commit.link_title)
klass = reference_class(:commit) klass = reference_class(:commit)
data = data_attribute(project.id) data = data_attribute(project: project.id, commit: id)
project_ref += '@' if project_ref project_ref += '@' if project_ref
...@@ -62,9 +72,9 @@ module Gitlab ...@@ -62,9 +72,9 @@ module Gitlab
end end
end end
def commit_from_ref(project, commit_ref) def self.commit_from_ref(project, id)
if project && project.valid_repo? if project && project.valid_repo?
project.commit(commit_ref) project.commit(id)
end end
end end
......
...@@ -47,8 +47,9 @@ module Gitlab ...@@ -47,8 +47,9 @@ module Gitlab
title = escape_once("Issue in #{project.external_issue_tracker.title}") title = escape_once("Issue in #{project.external_issue_tracker.title}")
klass = reference_class(:issue) klass = reference_class(:issue)
data = data_attribute(project: project.id)
%(<a href="#{url}" %(<a href="#{url}" #{data}
title="#{title}" title="#{title}"
class="#{klass}">#{match}</a>) class="#{klass}">#{match}</a>)
end end
......
...@@ -27,6 +27,13 @@ module Gitlab ...@@ -27,6 +27,13 @@ module Gitlab
end end
end end
def self.referenced_by(node)
issue = Issue.find(node.attr("data-issue")) rescue nil
return unless issue
{ issue: issue }
end
def call def call
replace_text_nodes_matching(Issue.reference_pattern) do |content| replace_text_nodes_matching(Issue.reference_pattern) do |content|
issue_link_filter(content) issue_link_filter(content)
...@@ -45,13 +52,11 @@ module Gitlab ...@@ -45,13 +52,11 @@ module Gitlab
project = self.project_from_ref(project_ref) project = self.project_from_ref(project_ref)
if project && issue = project.get_issue(id) if project && issue = project.get_issue(id)
push_result(:issue, issue)
url = url_for_issue(id, project, only_path: context[:only_path]) url = url_for_issue(id, project, only_path: context[:only_path])
title = escape_once("Issue: #{issue.title}") title = escape_once("Issue: #{issue.title}")
klass = reference_class(:issue) klass = reference_class(:issue)
data = data_attribute(project.id) data = data_attribute(project: project.id, issue: issue.id)
%(<a href="#{url}" #{data} %(<a href="#{url}" #{data}
title="#{title}" title="#{title}"
......
...@@ -22,6 +22,13 @@ module Gitlab ...@@ -22,6 +22,13 @@ module Gitlab
end end
end end
def self.referenced_by(node)
label = Label.find(node.attr("data-label")) rescue nil
return unless label
{ label: label }
end
def call def call
replace_text_nodes_matching(Label.reference_pattern) do |content| replace_text_nodes_matching(Label.reference_pattern) do |content|
label_link_filter(content) label_link_filter(content)
...@@ -41,11 +48,9 @@ module Gitlab ...@@ -41,11 +48,9 @@ module Gitlab
params = label_params(id, name) params = label_params(id, name)
if label = project.labels.find_by(params) if label = project.labels.find_by(params)
push_result(:label, label)
url = url_for_label(project, label) url = url_for_label(project, label)
klass = reference_class(:label) klass = reference_class(:label)
data = data_attribute(project.id) data = data_attribute(project: project.id, label: label.id)
%(<a href="#{url}" #{data} %(<a href="#{url}" #{data}
class="#{klass}">#{render_colored_label(label)}</a>) class="#{klass}">#{render_colored_label(label)}</a>)
......
...@@ -27,6 +27,13 @@ module Gitlab ...@@ -27,6 +27,13 @@ module Gitlab
end end
end end
def self.referenced_by(node)
merge_request = MergeRequest.find(node.attr("data-merge-request")) rescue nil
return unless merge_request
{ merge_request: merge_request }
end
def call def call
replace_text_nodes_matching(MergeRequest.reference_pattern) do |content| replace_text_nodes_matching(MergeRequest.reference_pattern) do |content|
merge_request_link_filter(content) merge_request_link_filter(content)
...@@ -45,11 +52,9 @@ module Gitlab ...@@ -45,11 +52,9 @@ module Gitlab
project = self.project_from_ref(project_ref) project = self.project_from_ref(project_ref)
if project && merge_request = project.merge_requests.find_by(iid: id) if project && merge_request = project.merge_requests.find_by(iid: id)
push_result(:merge_request, merge_request)
title = escape_once("Merge Request: #{merge_request.title}") title = escape_once("Merge Request: #{merge_request.title}")
klass = reference_class(:merge_request) klass = reference_class(:merge_request)
data = data_attribute(project.id) data = data_attribute(project: project.id, merge_request: merge_request.id)
url = url_for_merge_request(merge_request, project) url = url_for_merge_request(merge_request, project)
......
...@@ -19,49 +19,19 @@ module Gitlab ...@@ -19,49 +19,19 @@ module Gitlab
doc doc
end end
private
def user_can_reference?(node) def user_can_reference?(node)
if node.has_attribute?('data-group-id') if node.has_attribute?('data-reference-filter')
user_can_reference_group?(node.attr('data-group-id')) reference_type = node.attr('data-reference-filter')
elsif node.has_attribute?('data-project-id') reference_filter = reference_type.constantize
user_can_reference_project?(node.attr('data-project-id'))
elsif node.has_attribute?('data-user-id') reference_filter.user_can_reference?(current_user, node)
user_can_reference_user?(node.attr('data-user-id'))
else else
true true
end end
end end
def user_can_reference_group?(id)
group = Group.find(id)
group && can?(:read_group, group)
rescue ActiveRecord::RecordNotFound
false
end
def user_can_reference_project?(id)
project = Project.find(id)
project && can?(:read_project, project)
rescue ActiveRecord::RecordNotFound
false
end
def user_can_reference_user?(id)
# Permit all user reference links
true
end
private
def abilities
Ability.abilities
end
def can?(ability, object)
abilities.allowed?(current_user, ability, object)
end
def current_user def current_user
context[:current_user] context[:current_user]
end end
......
...@@ -15,10 +15,17 @@ module Gitlab ...@@ -15,10 +15,17 @@ module Gitlab
# Results: # Results:
# :references - A Hash of references that were found and replaced. # :references - A Hash of references that were found and replaced.
class ReferenceFilter < HTML::Pipeline::Filter class ReferenceFilter < HTML::Pipeline::Filter
def initialize(*args) def self.user_can_reference?(user, node)
super if node.has_attribute?('data-project')
project = Project.find(node.attr('data-project')) rescue nil
Ability.abilities.allowed?(user, :read_project, project)
else
true
end
end
result[:references] = Hash.new { |hash, type| hash[type] = [] } def self.referenced_by(node)
nil
end end
# Returns a data attribute String to attach to a reference link # Returns a data attribute String to attach to a reference link
...@@ -28,13 +35,14 @@ module Gitlab ...@@ -28,13 +35,14 @@ module Gitlab
# #
# Examples: # Examples:
# #
# data_attribute(1) # => "data-project-id=\"1\"" # data_attribute(project: 1) # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"1\""
# data_attribute(2, :user) # => "data-user-id=\"2\"" # data_attribute(user: 2) # => "data-reference-filter=\"SomeReferenceFilter\" data-user=\"2\""
# data_attribute(3, :group) # => "data-group-id=\"3\"" # data_attribute(group: 3) # => "data-reference-filter=\"SomeReferenceFilter\" data-group=\"3\""
# #
# Returns a String # Returns a String
def data_attribute(id, type = :project) def data_attribute(attributes = {})
%Q(data-#{type}-id="#{id}") attributes[:reference_filter] = self.class.name
attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{value}") }.join(" ")
end end
def escape_once(html) def escape_once(html)
...@@ -59,16 +67,6 @@ module Gitlab ...@@ -59,16 +67,6 @@ module Gitlab
context[:project] context[:project]
end end
# Add a reference to the pipeline's result Hash
#
# type - Singular Symbol reference type (e.g., :issue, :user, etc.)
# values - One or more Objects to add
def push_result(type, *values)
return if values.empty?
result[:references][type].push(*values)
end
def reference_class(type) def reference_class(type)
"gfm gfm-#{type}" "gfm gfm-#{type}"
end end
......
require 'gitlab/markdown'
require 'html/pipeline/filter'
module Gitlab
module Markdown
# HTML filter that removes references to records that the current user does
# not have permission to view.
#
# Expected to be run in its own post-processing pipeline.
#
class ReferenceGathererFilter < HTML::Pipeline::Filter
def initialize(*)
super
result[:references] ||= Hash.new { |hash, type| hash[type] = [] }
end
def call
doc.css('a.gfm').each do |node|
gather_references(node)
end
doc
end
private
def gather_references(node)
return unless node.has_attribute?('data-reference-filter')
reference_type = node.attr('data-reference-filter')
reference_filter = reference_type.constantize
return unless reference_filter.user_can_reference?(current_user, node)
references = reference_filter.referenced_by(node)
return unless references
references.each do |type, values|
result[:references][type].push(*values)
end
end
def current_user
context[:current_user]
end
end
end
end
...@@ -27,6 +27,13 @@ module Gitlab ...@@ -27,6 +27,13 @@ module Gitlab
end end
end end
def self.referenced_by(node)
snippet = Snippet.find(node.attr("data-snippet")) rescue nil
return unless snippet
{ snippet: snippet }
end
def call def call
replace_text_nodes_matching(Snippet.reference_pattern) do |content| replace_text_nodes_matching(Snippet.reference_pattern) do |content|
snippet_link_filter(content) snippet_link_filter(content)
...@@ -45,11 +52,9 @@ module Gitlab ...@@ -45,11 +52,9 @@ module Gitlab
project = self.project_from_ref(project_ref) project = self.project_from_ref(project_ref)
if project && snippet = project.snippets.find_by(id: id) if project && snippet = project.snippets.find_by(id: id)
push_result(:snippet, snippet)
title = escape_once("Snippet: #{snippet.title}") title = escape_once("Snippet: #{snippet.title}")
klass = reference_class(:snippet) klass = reference_class(:snippet)
data = data_attribute(project.id) data = data_attribute(project: project.id, snippet: snippet.id)
url = url_for_snippet(snippet, project) url = url_for_snippet(snippet, project)
......
...@@ -23,6 +23,34 @@ module Gitlab ...@@ -23,6 +23,34 @@ module Gitlab
end end
end end
def self.referenced_by(node)
if node.has_attribute?('data-group')
group = Group.find(node.attr('data-group')) rescue nil
return unless group
{ user: group.users }
elsif node.has_attribute?('data-user')
user = User.find(node.attr('data-user')) rescue nil
return unless user
{ user: user }
elsif node.has_attribute?('data-project')
project = Project.find(node.attr('data-project')) rescue nil
return unless project
{ user: project.team.members.flatten }
end
end
def self.user_can_reference?(user, node)
if node.has_attribute?('data-group')
group = Group.find(node.attr('data-group')) rescue nil
Ability.abilities.allowed?(user, :read_group, group)
else
super
end
end
def call def call
replace_text_nodes_matching(User.reference_pattern) do |content| replace_text_nodes_matching(User.reference_pattern) do |content|
user_link_filter(content) user_link_filter(content)
...@@ -61,14 +89,12 @@ module Gitlab ...@@ -61,14 +89,12 @@ module Gitlab
def link_to_all def link_to_all
project = context[:project] project = context[:project]
# FIXME (rspeicher): Law of Demeter
push_result(:user, *project.team.members.flatten)
url = urls.namespace_project_url(project.namespace, project, url = urls.namespace_project_url(project.namespace, project,
only_path: context[:only_path]) only_path: context[:only_path])
data = data_attribute(project: project.id)
text = User.reference_prefix + 'all' text = User.reference_prefix + 'all'
%(<a href="#{url}" class="#{link_class}">#{text}</a>) %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
end end
def link_to_namespace(namespace) def link_to_namespace(namespace)
...@@ -80,20 +106,16 @@ module Gitlab ...@@ -80,20 +106,16 @@ module Gitlab
end end
def link_to_group(group, namespace) def link_to_group(group, namespace)
push_result(:user, *namespace.users)
url = urls.group_url(group, only_path: context[:only_path]) url = urls.group_url(group, only_path: context[:only_path])
data = data_attribute(namespace.id, :group) data = data_attribute(group: namespace.id)
text = Group.reference_prefix + group text = Group.reference_prefix + group
%(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>) %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
end end
def link_to_user(user, namespace) def link_to_user(user, namespace)
push_result(:user, namespace.owner)
url = urls.user_url(user, only_path: context[:only_path]) url = urls.user_url(user, only_path: context[:only_path])
data = data_attribute(namespace.owner_id, :user) data = data_attribute(user: namespace.owner_id)
text = User.reference_prefix + user text = User.reference_prefix + user
%(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>) %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
......
...@@ -50,7 +50,7 @@ module Gitlab ...@@ -50,7 +50,7 @@ module Gitlab
ignore_blockquotes: true ignore_blockquotes: true
} }
pipeline = HTML::Pipeline.new([filter], context) pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context)
result = pipeline.call(@text) result = pipeline.call(@text)
result[:references][filter_type] result[:references][filter_type]
......
...@@ -44,7 +44,7 @@ describe GitlabMarkdownHelper do ...@@ -44,7 +44,7 @@ describe GitlabMarkdownHelper do
describe "override default project" do describe "override default project" do
let(:actual) { issue.to_reference } let(:actual) { issue.to_reference }
let(:second_project) { create(:project) } let(:second_project) { create(:project, :public) }
let(:second_issue) { create(:issue, project: second_project) } let(:second_issue) { create(:issue, project: second_project) }
it 'should link to the issue' do it 'should link to the issue' do
......
...@@ -4,7 +4,7 @@ module Gitlab::Markdown ...@@ -4,7 +4,7 @@ module Gitlab::Markdown
describe CommitRangeReferenceFilter do describe CommitRangeReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
let(:project) { create(:project) } let(:project) { create(:project, :public) }
let(:commit1) { project.commit } let(:commit1) { project.commit }
let(:commit2) { project.commit("HEAD~2") } let(:commit2) { project.commit("HEAD~2") }
...@@ -75,12 +75,20 @@ module Gitlab::Markdown ...@@ -75,12 +75,20 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range'
end end
it 'includes a data-project-id attribute' do it 'includes a data-project attribute' do
doc = filter("See #{reference}") doc = filter("See #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project-id') expect(link).to have_attribute('data-project')
expect(link.attr('data-project-id')).to eq project.id.to_s expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-commit-range attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-commit-range')
expect(link.attr('data-commit-range')).to eq range.to_reference
end end
it 'supports an :only_path option' do it 'supports an :only_path option' do
...@@ -92,14 +100,14 @@ module Gitlab::Markdown ...@@ -92,14 +100,14 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("See #{reference}") result = reference_pipeline_result("See #{reference}")
expect(result[:references][:commit_range]).not_to be_empty expect(result[:references][:commit_range]).not_to be_empty
end end
end end
context 'cross-project reference' do context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, namespace: namespace) } let(:project2) { create(:project, :public, namespace: namespace) }
let(:reference) { range.to_reference(project) } let(:reference) { range.to_reference(project) }
before do before do
...@@ -129,7 +137,7 @@ module Gitlab::Markdown ...@@ -129,7 +137,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("See #{reference}") result = reference_pipeline_result("See #{reference}")
expect(result[:references][:commit_range]).not_to be_empty expect(result[:references][:commit_range]).not_to be_empty
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab::Markdown ...@@ -4,7 +4,7 @@ module Gitlab::Markdown
describe CommitReferenceFilter do describe CommitReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
let(:project) { create(:project) } let(:project) { create(:project, :public) }
let(:commit) { project.commit } let(:commit) { project.commit }
it 'requires project context' do it 'requires project context' do
...@@ -71,12 +71,20 @@ module Gitlab::Markdown ...@@ -71,12 +71,20 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit'
end end
it 'includes a data-project-id attribute' do it 'includes a data-project attribute' do
doc = filter("See #{reference}") doc = filter("See #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project-id') expect(link).to have_attribute('data-project')
expect(link.attr('data-project-id')).to eq project.id.to_s expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-commit attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-commit')
expect(link.attr('data-commit')).to eq commit.id
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
...@@ -88,14 +96,14 @@ module Gitlab::Markdown ...@@ -88,14 +96,14 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("See #{reference}") result = reference_pipeline_result("See #{reference}")
expect(result[:references][:commit]).not_to be_empty expect(result[:references][:commit]).not_to be_empty
end end
end end
context 'cross-project reference' do context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, namespace: namespace) } let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit } let(:commit) { project2.commit }
let(:reference) { commit.to_reference(project) } let(:reference) { commit.to_reference(project) }
...@@ -119,7 +127,7 @@ module Gitlab::Markdown ...@@ -119,7 +127,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("See #{reference}") result = reference_pipeline_result("See #{reference}")
expect(result[:references][:commit]).not_to be_empty expect(result[:references][:commit]).not_to be_empty
end end
end end
......
...@@ -8,7 +8,7 @@ module Gitlab::Markdown ...@@ -8,7 +8,7 @@ module Gitlab::Markdown
IssuesHelper IssuesHelper
end end
let(:project) { create(:empty_project) } let(:project) { create(:empty_project, :public) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
it 'requires project context' do it 'requires project context' do
...@@ -68,12 +68,20 @@ module Gitlab::Markdown ...@@ -68,12 +68,20 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue'
end end
it 'includes a data-project-id attribute' do it 'includes a data-project attribute' do
doc = filter("Issue #{reference}") doc = filter("Issue #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project-id') expect(link).to have_attribute('data-project')
expect(link.attr('data-project-id')).to eq project.id.to_s expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-issue attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-issue')
expect(link.attr('data-issue')).to eq issue.id.to_s
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
...@@ -85,14 +93,14 @@ module Gitlab::Markdown ...@@ -85,14 +93,14 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Fixed #{reference}") result = reference_pipeline_result("Fixed #{reference}")
expect(result[:references][:issue]).to eq [issue] expect(result[:references][:issue]).to eq [issue]
end end
end end
context 'cross-project reference' do context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) } let(:issue) { create(:issue, project: project2) }
let(:reference) { issue.to_reference(project) } let(:reference) { issue.to_reference(project) }
...@@ -123,7 +131,7 @@ module Gitlab::Markdown ...@@ -123,7 +131,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Fixed #{reference}") result = reference_pipeline_result("Fixed #{reference}")
expect(result[:references][:issue]).to eq [issue] expect(result[:references][:issue]).to eq [issue]
end end
end end
......
...@@ -5,7 +5,7 @@ module Gitlab::Markdown ...@@ -5,7 +5,7 @@ module Gitlab::Markdown
describe LabelReferenceFilter do describe LabelReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
let(:project) { create(:empty_project) } let(:project) { create(:empty_project, :public) }
let(:label) { create(:label, project: project) } let(:label) { create(:label, project: project) }
let(:reference) { label.to_reference } let(:reference) { label.to_reference }
...@@ -25,12 +25,20 @@ module Gitlab::Markdown ...@@ -25,12 +25,20 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label'
end end
it 'includes a data-project-id attribute' do it 'includes a data-project attribute' do
doc = filter("Label #{reference}") doc = filter("Label #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project-id') expect(link).to have_attribute('data-project')
expect(link.attr('data-project-id')).to eq project.id.to_s expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-label attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-label')
expect(link.attr('data-label')).to eq label.id.to_s
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
...@@ -42,7 +50,7 @@ module Gitlab::Markdown ...@@ -42,7 +50,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Label #{reference}") result = reference_pipeline_result("Label #{reference}")
expect(result[:references][:label]).to eq [label] expect(result[:references][:label]).to eq [label]
end end
......
...@@ -4,7 +4,7 @@ module Gitlab::Markdown ...@@ -4,7 +4,7 @@ module Gitlab::Markdown
describe MergeRequestReferenceFilter do describe MergeRequestReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
let(:project) { create(:project) } let(:project) { create(:project, :public) }
let(:merge) { create(:merge_request, source_project: project) } let(:merge) { create(:merge_request, source_project: project) }
it 'requires project context' do it 'requires project context' do
...@@ -56,12 +56,20 @@ module Gitlab::Markdown ...@@ -56,12 +56,20 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request'
end end
it 'includes a data-project-id attribute' do it 'includes a data-project attribute' do
doc = filter("Merge #{reference}") doc = filter("Merge #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project-id') expect(link).to have_attribute('data-project')
expect(link.attr('data-project-id')).to eq project.id.to_s expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-merge-request attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-merge-request')
expect(link.attr('data-merge-request')).to eq merge.id.to_s
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
...@@ -73,14 +81,14 @@ module Gitlab::Markdown ...@@ -73,14 +81,14 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Merge #{reference}") result = reference_pipeline_result("Merge #{reference}")
expect(result[:references][:merge_request]).to eq [merge] expect(result[:references][:merge_request]).to eq [merge]
end end
end end
context 'cross-project reference' do context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, namespace: namespace) } let(:project2) { create(:project, :public, namespace: namespace) }
let(:merge) { create(:merge_request, source_project: project2) } let(:merge) { create(:merge_request, source_project: project2) }
let(:reference) { merge.to_reference(project) } let(:reference) { merge.to_reference(project) }
...@@ -104,7 +112,7 @@ module Gitlab::Markdown ...@@ -104,7 +112,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Merge #{reference}") result = reference_pipeline_result("Merge #{reference}")
expect(result[:references][:merge_request]).to eq [merge] expect(result[:references][:merge_request]).to eq [merge]
end end
end end
......
...@@ -16,72 +16,75 @@ module Gitlab::Markdown ...@@ -16,72 +16,75 @@ module Gitlab::Markdown
link_to('text', '', class: 'gfm', data: data) link_to('text', '', class: 'gfm', data: data)
end end
context 'with data-group-id' do context 'with data-project' do
it 'removes unpermitted Group references' do it 'removes unpermitted Project references' do
user = create(:user) user = create(:user)
group = create(:group) project = create(:empty_project)
link = reference_link(group_id: group.id) link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name)
doc = filter(link, current_user: user) doc = filter(link, current_user: user)
expect(doc.css('a').length).to eq 0 expect(doc.css('a').length).to eq 0
end end
it 'allows permitted Group references' do it 'allows permitted Project references' do
user = create(:user) user = create(:user)
group = create(:group) project = create(:empty_project)
group.add_developer(user) project.team << [user, :master]
link = reference_link(group_id: group.id) link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name)
doc = filter(link, current_user: user) doc = filter(link, current_user: user)
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
end end
it 'handles invalid Group references' do it 'handles invalid Project references' do
link = reference_link(group_id: 12345) link = reference_link(project: 12345, reference_filter: Gitlab::Markdown::ReferenceFilter.name)
expect { filter(link) }.not_to raise_error expect { filter(link) }.not_to raise_error
end end
end end
context 'with data-project-id' do context "for user references" do
it 'removes unpermitted Project references' do
user = create(:user)
project = create(:empty_project)
link = reference_link(project_id: project.id) context 'with data-group' do
doc = filter(link, current_user: user) it 'removes unpermitted Group references' do
user = create(:user)
group = create(:group)
expect(doc.css('a').length).to eq 0 link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
end doc = filter(link, current_user: user)
it 'allows permitted Project references' do expect(doc.css('a').length).to eq 0
user = create(:user) end
project = create(:empty_project)
project.team << [user, :master]
link = reference_link(project_id: project.id) it 'allows permitted Group references' do
doc = filter(link, current_user: user) user = create(:user)
group = create(:group)
group.add_developer(user)
expect(doc.css('a').length).to eq 1 link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
end doc = filter(link, current_user: user)
it 'handles invalid Project references' do expect(doc.css('a').length).to eq 1
link = reference_link(project_id: 12345) end
expect { filter(link) }.not_to raise_error it 'handles invalid Group references' do
link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
expect { filter(link) }.not_to raise_error
end
end end
end
context 'with data-user-id' do context 'with data-user' do
it 'allows any User reference' do it 'allows any User reference' do
user = create(:user) user = create(:user)
link = reference_link(user_id: user.id) link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
doc = filter(link) doc = filter(link)
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
end
end end
end end
end end
......
require 'spec_helper'
module Gitlab::Markdown
describe ReferenceGathererFilter do
include ActionView::Helpers::UrlHelper
include FilterSpecHelper
def reference_link(data)
link_to('text', '', class: 'gfm', data: data)
end
context "for issue references" do
context 'with data-project' do
it 'removes unpermitted Project references' do
user = create(:user)
project = create(:empty_project)
issue = create(:issue, project: project)
link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name)
result = pipeline_result(link, current_user: user)
expect(result[:references][:issue]).to be_empty
end
it 'allows permitted Project references' do
user = create(:user)
project = create(:empty_project)
issue = create(:issue, project: project)
project.team << [user, :master]
link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name)
result = pipeline_result(link, current_user: user)
expect(result[:references][:issue]).to eq([issue])
end
it 'handles invalid Project references' do
link = reference_link(project: 12345, issue: 12345, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name)
expect { pipeline_result(link) }.not_to raise_error
end
end
end
context "for user references" do
context 'with data-group' do
it 'removes unpermitted Group references' do
user = create(:user)
group = create(:group)
link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
result = pipeline_result(link, current_user: user)
expect(result[:references][:user]).to be_empty
end
it 'allows permitted Group references' do
user = create(:user)
group = create(:group)
group.add_developer(user)
link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
result = pipeline_result(link, current_user: user)
expect(result[:references][:user]).to eq([user])
end
it 'handles invalid Group references' do
link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
expect { pipeline_result(link) }.not_to raise_error
end
end
context 'with data-user' do
it 'allows any User reference' do
user = create(:user)
link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
result = pipeline_result(link)
expect(result[:references][:user]).to eq([user])
end
end
end
end
end
...@@ -4,7 +4,7 @@ module Gitlab::Markdown ...@@ -4,7 +4,7 @@ module Gitlab::Markdown
describe SnippetReferenceFilter do describe SnippetReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
let(:project) { create(:empty_project) } let(:project) { create(:empty_project, :public) }
let(:snippet) { create(:project_snippet, project: project) } let(:snippet) { create(:project_snippet, project: project) }
let(:reference) { snippet.to_reference } let(:reference) { snippet.to_reference }
...@@ -55,12 +55,20 @@ module Gitlab::Markdown ...@@ -55,12 +55,20 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet'
end end
it 'includes a data-project-id attribute' do it 'includes a data-project attribute' do
doc = filter("Snippet #{reference}") doc = filter("Snippet #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project-id') expect(link).to have_attribute('data-project')
expect(link.attr('data-project-id')).to eq project.id.to_s expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-snippet attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-snippet')
expect(link.attr('data-snippet')).to eq snippet.id.to_s
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
...@@ -72,14 +80,14 @@ module Gitlab::Markdown ...@@ -72,14 +80,14 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Snippet #{reference}") result = reference_pipeline_result("Snippet #{reference}")
expect(result[:references][:snippet]).to eq [snippet] expect(result[:references][:snippet]).to eq [snippet]
end end
end end
context 'cross-project reference' do context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:snippet) { create(:project_snippet, project: project2) } let(:snippet) { create(:project_snippet, project: project2) }
let(:reference) { snippet.to_reference(project) } let(:reference) { snippet.to_reference(project) }
...@@ -102,7 +110,7 @@ module Gitlab::Markdown ...@@ -102,7 +110,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Snippet #{reference}") result = reference_pipeline_result("Snippet #{reference}")
expect(result[:references][:snippet]).to eq [snippet] expect(result[:references][:snippet]).to eq [snippet]
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab::Markdown ...@@ -4,7 +4,7 @@ module Gitlab::Markdown
describe UserReferenceFilter do describe UserReferenceFilter do
include FilterSpecHelper include FilterSpecHelper
let(:project) { create(:empty_project) } let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:reference) { user.to_reference } let(:reference) { user.to_reference }
...@@ -39,7 +39,7 @@ module Gitlab::Markdown ...@@ -39,7 +39,7 @@ module Gitlab::Markdown
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Hey #{reference}") result = reference_pipeline_result("Hey #{reference}")
expect(result[:references][:user]).to eq [project.creator] expect(result[:references][:user]).to eq [project.creator]
end end
end end
...@@ -64,16 +64,16 @@ module Gitlab::Markdown ...@@ -64,16 +64,16 @@ module Gitlab::Markdown
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
end end
it 'includes a data-user-id attribute' do it 'includes a data-user attribute' do
doc = filter("Hey #{reference}") doc = filter("Hey #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-user-id') expect(link).to have_attribute('data-user')
expect(link.attr('data-user-id')).to eq user.namespace.owner_id.to_s expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Hey #{reference}") result = reference_pipeline_result("Hey #{reference}")
expect(result[:references][:user]).to eq [user] expect(result[:references][:user]).to eq [user]
end end
end end
...@@ -87,16 +87,16 @@ module Gitlab::Markdown ...@@ -87,16 +87,16 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
end end
it 'includes a data-group-id attribute' do it 'includes a data-group attribute' do
doc = filter("Hey #{reference}") doc = filter("Hey #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-group-id') expect(link).to have_attribute('data-group')
expect(link.attr('data-group-id')).to eq group.id.to_s expect(link.attr('data-group')).to eq group.id.to_s
end end
it 'adds to the results hash' do it 'adds to the results hash' do
result = pipeline_result("Hey #{reference}") result = reference_pipeline_result("Hey #{reference}")
expect(result[:references][:user]).to eq group.users expect(result[:references][:user]).to eq group.users
end end
end end
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ReferenceExtractor do describe Gitlab::ReferenceExtractor do
let(:project) { create(:project) } let(:project) { create(:project) }
subject { Gitlab::ReferenceExtractor.new(project, project.creator) } subject { Gitlab::ReferenceExtractor.new(project, project.owner) }
it 'accesses valid user objects' do it 'accesses valid user objects' do
@u_foo = create(:user, username: 'foo') @u_foo = create(:user, username: 'foo')
...@@ -102,7 +102,7 @@ describe Gitlab::ReferenceExtractor do ...@@ -102,7 +102,7 @@ describe Gitlab::ReferenceExtractor do
let(:issue) { create(:issue, project: other_project) } let(:issue) { create(:issue, project: other_project) }
before do before do
other_project.team << [project.creator, :developer] other_project.team << [project.owner, :developer]
end end
it 'handles project issue references' do it 'handles project issue references' do
......
...@@ -29,12 +29,19 @@ module FilterSpecHelper ...@@ -29,12 +29,19 @@ module FilterSpecHelper
# #
# Returns the Hash # Returns the Hash
def pipeline_result(body, contexts = {}) def pipeline_result(body, contexts = {})
contexts.reverse_merge!(project: project) contexts.reverse_merge!(project: project) if defined?(project)
pipeline = HTML::Pipeline.new([described_class], contexts) pipeline = HTML::Pipeline.new([described_class], contexts)
pipeline.call(body) pipeline.call(body)
end end
def reference_pipeline_result(body, contexts = {})
contexts.reverse_merge!(project: project) if defined?(project)
pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts)
pipeline.call(body)
end
# Modify a String reference to make it invalid # Modify a String reference to make it invalid
# #
# Commit SHAs get reversed, IDs get incremented by 1, all other Strings get # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment