summaryrefslogtreecommitdiff
path: root/lib/banzai/filter/user_reference_filter.rb
blob: 67c24faf991243f4aa167fbde94ff17a9ccbc129 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
require 'banzai'

module Banzai
  module Filter
    # HTML filter that replaces user or group references with links.
    #
    # A special `@all` reference is also supported.
    class UserReferenceFilter < ReferenceFilter
      # 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.reference_pattern) do |match|
          yield match, $~[:user]
        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: LazyReference.new(User, node.attr('data-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, context)
        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
        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
      # 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, link_text: nil)
        self.class.references_in(text) do |match, username|
          if username == 'all'
            link_to_all(link_text: link_text)
          elsif namespace = Namespace.find_by(path: username)
            link_to_namespace(namespace, link_text: link_text) || match
          else
            match
          end
        end
      end

      private

      def urls
        Gitlab::Application.routes.url_helpers
      end

      def link_class
        reference_class(:project_member)
      end

      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 = link_text || User.reference_prefix + 'all'

        link_tag(url, data, text)
      end

      def link_to_namespace(namespace, link_text: nil)
        if namespace.is_a?(Group)
          link_to_group(namespace.path, namespace, link_text: link_text)
        else
          link_to_user(namespace.path, namespace, link_text: link_text)
        end
      end

      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 = link_text || Group.reference_prefix + group

        link_tag(url, data, text)
      end

      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 = link_text || User.reference_prefix + user

        link_tag(url, data, text)
      end

      def link_tag(url, data, text)
        %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
      end
    end
  end
end