thredded 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b89649352c4f5481a4e2ad05a885e21a2bb9a5d0
4
- data.tar.gz: 4b7bf4007f49a57fbf4a00b176750cb700a8a801
3
+ metadata.gz: 868d81f0b7ff77d6ae58e572377f1001be7131b7
4
+ data.tar.gz: f23376c4be14345f85932348d1b9745322ac642a
5
5
  SHA512:
6
- metadata.gz: d2e4a2b77cd7495e279b80e910c20a28f236e1103974410ec3572cb0b99c482ec662f3eb5951f4f7cda7b4e88f5a8f6cdf4c430c363bcd63cc78384a58a5fae1
7
- data.tar.gz: d36788984d19723bb7607042e2675d3da6a9103b76d2e550a8797e557e53ac4493fc2f6aee8d6496920e0dc24cbbffcd3b6bd9e96357ec3d07eb0dfb0fdc9fb7
6
+ metadata.gz: 11bf79cf05bbf641a8c78540d6d07c2522dbb6ba660de178bdbbe1d98a18958b0edbc4360eaf74b71bb6db71f3f60717eb945326791ae03903f0cf64d1fc780e
7
+ data.tar.gz: 12b1603c89e6e4660d67fb7a6ed90993c42d2edd408a261d8b15f107e201d1a47dbff2b157ad94628e74713cf9d4607f1ae2a10fcbdbf86b36b49a4dac6a5f95
data/CHANGELOG.mkdn CHANGED
@@ -1,3 +1,18 @@
1
+ # v0.6.1
2
+
3
+ This is a minor bugfix release.
4
+
5
+ ## Added
6
+
7
+ * Adds an Activity tab to moderation, with a list of all the forum topics and posts, most recent first.
8
+
9
+ ## Fixed
10
+
11
+ * Moderation history rendering of deleted content.
12
+ * Various bugs in @-mentions and highlighting.
13
+
14
+ See the full list of changes here: https://github.com/thredded/thredded/compare/v0.6.0...v0.6.1.
15
+
1
16
  # 0.6.0 - 2016-06-12
2
17
 
3
18
  **NB:** If updating to this version from 0.5.x, you will need to copy and run [this migration](https://github.com/thredded/thredded/blob/66f64068b9501ff4e8686c95894b6795aae6082f/db/upgrade_migrations/20160611094616_upgrade_v0_5_to_v0_6.rb).
data/README.mkdn CHANGED
@@ -44,7 +44,7 @@ application and not an engine like Thredded.
44
44
  Add the gem to your Gemfile:
45
45
 
46
46
  ```ruby
47
- gem 'thredded', '~> 0.6.0'
47
+ gem 'thredded', '~> 0.6.1'
48
48
  ```
49
49
 
50
50
  Add the Thredded [initializer] to your parent app by running the install generator.
@@ -154,11 +154,12 @@ Thredded does not provide a user's profile page, but it provides a helper for re
154
154
  in your app's user profile page.
155
155
 
156
156
  To use it:
157
+
157
158
  1. Include `Thredded::ApplicationHelper` in the app's helpers module.
158
159
  2. Render the partial like this:
159
160
 
160
161
  ```erb
161
- <%= render 'thredded/users/_posts',
162
+ <%= render 'thredded/users/posts',
162
163
  posts: Thredded.posts_page_view(
163
164
  scope: user.thredded_posts.order_newest_first.limit(5),
164
165
  current_user: current_user) %>
@@ -11,7 +11,8 @@
11
11
  .thredded--pending-moderation &--moderation-navigation--pending,
12
12
  .thredded--moderation-history &--moderation-navigation--history,
13
13
  .thredded--moderation-users &--moderation-navigation--users,
14
- .thredded--moderation-user &--moderation-navigation--users{
14
+ .thredded--moderation-user &--moderation-navigation--users,
15
+ .thredded--moderation-activity &--moderation-navigation--activity {
15
16
  @extend %thredded--nav-tabs--item-current;
16
17
  }
17
18
 
@@ -13,7 +13,8 @@
13
13
  .thredded--pending-moderation &--user-navigation--moderation,
14
14
  .thredded--moderation-history &--user-navigation--moderation,
15
15
  .thredded--moderation-users &--user-navigation--moderation,
16
- .thredded--moderation-user &--user-navigation--moderation {
16
+ .thredded--moderation-user &--user-navigation--moderation,
17
+ .thredded--moderation-activity &--user-navigation--moderation {
17
18
  @extend %thredded--nav-tabs--item-current;
18
19
  }
19
20
 
@@ -1,16 +1,23 @@
1
1
  # frozen_string_literal: true
2
+ require_dependency 'html/pipeline/at_mention_filter'
2
3
  module Thredded
3
4
  class AtNotificationExtractor
4
- # Matches the names in @joe, @"Joe 1", but not email@host.com.
5
- # The matched name is captured and may contain the surrounding quotes.
6
- MATCH_NAME_RE = /(?:^|[\s>])@([\w]+|"[\w ]+")(?=\W|$)/
7
-
8
- def initialize(content)
9
- @content = content
5
+ def initialize(post)
6
+ @post = post
10
7
  end
11
8
 
9
+ # @return [Array<Thredded.user_class>]
12
10
  def run
13
- @content.scan(MATCH_NAME_RE).map(&:first).map { |m| m.start_with?('"') ? m[1..-2] : m }.uniq
11
+ view_context = Thredded::ApplicationController.new.view_context
12
+ # Do not highlight @-mentions at first, because:
13
+ # * When parsing, @-mentions within <a> tags will not be considered.
14
+ # * We can't always generate the user URL here because request.host is not available.
15
+ html = @post.filtered_content(view_context, users_provider: nil)
16
+ HTML::Pipeline::AtMentionFilter.new(
17
+ html,
18
+ view_context: view_context,
19
+ users_provider: -> (user_names) { @post.readers_from_user_names(user_names).to_a }
20
+ ).mentioned_users
14
21
  end
15
22
  end
16
23
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require_dependency 'thredded/at_notification_extractor'
2
3
  module Thredded
3
4
  class AutofollowMentionedUsers
4
5
  def initialize(post)
@@ -12,10 +13,9 @@ module Thredded
12
13
  end
13
14
 
14
15
  def autofollowers
15
- user_names = Thredded::AtNotificationExtractor.new(post.content).run
16
- autofollowers = post.readers_from_user_names(user_names).to_a
17
- autofollowers.delete post.user
18
- exclude_those_opting_out_of_at_notifications(autofollowers)
16
+ autofollowers = Thredded::AtNotificationExtractor.new(post).run
17
+ autofollowers.delete(post.user)
18
+ exclude_those_opting_out_of_at_notifications autofollowers
19
19
  end
20
20
 
21
21
  private
@@ -26,6 +26,19 @@ module Thredded
26
26
  .page(current_page)
27
27
  end
28
28
 
29
+ def activity
30
+ @posts = PostsPageView.new(
31
+ thredded_current_user,
32
+ moderatable_posts
33
+ .order_newest_first
34
+ .preload(:user, :postable)
35
+ .page(current_page)
36
+ )
37
+ if flash[:last_moderated_record_id]
38
+ @last_moderated_record = accessible_post_moderation_records.find(flash[:last_moderated_record_id].to_i)
39
+ end
40
+ end
41
+
29
42
  def moderate_post
30
43
  return head(:bad_request) unless Thredded::Post.moderation_states.include?(params[:moderation_state])
31
44
  flash[:last_moderated_record_id] = ModeratePost.run!(
@@ -25,11 +25,8 @@ module Thredded
25
25
 
26
26
  # @param view_context [Object] the context of the rendering view.
27
27
  # @return [String] formatted and sanitized html-safe post content.
28
- def filtered_content(view_context)
29
- Thredded::ContentFormatter.new(
30
- view_context,
31
- users_provider: -> (names) { readers_from_user_names(names) }
32
- ).format_content(content)
28
+ def filtered_content(view_context, users_provider: -> (names) { readers_from_user_names(names) })
29
+ Thredded::ContentFormatter.new(view_context, users_provider: users_provider).format_content(content)
33
30
  end
34
31
 
35
32
  private
@@ -4,7 +4,7 @@ module Thredded
4
4
  include ModerationState
5
5
 
6
6
  belongs_to :user, class_name: Thredded.user_class, inverse_of: :thredded_user_detail
7
- validates :user_id, presence: true
7
+ validates :user_id, presence: true, uniqueness: true
8
8
 
9
9
  has_many :topics, class_name: 'Thredded::Topic', foreign_key: :user_id, primary_key: :user_id
10
10
  has_many :private_topics, class_name: 'Thredded::PrivateTopic', foreign_key: :user_id, primary_key: :user_id
@@ -3,7 +3,7 @@ module Thredded
3
3
  class UserTopicFollow < ActiveRecord::Base
4
4
  enum reason: [:manual, :posted, :mentioned]
5
5
 
6
- belongs_to :user, inverse_of: :thredded_topic_follows
6
+ belongs_to :user, inverse_of: :thredded_topic_follows, class_name: Thredded.user_class
7
7
  belongs_to :topic, inverse_of: :user_follows
8
8
 
9
9
  validates :user_id, presence: true
@@ -7,6 +7,9 @@
7
7
  <li class="thredded--moderation-navigation--item thredded--moderation-navigation--history">
8
8
  <%= link_to t('thredded.nav.moderation_history'), moderation_history_path %>
9
9
  </li>
10
+ <li class="thredded--moderation-navigation--item thredded--moderation-navigation--activity">
11
+ <%= link_to t('thredded.nav.moderation_activity'), moderation_activity_path %>
12
+ </li>
10
13
  <li class="thredded--moderation-navigation--item thredded--moderation-navigation--users">
11
14
  <%= link_to t('thredded.nav.moderation_users'), users_moderation_path %>
12
15
  </li>
@@ -10,5 +10,10 @@
10
10
  %>
11
11
  <%= render 'thredded/posts_common/content', post: post %>
12
12
  <%= render 'thredded/posts_common/actions', post: post %>
13
+ <% if post.blocked? %>
14
+ <p class="thredded--alert thredded--alert-danger">
15
+ <%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %>
16
+ </p>
17
+ <% end %>
13
18
  <%= render 'post_moderation_actions', post: post %>
14
19
  <% end %>
@@ -23,12 +23,12 @@
23
23
  <%= t('thredded.moderation.posts_content_changed_since_moderation_html', post_url: post_permalink_path(post)) %>
24
24
  </p>
25
25
  <% end %>
26
- <%= t 'thredded.moderation.post_deleted_notice' unless post %>
26
+ <%= content_tag :em, t('thredded.moderation.post_deleted_notice') unless post %>
27
27
  </header>
28
28
  <article class="thredded--post">
29
29
  <% post_user_link = capture do %>
30
- <% if post.user %>
31
- <%= link_to post.user, user_moderation_path(post.user.id) %>
30
+ <% if record.post_user %>
31
+ <%= link_to record.post_user, user_moderation_path(record.post_user.id) %>
32
32
  <% else %>
33
33
  <%= safe_join [record.post_user_name, content_tag(:em, t('thredded.null_user_name'))].compact, ', ' %>
34
34
  <% end %>
@@ -3,5 +3,10 @@
3
3
  <%= render 'thredded/posts_common/header_with_topic', post: post %>
4
4
  <%= render 'thredded/posts_common/content', post: post %>
5
5
  <%= render 'thredded/posts_common/actions', post: post %>
6
+ <% if post.blocked? %>
7
+ <p class="thredded--alert thredded--alert-danger">
8
+ <%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %>
9
+ </p>
10
+ <% end %>
6
11
  <%= render 'post_moderation_actions', post: post %>
7
12
  <% end %>
@@ -0,0 +1,18 @@
1
+ <% content_for :thredded_page_title, t('thredded.nav.moderation') %>
2
+ <% content_for :thredded_page_id, 'thredded--moderation-activity' %>
3
+ <%= render 'nav' %>
4
+
5
+ <%= thredded_page do %>
6
+ <h1><%= t 'thredded.recent_activity' %></h1>
7
+ <%= content_tag :section, class: 'thredded--main-section' do %>
8
+ <% if @last_moderated_record %>
9
+ <div class="thredded--moderated-notice">
10
+ <%= render 'post_moderation_record', post_moderation_record: @last_moderated_record %>
11
+ </div>
12
+ <% end %>
13
+ <% if @posts.present? %>
14
+ <%= render partial: 'post', collection: @posts %>
15
+ <%= paginate @posts %>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  en:
3
3
  thredded:
4
+ recent_activity: Recent activity
4
5
  errors:
5
6
  login_required: Please sign in first.
6
7
  not_authorized: You are not authorized to access this page.
@@ -9,7 +10,7 @@ en:
9
10
  form:
10
11
  update: Update
11
12
  content_moderation_states:
12
- content_blocked_notice: Blocked by a moderator
13
+ content_blocked_notice: Blocked
13
14
  content_blocked_notice_with_record_html: Blocked by %{moderator} %{time_ago}
14
15
  messageboard:
15
16
  create: Create a New Messageboard
@@ -51,6 +52,7 @@ en:
51
52
  moderation_history: History
52
53
  moderation_pending: Pending
53
54
  moderation_users: Users
55
+ moderation_activity: Activity
54
56
  private_topics: Private Messages
55
57
  settings: Notification Settings
56
58
  null_user_name: Deleted user
@@ -140,7 +142,7 @@ en:
140
142
  users:
141
143
  last_active_html: "Last active %{time_ago}"
142
144
  posted_in_topic_html: "Posted in %{topic_link}"
143
- recent_activity: Recent activity
145
+ recent_activity: :thredded.recent_activity
144
146
  started_topic_html: "Started %{topic_link}"
145
147
  user_posted_in_topic_html: "%{user_link} posted in %{topic_link}"
146
148
  user_since_html: "User since %{time_ago}"
@@ -2,7 +2,7 @@
2
2
  pt-BR:
3
3
  thredded:
4
4
  content_moderation_states:
5
- content_blocked_notice: Bloqueado por um moderador
5
+ content_blocked_notice: Bloqueado
6
6
  content_blocked_notice_with_record_html: Bloqueados por %{moderator} %{time_ago}
7
7
  errors:
8
8
  login_required: Por favor, autentique-se primeiro.
@@ -48,6 +48,7 @@ pt-BR:
48
48
  edit_private_topic: :thredded.nav.edit_topic
49
49
  edit_topic: Editar
50
50
  moderation: Moderação
51
+ moderation_activity: Atividade
51
52
  moderation_history: História
52
53
  moderation_pending: Pendente
53
54
  moderation_users: Usuários
@@ -108,6 +109,7 @@ pt-BR:
108
109
  create_btn: Inicie sua primeira conversa privada
109
110
  title: Você não possui mensagens privadas.
110
111
  updated_notice: Título atualizado
112
+ recent_activity: Atividade recente
111
113
  search:
112
114
  form:
113
115
  btn_submit: :thredded.search.form.label
@@ -144,7 +146,7 @@ pt-BR:
144
146
  posts_count:
145
147
  one: Postado uma vez
146
148
  other: vezes %{count} Publicado
147
- recent_activity: Atividade recente
149
+ recent_activity: :thredded.recent_activity
148
150
  started_topic_html: Começou %{topic_link}
149
151
  started_topics_count:
150
152
  one: Começou um tópico
data/config/routes.rb CHANGED
@@ -35,6 +35,7 @@ Thredded::Engine.routes.draw do
35
35
  get '/history(/page-:page)', action: :history, as: :moderation_history
36
36
  get '/users(/page-:page)', action: :users, as: :users_moderation
37
37
  get '/users/:id(/page-:page)', action: :user, as: :user_moderation
38
+ get '/activity(/page-:page)', action: :activity, as: :moderation_activity
38
39
  end
39
40
  post '', action: :moderate_post, as: :moderate_post
40
41
  post '/user/:id', action: :moderate_user, as: :moderate_user
data/heroku.gemfile.lock CHANGED
@@ -9,7 +9,7 @@ GIT
9
9
  PATH
10
10
  remote: .
11
11
  specs:
12
- thredded (0.6.0)
12
+ thredded (0.6.1)
13
13
  active_record_union (>= 1.1.1)
14
14
  autoprefixer-rails
15
15
  autosize-rails
@@ -30,7 +30,6 @@ PATH
30
30
  rails (>= 4.2.0)
31
31
  rails-timeago
32
32
  rb-gravatar
33
- rinku
34
33
  sanitize
35
34
  sass (>= 3.4.21)
36
35
  select2-rails (~> 3.5)
@@ -212,7 +211,6 @@ GEM
212
211
  rb-gravatar (1.0.5)
213
212
  ref (2.0.0)
214
213
  request_store (1.3.1)
215
- rinku (2.0.0)
216
214
  rollbar (2.11.5)
217
215
  multi_json
218
216
  sanitize (4.0.1)
@@ -228,7 +226,7 @@ GEM
228
226
  tilt (>= 1.1, < 3)
229
227
  select2-rails (3.5.10)
230
228
  thor (~> 0.14)
231
- sprockets (3.6.0)
229
+ sprockets (3.6.1)
232
230
  concurrent-ruby (~> 1.0)
233
231
  rack (> 1, < 3)
234
232
  sprockets-es6 (0.9.0)
@@ -1,23 +1,76 @@
1
1
  # frozen_string_literal: true
2
- require 'thredded/at_users'
3
-
4
2
  module HTML
5
3
  class Pipeline
6
4
  class AtMentionFilter < Filter
5
+ DEFAULT_IGNORED_ANCESTOR_TAGS = %w(pre code tt a style).freeze
6
+
7
7
  # @param context [Hash]
8
8
  # @options context :users_provider [#call(usernames)] given usernames, returns a list of users.
9
- def initialize(text, context = nil, result = nil)
10
- super text, context, result
11
- @text = text.to_s.delete("\r")
9
+ def initialize(doc, context = nil, result = nil)
10
+ super doc, context, result
12
11
  @users_provider = context[:users_provider]
13
12
  @view_context = context[:view_context]
14
13
  end
15
14
 
16
15
  def call
17
- return html unless @users_provider
18
- html = Thredded::AtUsers.render(@text, @users_provider, @view_context)
19
- html.rstrip!
20
- html
16
+ return doc unless @users_provider
17
+ process_text_nodes! do |node|
18
+ content = node.to_html
19
+ next unless content.include?('@')
20
+ highlight! content
21
+ node.replace content
22
+ end
23
+ doc
24
+ end
25
+
26
+ # @return [Array<Thredded.user_class>] users that were @-mentioned
27
+ def mentioned_users
28
+ return [] unless @users_provider
29
+ names = []
30
+ process_text_nodes! { |node| names.concat mentioned_names(node.to_html) }
31
+ names.uniq!
32
+ @users_provider.call(names)
33
+ end
34
+
35
+ private
36
+
37
+ MATCH_NAME_RE = /(?:^|[\s>])@([\w]+|"[\w ]+")(?=\W|$)/
38
+
39
+ def mentioned_names(text_node_html)
40
+ text_node_html.scan(MATCH_NAME_RE).map(&:first).map { |m| m.start_with?('"') ? m[1..-2] : m }
41
+ end
42
+
43
+ def highlight!(text_node_html)
44
+ names = mentioned_names(text_node_html)
45
+ return unless names.present?
46
+ @users_provider.call(names).each do |user|
47
+ name = user.to_s
48
+ maybe_quoted_name = name.include?(' ') ? %("#{name}") : name
49
+ url = Thredded.user_path(@view_context, user)
50
+ text_node_html.gsub!(
51
+ /(^|[\s>])(@#{Regexp.escape maybe_quoted_name})([^a-z\d]|$)/i,
52
+ %(\\1<a href="#{ERB::Util.html_escape url}">@#{ERB::Util.html_escape maybe_quoted_name}</a>\\3)
53
+ )
54
+ end
55
+ end
56
+
57
+ # Yields text nodes that should be processed.
58
+ def process_text_nodes!
59
+ doc.search('.//text()').each do |node|
60
+ next if has_ancestor?(node, ignored_ancestor_tags)
61
+ yield node
62
+ end
63
+ end
64
+
65
+ # Return ancestor tags to stop the at-mention highlighting.
66
+ #
67
+ # @return [Array<String>] Ancestor tags.
68
+ def ignored_ancestor_tags
69
+ if @context[:ignored_ancestor_tags]
70
+ DEFAULT_IGNORED_ANCESTOR_TAGS | @context[:ignored_ancestor_tags]
71
+ else
72
+ DEFAULT_IGNORED_ANCESTOR_TAGS
73
+ end
21
74
  end
22
75
  end
23
76
  end
@@ -43,13 +43,12 @@ module Thredded
43
43
  mattr_accessor :pipeline_filters
44
44
 
45
45
  self.pipeline_filters = [
46
- HTML::Pipeline::AtMentionFilter,
47
46
  HTML::Pipeline::VimeoFilter,
48
47
  HTML::Pipeline::YoutubeFilter,
49
48
  HTML::Pipeline::BbcodeFilter,
50
49
  HTML::Pipeline::MarkdownFilter,
50
+ HTML::Pipeline::AtMentionFilter,
51
51
  HTML::Pipeline::EmojiFilter,
52
- HTML::Pipeline::AutolinkFilter,
53
52
  HTML::Pipeline::SanitizationFilter,
54
53
  ].freeze
55
54
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Thredded
3
- VERSION = '0.6.0'
3
+ VERSION = '0.6.1'
4
4
  end
data/thredded.gemspec CHANGED
@@ -36,7 +36,6 @@ Gem::Specification.new do |s|
36
36
  # html-pipeline dependencies, see https://github.com/jch/html-pipeline#dependencies
37
37
  s.add_dependency 'gemoji'
38
38
  s.add_dependency 'github-markdown'
39
- s.add_dependency 'rinku'
40
39
  s.add_dependency 'sanitize'
41
40
 
42
41
  # frontend
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thredded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Oliveira
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-06-12 00:00:00.000000000 Z
12
+ date: 2016-06-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bbcoder
@@ -235,20 +235,6 @@ dependencies:
235
235
  - - ">="
236
236
  - !ruby/object:Gem::Version
237
237
  version: '0'
238
- - !ruby/object:Gem::Dependency
239
- name: rinku
240
- requirement: !ruby/object:Gem::Requirement
241
- requirements:
242
- - - ">="
243
- - !ruby/object:Gem::Version
244
- version: '0'
245
- type: :runtime
246
- prerelease: false
247
- version_requirements: !ruby/object:Gem::Requirement
248
- requirements:
249
- - - ">="
250
- - !ruby/object:Gem::Version
251
- version: '0'
252
238
  - !ruby/object:Gem::Dependency
253
239
  name: sanitize
254
240
  requirement: !ruby/object:Gem::Requirement
@@ -788,6 +774,7 @@ files:
788
774
  - app/views/thredded/moderation/_user_moderation_state.html.erb
789
775
  - app/views/thredded/moderation/_user_post.html.erb
790
776
  - app/views/thredded/moderation/_users_search_form.html.erb
777
+ - app/views/thredded/moderation/activity.html.erb
791
778
  - app/views/thredded/moderation/history.html.erb
792
779
  - app/views/thredded/moderation/pending.html.erb
793
780
  - app/views/thredded/moderation/user.html.erb
@@ -871,7 +858,6 @@ files:
871
858
  - lib/html/pipeline/bbcode_filter.rb
872
859
  - lib/tasks/thredded_tasks.rake
873
860
  - lib/thredded.rb
874
- - lib/thredded/at_users.rb
875
861
  - lib/thredded/content_formatter.rb
876
862
  - lib/thredded/engine.rb
877
863
  - lib/thredded/errors.rb
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- module Thredded
3
- class AtUsers
4
- # @param users_provider [#call(usernames)] given usernames, returns a list of users.
5
- def self.render(content, users_provider, view_context)
6
- at_names = AtNotificationExtractor.new(content).run
7
-
8
- if at_names.any?
9
- members = users_provider.call(at_names)
10
-
11
- members.each do |member|
12
- member_path = Thredded.user_path(view_context, member)
13
- name = member.to_s
14
- content.gsub!(/(^|[\s>])(@#{name.include?(' ') ? %("#{name}") : name})\b/i,
15
- %(\1<a href="#{ERB::Util.html_escape member_path}">@#{ERB::Util.html_escape name}</a>))
16
- end
17
- end
18
-
19
- content
20
- end
21
- end
22
- end