diff options
Diffstat (limited to 'lib/gitlab/markdown/user_reference_filter.rb')
-rw-r--r-- | lib/gitlab/markdown/user_reference_filter.rb | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb new file mode 100644 index 00000000000..eaf4094338b --- /dev/null +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -0,0 +1,117 @@ +require 'html/pipeline' + +module Gitlab + module Markdown + # HTML filter that replaces user or group references with links. References + # within <pre>, <code>, <a>, and <style> elements are ignored. + # + # A special `@all` reference is also supported. + # + # Context options: + # :project (required) - Current project. + # :reference_class - Custom CSS class added to reference links. + # :only_path - Generate path-only links. + # + class UserReferenceFilter < HTML::Pipeline::Filter + # Public: Find `@user` user references in text + # + # UserReferenceFilter.references_in(text) do |match, username| + # "<a href=...>@#{user}</a>" + # end + # + # text - String text to search. + # + # Yields the String match, and the String user name. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(USER_PATTERN) do |match| + yield match, $~[:user] + end + end + + # Pattern used to extract `@user` user references from text + USER_PATTERN = /@(?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})/ + + # Don't look for references in text nodes that are children of these + # elements. + IGNORE_PARENTS = %w(pre code a style).to_set + + def call + doc.search('text()').each do |node| + content = node.to_html + + next if project.nil? + next unless content.match(USER_PATTERN) + next if has_ancestor?(node, IGNORE_PARENTS) + + html = user_link_filter(content) + + next if html == content + + node.replace(html) + end + + doc + end + + def validate + needs :project + end + + # Replace `@user` user references in text with links to the referenced + # user's profile page. + # + # text - String text to replace references in. + # + # 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 = "gfm gfm-project_member #{context[:reference_class]}".strip + + 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) + url = group_url(user, only_path: context[:only_path]) + else + url = user_url(user, only_path: context[:only_path]) + end + + %(<a href="#{url}" class="#{klass}">@#{user}</a>) + else + match + end + end + end + + def project + context[:project] + end + + # TODO (rspeicher): Cleanup + def group_url(*args) + h = Rails.application.routes.url_helpers + h.group_url(*args) + end + + # TODO (rspeicher): Cleanup + def user_url(*args) + h = Rails.application.routes.url_helpers + h.user_url(*args) + end + + # TODO (rspeicher): Cleanup + def link_to_all(project) + h = Rails.application.routes.url_helpers + h.namespace_project_url(project.namespace, project, + only_path: context[:only_path]) + end + end + end +end |