From 1d6d757dbd563500671f57f45faa808510a612d1 Mon Sep 17 00:00:00 2001
From: Douwe Maan <douwe@gitlab.com>
Date: Tue, 1 Dec 2015 16:25:56 +0100
Subject: [PATCH] Allow reference format as link href

---
 lib/gitlab/markdown.rb                        |  3 +-
 .../markdown/abstract_reference_filter.rb     | 22 +++++++++-----
 .../external_issue_reference_filter.rb        | 10 +++++--
 lib/gitlab/markdown/label_reference_filter.rb | 10 +++++--
 lib/gitlab/markdown/reference_filter.rb       | 29 ++++++++++++++++++-
 lib/gitlab/markdown/relative_link_filter.rb   |  3 ++
 lib/gitlab/markdown/user_reference_filter.rb  | 28 ++++++++++--------
 7 files changed, 80 insertions(+), 25 deletions(-)

diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index b082bfc434b..886a09f52af 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -178,7 +178,6 @@ module Gitlab
         Gitlab::Markdown::SanitizationFilter,
 
         Gitlab::Markdown::UploadLinkFilter,
-        Gitlab::Markdown::RelativeLinkFilter,
         Gitlab::Markdown::EmojiFilter,
         Gitlab::Markdown::TableOfContentsFilter,
         Gitlab::Markdown::AutolinkFilter,
@@ -193,6 +192,8 @@ module Gitlab
         Gitlab::Markdown::CommitReferenceFilter,
         Gitlab::Markdown::LabelReferenceFilter,
 
+        Gitlab::Markdown::RelativeLinkFilter,
+
         Gitlab::Markdown::TaskListFilter
       ]
     end
diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb
index 37ed423eeda..b044a73ed17 100644
--- a/lib/gitlab/markdown/abstract_reference_filter.rb
+++ b/lib/gitlab/markdown/abstract_reference_filter.rb
@@ -2,7 +2,7 @@ require 'gitlab/markdown'
 
 module Gitlab
   module Markdown
-    # Issues, Snippets, Merge Requests, Commits and Commit Ranges share
+    # Issues, Merge Requests, Snippets, Commits and Commit Ranges share
     # similar functionality in refernce filtering.
     class AbstractReferenceFilter < ReferenceFilter
       include CrossProjectReference
@@ -64,8 +64,13 @@ module Gitlab
           object_link_filter(content, object_class.reference_pattern)
         end
 
-        replace_link_nodes_matching(object_class.link_reference_pattern) do |content|
-          object_link_filter(content, object_class.link_reference_pattern)
+        replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
+          object_link_filter(link, object_class.reference_pattern, link_text: text)
+        end
+
+        replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
+          object_link_filter(text, object_class.link_reference_pattern)
+        end
         end
       end
 
@@ -76,7 +81,7 @@ module Gitlab
       #
       # Returns a String with references replaced with links. All links
       # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
-      def object_link_filter(text, pattern)
+      def object_link_filter(text, pattern, link_text: nil)
         references_in(text, pattern) do |match, id, project_ref, matches|
           project = project_from_ref(project_ref)
 
@@ -88,10 +93,13 @@ module Gitlab
             url = matches[:url] if matches.names.include?("url")
             url ||= url_for_object(object, project)
 
-            text = object.reference_link_text(context[:project])
+            text = link_text
+            unless text
+              text = object.reference_link_text(context[:project])
 
-            extras = object_link_text_extras(object, matches)
-            text += " (#{extras.join(", ")})" if extras.any?
+              extras = object_link_text_extras(object, matches)
+              text += " (#{extras.join(", ")})" if extras.any?
+            end
 
             %(<a href="#{url}" #{data}
                  title="#{title}"
diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb
index 8f86f13976a..14bdf5521fc 100644
--- a/lib/gitlab/markdown/external_issue_reference_filter.rb
+++ b/lib/gitlab/markdown/external_issue_reference_filter.rb
@@ -30,6 +30,10 @@ module Gitlab
         replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
           issue_link_filter(content)
         end
+
+        replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text|
+          issue_link_filter(link, link_text: text)
+        end
       end
 
       # Replace `JIRA-123` issue references in text with links to the referenced
@@ -39,7 +43,7 @@ module Gitlab
       #
       # Returns a String with `JIRA-123` references replaced with links. All
       # links have `gfm` and `gfm-issue` class names attached for styling.
-      def issue_link_filter(text)
+      def issue_link_filter(text, link_text: nil)
         project = context[:project]
 
         self.class.references_in(text) do |match, issue|
@@ -49,9 +53,11 @@ module Gitlab
           klass = reference_class(:issue)
           data  = data_attribute(project: project.id)
 
+          text = link_text || match
+
           %(<a href="#{url}" #{data}
                title="#{title}"
-               class="#{klass}">#{match}</a>)
+               class="#{klass}">#{text}</a>)
         end
       end
 
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 618acb7a578..4d0507b607d 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -30,6 +30,10 @@ module Gitlab
         replace_text_nodes_matching(Label.reference_pattern) do |content|
           label_link_filter(content)
         end
+
+        replace_link_nodes_with_href(Label.reference_pattern) do |link, text|
+          label_link_filter(link, link_text: text)
+        end
       end
 
       # Replace label references in text with links to the label specified.
@@ -38,7 +42,7 @@ module Gitlab
       #
       # Returns a String with label references replaced with links. All links
       # have `gfm` and `gfm-label` class names attached for styling.
-      def label_link_filter(text)
+      def label_link_filter(text, link_text: nil)
         project = context[:project]
 
         self.class.references_in(text) do |match, id, name|
@@ -49,8 +53,10 @@ module Gitlab
             klass = reference_class(:label)
             data = data_attribute(project: project.id, label: label.id)
 
+            text = link_text || render_colored_label(label)
+
             %(<a href="#{url}" #{data}
-                 class="#{klass}">#{render_colored_label(label)}</a>)
+                 class="#{klass}">#{text}</a>)
           else
             match
           end
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index e52633ee74c..2597784c7ae 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -122,7 +122,7 @@ module Gitlab
         doc
       end
 
-      def replace_link_nodes_matching(pattern)
+      def replace_link_nodes_with_text(pattern)
         return doc if project.nil?
 
         doc.search('a').each do |node|
@@ -132,6 +132,9 @@ module Gitlab
           link = node.attr('href')
           text = node.text
 
+          next unless link && text
+
+          link = URI.decode(link)
           # Ignore ending punctionation like periods or commas
           next unless link == text && text =~ /\A#{pattern}/
 
@@ -145,6 +148,30 @@ module Gitlab
         doc
       end
 
+      def replace_link_nodes_with_href(pattern)
+        return doc if project.nil?
+
+        doc.search('a').each do |node|
+          klass = node.attr('class')
+          next if klass && klass.include?('gfm')
+
+          link = node.attr('href')
+          text = node.text
+
+          next unless link && text
+          link = URI.decode(link)
+          next unless link && link =~ /\A#{pattern}\z/
+
+          html = yield link, text
+
+          next if html == link
+
+          node.replace(html)
+        end
+
+        doc
+      end
+
       # Ensure that a :project key exists in context
       #
       # Note that while the key might exist, its value could be nil!
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 632be4d7542..692c51fd324 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -17,6 +17,9 @@ module Gitlab
         return doc unless linkable_files?
 
         doc.search('a').each do |el|
+          klass = el.attr('class')
+          next if klass && klass.include?('gfm')
+          
           process_link_attr el.attribute('href')
         end
 
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index ab5e1f6fe9e..0a20d9c0347 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -52,6 +52,10 @@ module Gitlab
         replace_text_nodes_matching(User.reference_pattern) do |content|
           user_link_filter(content)
         end
+
+        replace_link_nodes_with_href(User.reference_pattern) do |link, text|
+          user_link_filter(link, link_text: text)
+        end
       end
 
       # Replace `@user` user references in text with links to the referenced
@@ -61,12 +65,12 @@ 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)
+      def user_link_filter(text, link_text: nil)
         self.class.references_in(text) do |match, username|
           if username == 'all'
-            link_to_all
+            link_to_all(link_text: link_text)
           elsif namespace = Namespace.find_by(path: username)
-            link_to_namespace(namespace) || match
+            link_to_namespace(namespace, link_text: link_text) || match
           else
             match
           end
@@ -83,36 +87,36 @@ module Gitlab
         reference_class(:project_member)
       end
 
-      def link_to_all
+      def link_to_all(link_text: nil)
         project = context[:project]
         url = urls.namespace_project_url(project.namespace, project,
                                          only_path: context[:only_path])
         data = data_attribute(project: project.id)
-        text = User.reference_prefix + 'all'
+        text = link_text || User.reference_prefix + 'all'
 
         link_tag(url, data, text)
       end
 
-      def link_to_namespace(namespace)
+      def link_to_namespace(namespace, link_text: nil)
         if namespace.is_a?(Group)
-          link_to_group(namespace.path, namespace)
+          link_to_group(namespace.path, namespace, link_text: link_text)
         else
-          link_to_user(namespace.path, namespace)
+          link_to_user(namespace.path, namespace, link_text: link_text)
         end
       end
 
-      def link_to_group(group, namespace)
+      def link_to_group(group, namespace, link_text: nil)
         url = urls.group_url(group, only_path: context[:only_path])
         data = data_attribute(group: namespace.id)
-        text = Group.reference_prefix + group
+        text = link_text || Group.reference_prefix + group
 
         link_tag(url, data, text)
       end
 
-      def link_to_user(user, namespace)
+      def link_to_user(user, namespace, link_text: nil)
         url = urls.user_url(user, only_path: context[:only_path])
         data = data_attribute(user: namespace.owner_id)
-        text = User.reference_prefix + user
+        text = link_text || User.reference_prefix + user
 
         link_tag(url, data, text)
       end
-- 
GitLab