summaryrefslogtreecommitdiff
path: root/lib/gitlab
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2015-04-27 11:38:36 +0300
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2015-04-27 11:38:36 +0300
commit278c3ba401efc68b6454abc2d530d813f2a9ffec (patch)
tree9a2b8349e71a5dffc069a61fb92e8454a19a587f /lib/gitlab
parent2cbdfbfd1edd251fdeab0e238552175644fe18d1 (diff)
parentf3ffd7d0b1d357b1dff6c00f02e3fbcaead7e2b6 (diff)
downloadgitlab-ce-278c3ba401efc68b6454abc2d530d813f2a9ffec.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce
Diffstat (limited to 'lib/gitlab')
-rw-r--r--lib/gitlab/gitorious_import/client.rb2
-rw-r--r--lib/gitlab/markdown/commit_range_reference_filter.rb43
-rw-r--r--lib/gitlab/markdown/commit_reference_filter.rb4
-rw-r--r--lib/gitlab/markdown/issue_reference_filter.rb3
-rw-r--r--lib/gitlab/markdown/label_reference_filter.rb6
-rw-r--r--lib/gitlab/markdown/merge_request_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/reference_filter.rb18
-rw-r--r--lib/gitlab/markdown/snippet_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/user_reference_filter.rb69
-rw-r--r--lib/gitlab/reference_extractor.rb157
-rw-r--r--lib/gitlab/theme.rb39
11 files changed, 148 insertions, 197 deletions
diff --git a/lib/gitlab/gitorious_import/client.rb b/lib/gitlab/gitorious_import/client.rb
index 8cdc3d4afae..1fa89dba448 100644
--- a/lib/gitlab/gitorious_import/client.rb
+++ b/lib/gitlab/gitorious_import/client.rb
@@ -14,7 +14,7 @@ module Gitlab
end
def repos
- @repos ||= repo_names.map { |full_name| Repository.new(full_name) }
+ @repos ||= repo_names.map { |full_name| GitoriousImport::Repository.new(full_name) }
end
def repo(id)
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
index baa97bee9bf..8764f7e474f 100644
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_range_reference_filter.rb
@@ -32,11 +32,8 @@ module Gitlab
# Pattern used to extract commit range references from text
#
- # The beginning and ending SHA1 sums can be between 6 and 40 hex
- # characters, and the range selection can be double- or triple-dot.
- #
# This pattern supports cross-project references.
- COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit_range>\h{6,40}\.{2,3}\h{6,40})/
+ COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit_range>#{CommitRange::PATTERN})/
def call
replace_text_nodes_matching(COMMIT_RANGE_PATTERN) do |content|
@@ -53,52 +50,34 @@ module Gitlab
# links have `gfm` and `gfm-commit_range` class names attached for
# styling.
def commit_range_link_filter(text)
- self.class.references_in(text) do |match, commit_range, project_ref|
+ self.class.references_in(text) do |match, id, project_ref|
project = self.project_from_ref(project_ref)
- from_id, to_id = split_commit_range(commit_range)
+ range = CommitRange.new(id, project)
+
+ if range.valid_commits?
+ push_result(:commit_range, range)
- if valid_range?(project, from_id, to_id)
- url = url_for_commit_range(project, from_id, to_id)
+ url = url_for_commit_range(project, range)
- title = "Commits #{from_id} through #{to_id}"
+ title = range.reference_title
klass = reference_class(:commit_range)
project_ref += '@' if project_ref
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{project_ref}#{commit_range}</a>)
+ class="#{klass}">#{project_ref}#{range}</a>)
else
match
end
end
end
- def split_commit_range(range)
- from_id, to_id = range.split(/\.{2,3}/, 2)
- from_id << "^" if range !~ /\.{3}/
-
- [from_id, to_id]
- end
-
- def commit(id)
- unless @commit_map[id]
- @commit_map[id] = project.commit(id)
- end
-
- @commit_map[id]
- end
-
- def valid_range?(project, from_id, to_id)
- project && project.valid_repo? && commit(from_id) && commit(to_id)
- end
-
- def url_for_commit_range(project, from_id, to_id)
+ def url_for_commit_range(project, range)
h = Rails.application.routes.url_helpers
h.namespace_project_compare_url(project.namespace, project,
- from: from_id, to: to_id,
- only_path: context[:only_path])
+ range.to_param.merge(only_path: context[:only_path]))
end
end
end
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index 66598127f6e..b20b29f5d0c 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -48,6 +48,8 @@ module Gitlab
project = self.project_from_ref(project_ref)
if commit = commit_from_ref(project, commit_ref)
+ push_result(:commit, commit)
+
url = url_for_commit(project, commit)
title = escape_once(commit.link_title)
@@ -57,7 +59,7 @@ module Gitlab
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{project_ref}#{commit_ref}</a>)
+ class="#{klass}">#{project_ref}#{commit.short_id}</a>)
else
match
end
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index c9267cc3e9d..4b360369d37 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -48,6 +48,9 @@ module Gitlab
project = self.project_from_ref(project_ref)
if project && project.issue_exists?(issue)
+ # FIXME (rspeicher): Law of Demeter
+ push_result(:issue, project.issues.where(iid: issue).first)
+
url = url_for_issue(issue, project, only_path: context[:only_path])
title = escape_once("Issue: #{title_for_issue(issue, project)}")
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 4c21192c0d3..a357f28458d 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -52,11 +52,13 @@ module Gitlab
params = label_params(id, name)
if label = project.labels.find_by(params)
- url = url_for_label(project, label)
+ push_result(:label, label)
+ url = url_for_label(project, label)
klass = reference_class(:label)
- %(<a href="#{url}" class="#{klass}">#{render_colored_label(label)}</a>)
+ %(<a href="#{url}"
+ class="#{klass}">#{render_colored_label(label)}</a>)
else
match
end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 40239523cda..7c28fe112ef 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -48,6 +48,8 @@ module Gitlab
project = self.project_from_ref(project_ref)
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}")
klass = reference_class(:merge_request)
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index ef4aa408a7e..a4303d96bef 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -12,7 +12,15 @@ module Gitlab
# :reference_class - Custom CSS class added to reference links.
# :only_path - Generate path-only links.
#
+ # Results:
+ # :references - A Hash of references that were found and replaced.
class ReferenceFilter < HTML::Pipeline::Filter
+ def initialize(*args)
+ super
+
+ result[:references] = Hash.new { |hash, type| hash[type] = [] }
+ end
+
def escape_once(html)
ERB::Util.html_escape_once(html)
end
@@ -29,6 +37,16 @@ module Gitlab
context[:project]
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)
"gfm gfm-#{type} #{context[:reference_class]}".strip
end
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb
index ada67de992b..64a0a2696f7 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/gitlab/markdown/snippet_reference_filter.rb
@@ -48,6 +48,8 @@ module Gitlab
project = self.project_from_ref(project_ref)
if project && snippet = project.snippets.find_by(id: id)
+ push_result(:snippet, snippet)
+
title = escape_once("Snippet: #{snippet.title}")
klass = reference_class(:snippet)
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index 5fc8ed55fe2..28ec041b1d4 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -38,27 +38,11 @@ module Gitlab
# Returns a String with `@user` references replaced with links. All links
# have `gfm` and `gfm-project_member` class names attached for styling.
def user_link_filter(text)
- project = context[:project]
-
- self.class.references_in(text) do |match, user|
- klass = reference_class(:project_member)
-
- if user == 'all'
- url = link_to_all(project)
-
- %(<a href="#{url}" class="#{klass}">@#{user}</a>)
- elsif namespace = Namespace.find_by(path: user)
- if namespace.is_a?(Group)
- if user_can_reference_group?(namespace)
- url = group_url(user, only_path: context[:only_path])
- %(<a href="#{url}" class="#{klass}">@#{user}</a>)
- else
- match
- end
- else
- url = user_url(user, only_path: context[:only_path])
- %(<a href="#{url}" class="#{klass}">@#{user}</a>)
- end
+ self.class.references_in(text) do |match, username|
+ if username == 'all'
+ link_to_all
+ elsif namespace = Namespace.find_by(path: username)
+ link_to_namespace(namespace) || match
else
match
end
@@ -71,17 +55,46 @@ module Gitlab
Rails.application.routes.url_helpers
end
- def group_url(*args)
- urls.group_url(*args)
+ def link_class
+ reference_class(:project_member)
+ end
+
+ def link_to_all
+ project = context[:project]
+
+ # FIXME (rspeicher): Law of Demeter
+ push_result(:user, *project.team.members.flatten)
+
+ url = urls.namespace_project_url(project.namespace, project,
+ only_path: context[:only_path])
+
+ %(<a href="#{url}" class="#{link_class}">@all</a>)
+ end
+
+ def link_to_namespace(namespace)
+ if namespace.is_a?(Group)
+ link_to_group(namespace.path, namespace)
+ else
+ link_to_user(namespace.path, namespace)
+ end
end
- def user_url(*args)
- urls.user_url(*args)
+ def link_to_group(group, namespace)
+ return unless user_can_reference_group?(namespace)
+
+ push_result(:user, *namespace.users)
+
+ url = urls.group_url(group, only_path: context[:only_path])
+
+ %(<a href="#{url}" class="#{link_class}">@#{group}</a>)
end
- def link_to_all(project)
- urls.namespace_project_url(project.namespace, project,
- only_path: context[:only_path])
+ def link_to_user(user, namespace)
+ push_result(:user, namespace.owner)
+
+ url = urls.user_url(user, only_path: context[:only_path])
+
+ %(<a href="#{url}" class="#{link_class}">@#{user}</a>)
end
def user_can_reference_group?(group)
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 949dd5d26b1..e35f848fa6e 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -8,151 +8,70 @@ module Gitlab
@current_user = current_user
end
- def can?(user, action, subject)
- Ability.abilities.allowed?(user, action, subject)
- end
-
def analyze(text)
- text = text.dup
-
- # Remove preformatted/code blocks so that references are not included
- text.gsub!(/^```.*?^```/m, '')
- text.gsub!(/[^`]`[^`]*?`[^`]/, '')
-
- @references = Hash.new { |hash, type| hash[type] = [] }
- parse_references(text)
+ @_text = text.dup
end
- # Given a valid project, resolve the extracted identifiers of the requested type to
- # model objects.
-
def users
- references[:user].uniq.map do |project, identifier|
- if identifier == "all"
- project.team.members.flatten
- elsif namespace = Namespace.find_by(path: identifier)
- if namespace.is_a?(Group)
- namespace.users if can?(current_user, :read_group, namespace)
- else
- namespace.owner
- end
- end
- end.flatten.compact.uniq
+ result = pipeline_result(:user)
+ result.uniq
end
def labels
- references[:label].uniq.map do |project, identifier|
- project.labels.where(id: identifier).first
- end.compact.uniq
+ result = pipeline_result(:label)
+ result.uniq
end
def issues
- references[:issue].uniq.map do |project, identifier|
- if project.default_issues_tracker?
- project.issues.where(iid: identifier).first
- end
- end.compact.uniq
+ # TODO (rspeicher): What about external issues?
+
+ result = pipeline_result(:issue)
+ result.uniq
end
def merge_requests
- references[:merge_request].uniq.map do |project, identifier|
- project.merge_requests.where(iid: identifier).first
- end.compact.uniq
+ result = pipeline_result(:merge_request)
+ result.uniq
end
def snippets
- references[:snippet].uniq.map do |project, identifier|
- project.snippets.where(id: identifier).first
- end.compact.uniq
+ result = pipeline_result(:snippet)
+ result.uniq
end
def commits
- references[:commit].uniq.map do |project, identifier|
- repo = project.repository
- repo.commit(identifier) if repo
- end.compact.uniq
+ result = pipeline_result(:commit)
+ result.uniq
end
def commit_ranges
- references[:commit_range].uniq.map do |project, identifier|
- repo = project.repository
- if repo
- from_id, to_id = identifier.split(/\.{2,3}/, 2)
- [repo.commit(from_id), repo.commit(to_id)]
- end
- end.compact.uniq
+ result = pipeline_result(:commit_range)
+ result.uniq
end
private
- NAME_STR = Gitlab::Regex::NAMESPACE_REGEX_STR
- PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})"
-
- REFERENCE_PATTERN = %r{
- (?<prefix>\W)? # Prefix
- ( # Reference
- @(?<user>#{NAME_STR}) # User name
- |~(?<label>\d+) # Label ID
- |(?<issue>([A-Z\-]+-)\d+) # JIRA Issue ID
- |#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
- |#{PROJ_STR}?!(?<merge_request>\d+) # MR ID
- |\$(?<snippet>\d+) # Snippet ID
- |(#{PROJ_STR}@)?(?<commit_range>[\h]{6,40}\.{2,3}[\h]{6,40}) # Commit range
- |(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID
- )
- (?<suffix>\W)? # Suffix
- }x.freeze
-
- TYPES = %i(user issue label merge_request snippet commit commit_range).freeze
-
- def parse_references(text, project = @project)
- # parse reference links
- text.gsub!(REFERENCE_PATTERN) do |match|
- type = TYPES.detect { |t| $~[t].present? }
-
- actual_project = project
- project_prefix = nil
- project_path = $LAST_MATCH_INFO[:project]
- if project_path
- actual_project = ::Project.find_with_namespace(project_path)
- actual_project = nil unless can?(current_user, :read_project, actual_project)
- project_prefix = project_path
- end
-
- parse_result($LAST_MATCH_INFO, type,
- actual_project, project_prefix) || match
- end
- end
-
- # Called from #parse_references. Attempts to build a gitlab reference
- # link. Returns nil if +type+ is nil, if the match string is an HTML
- # entity, if the reference is invalid, or if the matched text includes an
- # invalid project path.
- def parse_result(match_info, type, project, project_prefix)
- prefix = match_info[:prefix]
- suffix = match_info[:suffix]
-
- return nil if html_entity?(prefix, suffix) || type.nil?
- return nil if project.nil? && !project_prefix.nil?
-
- identifier = match_info[type]
- ref_link = reference_link(type, identifier, project, project_prefix)
-
- if ref_link
- "#{prefix}#{ref_link}#{suffix}"
- else
- nil
- end
- end
-
- # Return true if the +prefix+ and +suffix+ indicate that the matched string
- # is an HTML entity like &amp;
- def html_entity?(prefix, suffix)
- prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
- end
-
- def reference_link(type, identifier, project, _)
- references[type] << [project, identifier]
+ # Instantiate and call HTML::Pipeline with a single reference filter type,
+ # returning the result
+ #
+ # filter_type - Symbol reference type (e.g., :commit, :issue, etc.)
+ #
+ # Returns the results Array for the requested filter type
+ def pipeline_result(filter_type)
+ klass = filter_type.to_s.camelize + 'ReferenceFilter'
+ filter = "Gitlab::Markdown::#{klass}".constantize
+
+ context = {
+ project: project,
+ current_user: current_user,
+ # We don't actually care about the links generated
+ only_path: true
+ }
+
+ pipeline = HTML::Pipeline.new([filter], context)
+ result = pipeline.call(@_text)
+
+ result[:references][filter_type]
end
end
end
diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb
index 43093c7d27e..f0e61aa2e81 100644
--- a/lib/gitlab/theme.rb
+++ b/lib/gitlab/theme.rb
@@ -7,33 +7,44 @@ module Gitlab
COLOR = 5 unless const_defined?(:COLOR)
BLUE = 6 unless const_defined?(:BLUE)
- def self.css_class_by_id(id)
- themes = {
- BASIC => "ui_basic",
- MARS => "ui_mars",
- MODERN => "ui_modern",
- GRAY => "ui_gray",
- COLOR => "ui_color",
- BLUE => "ui_blue"
+ def self.classes
+ @classes ||= {
+ BASIC => 'ui_basic',
+ MARS => 'ui_mars',
+ MODERN => 'ui_modern',
+ GRAY => 'ui_gray',
+ COLOR => 'ui_color',
+ BLUE => 'ui_blue'
}
+ end
+ def self.css_class_by_id(id)
id ||= Gitlab.config.gitlab.default_theme
-
- themes[id]
+ classes[id]
end
- def self.type_css_class_by_id(id)
- types = {
+ def self.types
+ @types ||= {
BASIC => 'light_theme',
MARS => 'dark_theme',
MODERN => 'dark_theme',
GRAY => 'dark_theme',
- COLOR => 'dark_theme'
+ COLOR => 'dark_theme',
+ BLUE => 'light_theme'
}
+ end
+ def self.type_css_class_by_id(id)
id ||= Gitlab.config.gitlab.default_theme
-
types[id]
end
+
+ # Convenience method to get a space-separated String of all the theme
+ # classes that mighty be applied to the `body` element
+ #
+ # Returns a String
+ def self.body_classes
+ (classes.values + types.values).uniq.join(' ')
+ end
end
end