thredded 0.16.16 → 1.0.0

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -7
  3. data/app/assets/javascripts/thredded/components/preview_area.es6 +3 -1
  4. data/app/controllers/concerns/thredded/render_preview.rb +6 -0
  5. data/app/controllers/thredded/application_controller.rb +5 -10
  6. data/app/controllers/thredded/topics_controller.rb +0 -6
  7. data/app/helpers/thredded/application_helper.rb +1 -1
  8. data/app/helpers/thredded/urls_helper.rb +1 -1
  9. data/app/models/concerns/thredded/post_common.rb +5 -4
  10. data/app/models/concerns/thredded/topic_common.rb +1 -1
  11. data/app/models/concerns/thredded/user_topic_read_state_common.rb +5 -5
  12. data/app/models/thredded/messageboard.rb +3 -4
  13. data/app/models/thredded/notifications_for_followed_topics.rb +1 -1
  14. data/app/models/thredded/post.rb +3 -2
  15. data/app/models/thredded/post_moderation_record.rb +8 -9
  16. data/app/models/thredded/private_post.rb +2 -2
  17. data/app/models/thredded/private_topic.rb +4 -2
  18. data/app/models/thredded/topic.rb +2 -2
  19. data/app/models/thredded/user_detail.rb +1 -1
  20. data/app/models/thredded/user_extender.rb +1 -1
  21. data/app/view_models/thredded/posts_page_view.rb +10 -9
  22. data/app/view_models/thredded/private_topics_page_view.rb +10 -6
  23. data/app/view_models/thredded/topic_posts_page_view.rb +16 -3
  24. data/app/view_models/thredded/topics_page_view.rb +9 -6
  25. data/app/views/layouts/thredded/application.html.erb +2 -2
  26. data/app/views/thredded/messageboards/_form.html.erb +3 -3
  27. data/app/views/thredded/posts_common/_content.html.erb +1 -1
  28. data/app/views/thredded/posts_common/actions/_quote.html.erb +1 -1
  29. data/app/views/thredded/shared/_page.html.erb +1 -1
  30. data/app/views/thredded/shared/nav/_standalone.html.erb +2 -2
  31. data/bin/create_migration_fixture +23 -0
  32. data/config/i18n-tasks.yml +5 -0
  33. data/config/locales/de.yml +2 -0
  34. data/config/locales/en.yml +3 -1
  35. data/config/locales/es.yml +2 -0
  36. data/config/locales/fr.yml +2 -0
  37. data/config/locales/it.yml +4 -2
  38. data/config/locales/pl.yml +2 -0
  39. data/config/locales/pt-BR.yml +2 -0
  40. data/config/locales/ru.yml +2 -0
  41. data/config/locales/zh-CN.yml +2 -0
  42. data/db/upgrade_migrations/20180110200009_upgrade_thredded_v0_14_to_v0_15.rb +1 -1
  43. data/lib/generators/thredded/install/templates/initializer.rb +2 -2
  44. data/lib/thredded/arel_compat.rb +0 -41
  45. data/lib/thredded/base_migration.rb +1 -1
  46. data/lib/thredded/collection_to_strings_with_cache_renderer.rb +33 -31
  47. data/lib/thredded/compat.rb +35 -0
  48. data/lib/thredded/database_seeder.rb +11 -4
  49. data/lib/thredded/db_tools.rb +2 -4
  50. data/lib/thredded/email_transformer.rb +2 -2
  51. data/lib/thredded/version.rb +1 -1
  52. data/lib/thredded.rb +3 -23
  53. metadata +39 -27
  54. data/bin/rubocop +0 -18
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # edit constants in lib/thredded/db_tools.rb
4
+ # git checkout v0.x.0 -b create-0.x-migration-fixture
5
+ # bundle
6
+
7
+ DIR=$(pwd)
8
+
9
+ cd spec/dummy && DB=sqlite3 rails db:environment:set RAILS_ENV=development
10
+ cd $DIR
11
+ DB=sqlite3 rake db:drop db:create db:migrate db:miniseed_dump
12
+
13
+ cd spec/dummy && DB=postgresql rails db:environment:set RAILS_ENV=development
14
+ cd $DIR
15
+ DB=postgresql rake db:drop db:create db:migrate db:miniseed_dump
16
+
17
+ cd spec/dummy && DB=mysql2 rails db:environment:set RAILS_ENV=development
18
+ cd $DIR
19
+ DB=mysql2 rake db:drop db:create db:migrate db:miniseed_dump
20
+
21
+ git add `pwd`/spec/migration/*.dump
22
+
23
+ git commit -em "add sample data for 0.8 base for migration spec"
@@ -15,3 +15,8 @@ data:
15
15
  yaml:
16
16
  write:
17
17
  line_width: 110
18
+
19
+ ## Exclude these keys from the `i18n-tasks check-consistent-interpolations` report:
20
+ ignore_inconsistent_interpolations:
21
+ - 'thredded.users.posts_count.one'
22
+ - 'thredded.users.started_topics_count.one'
@@ -128,6 +128,8 @@ de:
128
128
  moderation_users: Nutzer
129
129
  private_topics: Private Unterhaltungen
130
130
  settings: Benachrichtigungseinstellungen
131
+ sign_in: Anmelden
132
+ sign_out: Ausloggen
131
133
  unread_topics: Ungelesen
132
134
  null_user_name: Gelöschte Nutzer
133
135
  posts:
@@ -127,6 +127,8 @@ en:
127
127
  moderation_users: Users
128
128
  private_topics: Private Messages
129
129
  settings: Notification Settings
130
+ sign_in: Sign In / Register
131
+ sign_out: Sign Out
130
132
  unread_topics: Unread
131
133
  null_user_name: Deleted user
132
134
  posts:
@@ -262,7 +264,7 @@ en:
262
264
  last_active_html: Last active %{time_ago}
263
265
  posted_in_topic_html: Posted in %{topic_link}
264
266
  posts_count:
265
- one: Posted %{count} time
267
+ one: Posted once
266
268
  other: Posted %{count} times
267
269
  recent_activity: :thredded.recent_activity
268
270
  send_private_message: Send private message
@@ -129,6 +129,8 @@ es:
129
129
  moderation_users: Usuarios
130
130
  private_topics: Mensajes Privados
131
131
  settings: Ajuste de Notificaciones
132
+ sign_in: Registrarse
133
+ sign_out: Desconectar
132
134
  unread_topics: No leído
133
135
  null_user_name: Usuario eliminado
134
136
  posts:
@@ -127,6 +127,8 @@ fr:
127
127
  moderation_users: Utilisateurs
128
128
  private_topics: Messages privés
129
129
  settings: Paramètres de notifications
130
+ sign_in: Se connecter
131
+ sign_out: Déconnexion
130
132
  unread_topics: Non lu
131
133
  null_user_name: Utilisateur effacé
132
134
  posts:
@@ -127,6 +127,8 @@ it:
127
127
  moderation_users: Utenti
128
128
  private_topics: Messaggi Privati
129
129
  settings: Impostazioni Notifiche
130
+ sign_in: Entra
131
+ sign_out: Esci
130
132
  unread_topics: Non letto
131
133
  null_user_name: Utente cancellato
132
134
  posts:
@@ -266,13 +268,13 @@ it:
266
268
  last_active_html: Ultima attività %{time_ago}
267
269
  posted_in_topic_html: Ha scritto in %{topic_link}
268
270
  posts_count:
269
- one: Ha commentato %{count} volta
271
+ one: Ha commentato una volta
270
272
  other: Ha commentato %{count} volte
271
273
  recent_activity: :thredded.recent_activity
272
274
  send_private_message: Invia un messaggio privato
273
275
  started_topic_html: Ha iniziato %{topic_link}
274
276
  started_topics_count:
275
- one: Ha iniziato %{count} discussione
277
+ one: Ha iniziato una discussione
276
278
  other: Ha iniziato %{count} discussioni
277
279
  user_posted_in_topic_html: "%{user_link} ha commentato in %{topic_link}"
278
280
  user_since_html: Utente da %{time_ago}
@@ -127,6 +127,8 @@ pl:
127
127
  moderation_users: Użytkownicy
128
128
  private_topics: Prywatne wiadomości
129
129
  settings: Ustawienia powiadomień
130
+ sign_in: Zaloguj
131
+ sign_out: Wyloguj się
130
132
  unread_topics: Nieprzeczytane
131
133
  null_user_name: Usunięci użytkownicy
132
134
  posts:
@@ -129,6 +129,8 @@ pt-BR:
129
129
  moderation_users: Usuários
130
130
  private_topics: Mensagens Privadas
131
131
  settings: Configurações de Notificação
132
+ sign_in: Entrar / Cadastrar
133
+ sign_out: Sair
132
134
  unread_topics: Não lida
133
135
  null_user_name: Usuário deletado
134
136
  posts:
@@ -125,6 +125,8 @@ ru:
125
125
  moderation_users: Пользователи
126
126
  private_topics: Личное
127
127
  settings: Настройки
128
+ sign_in: Войти / Зарегистрироваться
129
+ sign_out: Выход
128
130
  unread_topics: Непрочитанные
129
131
  null_user_name: Пользователь удален
130
132
  posts:
@@ -120,6 +120,8 @@ zh-CN:
120
120
  moderation_users: 用户
121
121
  private_topics: 私人对话
122
122
  settings: 通知设置
123
+ sign_in: 登录/注册
124
+ sign_out: 登出
123
125
  unread_topics: 未读
124
126
  null_user_name: 删除用户
125
127
  posts:
@@ -86,6 +86,6 @@ class UpgradeThreddedV014ToV015 < Thredded::BaseMigration
86
86
  def remove_string_limit(table, column, type: :text, indices: [])
87
87
  indices.each { |(_, options)| remove_index table, name: options[:name] }
88
88
  change_column table, column, type, limit: nil
89
- indices.each { |args| add_index table, *args }
89
+ indices.each { |(columns, options)| add_index table, columns, **options }
90
90
  end
91
91
  end
@@ -29,8 +29,8 @@ Thredded.user_path = ->(user) {
29
29
  # This method is used by Thredded controllers and views to fetch the currently signed-in user
30
30
  Thredded.current_user_method = :"current_#{Thredded.user_class_name.demodulize.underscore}"
31
31
 
32
- # User avatar URL. rb-gravatar gem is used by default:
33
- Thredded.avatar_url = ->(user) { Gravatar.src(user.email, 156, 'mm') }
32
+ # User avatar URL. rails_gravatar gem is used by default:
33
+ Thredded.avatar_url = ->(user) { RailsGravatar.src(user.email, 156, 'mm') }
34
34
 
35
35
  # ==> Permissions Configuration
36
36
  # By default, thredded uses a simple permission model, where all the users can post to all message boards,
@@ -1,40 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- unless Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2 || Rails::VERSION::MAJOR > 5
4
- require 'thredded/rails_lt_5_2_arel_case_node.rb'
5
- end
6
-
7
3
  module Thredded
8
4
  module ArelCompat
9
5
  module_function
10
6
 
11
- # On Rails >= 5, this method simply returns `relation.pluck(*columns)`.
12
- #
13
- # Rails 4 `pluck` does not support Arel nodes and attributes. This method does.
14
- #
15
- # This is an external method because monkey-patching `pluck` would
16
- # have compatibility issues, see:
17
- #
18
- # https://github.com/thredded/thredded/issues/842
19
- # https://blog.newrelic.com/engineering/ruby-agent-module-prepend-alias-method-chains/
20
- if Rails::VERSION::MAJOR > 4
21
- def pluck(relation, *columns)
22
- relation.pluck(*columns)
23
- end
24
- else
25
- def pluck(relation, *columns)
26
- relation.pluck(*columns.map do |n|
27
- if n.is_a?(Arel::Node)
28
- Arel.sql(n.to_sql)
29
- elsif n.is_a?(Arel::Attributes::Attribute)
30
- n.name
31
- else
32
- n
33
- end
34
- end)
35
- end
36
- end
37
-
38
7
  # @param [#connection] engine
39
8
  # @param [Arel::Nodes::Node] a integer node
40
9
  # @param [Arel::Nodes::Node] b integer node
@@ -46,15 +15,5 @@ module Thredded
46
15
  Arel::Nodes::Division.new(a, b)
47
16
  end
48
17
  end
49
-
50
- if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2 || Rails::VERSION::MAJOR > 5
51
- def true_value(_engine)
52
- true
53
- end
54
- else
55
- def true_value(engine)
56
- engine.connection.adapter_name =~ /sqlite|mysql|mariadb/i ? 1 : true
57
- end
58
- end
59
18
  end
60
19
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- class BaseMigration < (Thredded.rails_gte_51? ? ActiveRecord::Migration[5.1] : ActiveRecord::Migration)
4
+ class BaseMigration < ActiveRecord::Migration[5.1]
5
5
  protected
6
6
 
7
7
  def user_id_type
@@ -22,9 +22,11 @@ module Thredded
22
22
  def render_collection_to_strings_with_cache( # rubocop:disable Metrics/ParameterLists
23
23
  view_context, collection:, partial:, expires_in:, render_threads: self.class.render_threads, locals: {}, **opts
24
24
  )
25
- template = @lookup_context.find_template(partial, [], true, locals, {})
25
+ template = @lookup_context.find_template(partial, [], true, locals.keys, {})
26
26
  collection = collection.to_a
27
- instrument(:collection, identifier: template.identifier, count: collection.size) do |instrumentation_payload|
27
+ ActiveSupport::Notifications.instrument(:collection,
28
+ identifier: template.identifier,
29
+ count: collection.size) do |instrumentation_payload|
28
30
  return [] if collection.blank?
29
31
 
30
32
  # Result is a hash with the key represents the
@@ -84,48 +86,48 @@ module Thredded
84
86
  # https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L379
85
87
  # https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L348-L356
86
88
  opts[:locals] = opts[:locals].dup if opts[:locals]
87
- render_partials_serial(view_context.dup, slice, opts)
89
+ ActiveRecord::Base.connection_pool.with_connection do
90
+ render_partials_serial(view_context.dup, slice, opts)
91
+ end
88
92
  end
89
93
  end.flat_map(&:value)
90
94
  end
91
95
  end
92
96
 
93
- # @param [Array<Object>] collection
94
- # @param [Hash] opts
95
- # @param view_context
96
- # @return [Array<String>]
97
- def render_partials_serial(view_context, collection, opts)
98
- partial_renderer = ActionView::PartialRenderer.new(@lookup_context)
99
- collection.map { |object| render_partial(partial_renderer, view_context, opts.merge(object: object)) }
100
- end
101
-
102
- if Rails::VERSION::MAJOR >= 5
103
- def collection_cache
104
- ActionView::PartialRenderer.collection_cache
97
+ if Thredded::Compat.rails_gte_61?
98
+ # @param [Array<Object>] collection
99
+ # @param [Hash] opts
100
+ # @param view_context
101
+ # @return [Array<String>]
102
+ def render_partials_serial(view_context, collection, opts)
103
+ # https://github.com/rails/rails/pull/38594
104
+ collection.map do |object|
105
+ renderer = ActionView::ObjectRenderer.new(@lookup_context, opts)
106
+ renderer.render_object_with_partial(object, opts[:partial], view_context, nil).body
107
+ end
105
108
  end
106
109
  else
107
- def collection_cache
108
- Rails.application.config.action_controller.cache_store
110
+ def render_partials_serial(view_context, collection, opts)
111
+ partial_renderer = ActionView::PartialRenderer.new(@lookup_context)
112
+ collection.map { |object| render_partial(partial_renderer, view_context, **opts.merge(object: object)) }
109
113
  end
110
114
  end
111
115
 
112
- if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2)
113
- def combined_fragment_cache_key(view, key)
114
- view.combined_fragment_cache_key(key)
115
- end
116
- elsif Rails::VERSION::MAJOR >= 5
117
- def combined_fragment_cache_key(view, key)
118
- view.fragment_cache_key(key)
119
- end
120
- else
121
- def combined_fragment_cache_key(view, key)
122
- view.controller.fragment_cache_key(key)
123
- end
116
+ def collection_cache
117
+ ActionView::PartialRenderer.collection_cache
118
+ end
119
+
120
+ def combined_fragment_cache_key(view, key)
121
+ view.combined_fragment_cache_key(key)
124
122
  end
125
123
 
126
- if Rails::VERSION::MAJOR >= 6
124
+ if Thredded::Compat.rails_gte_60?
127
125
  def cache_fragment_name(view, key, virtual_path:, digest_path:)
128
- view.cache_fragment_name(key, virtual_path: virtual_path, digest_path: digest_path)
126
+ if Thredded::Compat.rails_gte_61?
127
+ view.cache_fragment_name(key, digest_path: digest_path)
128
+ else
129
+ view.cache_fragment_name(key, virtual_path: virtual_path, digest_path: digest_path)
130
+ end
129
131
  end
130
132
 
131
133
  def digest_path_from_template(view, template)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ module Compat
5
+ class << self
6
+ # @api private
7
+ def rails_gte_60?
8
+ @rails_gte_60 = (Rails.gem_version >= Gem::Version.new('6.0.0')) if @rails_gte_60.nil?
9
+ @rails_gte_60
10
+ end
11
+
12
+ # @api private
13
+ def rails_gte_61?
14
+ @rails_gte_61 = (Rails.gem_version >= Gem::Version.new('6.1.0')) if @rails_gte_61.nil?
15
+ @rails_gte_61
16
+ end
17
+
18
+ if Rails.gem_version >= Gem::Version.new('7.0.0')
19
+ # @api private
20
+ def association_preloader(records:, associations:, scope:)
21
+ ActiveRecord::Associations::Preloader.new(
22
+ records: records, associations: associations, scope: scope
23
+ ).call
24
+ end
25
+ else
26
+ # @api private
27
+ def association_preloader(records:, associations:, scope:)
28
+ ActiveRecord::Associations::Preloader.new.preload(
29
+ records, associations, scope
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'factory_bot'
4
- require_relative '../../spec/support/features/fake_content'
4
+ require_relative '../../spec/support/system/fake_content'
5
5
 
6
6
  # rubocop:disable HandleExceptions
7
7
  begin
@@ -40,6 +40,7 @@ module Thredded
40
40
  end
41
41
 
42
42
  include LogTime
43
+ include Thredded::ApplicationHelper
43
44
 
44
45
  SKIP_CALLBACKS = [
45
46
  [Thredded::Post, :commit, :after, :update_parent_last_user_and_time_from_last_post, on: %i[create destroy]],
@@ -131,7 +132,13 @@ module Thredded
131
132
  end
132
133
 
133
134
  def fake_post_contents
134
- @fake_post_contents ? @fake_post_contents.sample : FakeContent.post_content
135
+ with_mentions(@fake_post_contents ? @fake_post_contents.sample : FakeContent.post_content)
136
+ end
137
+
138
+ def with_mentions(post, mentions_count: rand(3))
139
+ return post if mentions_count.zero?
140
+
141
+ ([post] + Array.new(mentions_count).map { user_mention(@users.sample) }).join(' ')
135
142
  end
136
143
 
137
144
  def first_user
@@ -255,9 +262,9 @@ module Thredded
255
262
 
256
263
  delegate :log, to: :seeder
257
264
 
258
- def find_or_create(*args)
265
+ def find_or_create(*args, **kwargs)
259
266
  return @stored if @stored
260
- @stored = (find || create(*args))
267
+ @stored = (find || create(*args, **kwargs))
261
268
  end
262
269
 
263
270
  def range_of_dates_in_order(up_to: Time.zone.now, count: 1)
@@ -10,12 +10,10 @@ module Thredded
10
10
  verbose_was = ActiveRecord::Migration.verbose
11
11
  ActiveRecord::Migration.verbose = !quiet
12
12
  migrate =
13
- if Rails.gem_version >= Gem::Version.new('6.0.0.rc2')
13
+ if Thredded::Compat.rails_gte_60?
14
14
  -> { ActiveRecord::MigrationContext.new(paths, ActiveRecord::SchemaMigration).migrate(nil, &filter) }
15
- elsif Rails::VERSION::STRING >= '5.2'
15
+ else # Rails 5.2
16
16
  -> { ActiveRecord::MigrationContext.new(paths).migrate(nil, &filter) }
17
- else
18
- -> { ActiveRecord::Migrator.migrate(paths, &filter) }
19
17
  end
20
18
  if quiet
21
19
  silence_active_record(&migrate)
@@ -17,8 +17,8 @@ module Thredded
17
17
  end
18
18
  @transformers = [Onebox, Spoiler]
19
19
 
20
- # @param doc [Nokogiri::HTML::Document]
21
- def self.call(doc)
20
+ # @param dom [Nokogiri::HTML::Document]
21
+ def self.call(doc, *)
22
22
  transformers.each { |transformer| transformer.call(doc) }
23
23
  end
24
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- VERSION = '0.16.16'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/thredded.rb CHANGED
@@ -9,7 +9,7 @@ require 'html/pipeline'
9
9
  require 'html/pipeline/sanitization_filter'
10
10
  require 'rinku'
11
11
  require 'kaminari'
12
- require 'rb-gravatar'
12
+ require 'rails_gravatar'
13
13
  require 'active_job'
14
14
  require 'inline_svg'
15
15
 
@@ -32,6 +32,7 @@ require 'sprockets/es6'
32
32
  require 'sassc-rails'
33
33
 
34
34
  require 'thredded/version'
35
+ require 'thredded/compat'
35
36
  require 'thredded/engine'
36
37
  require 'thredded/errors'
37
38
 
@@ -48,15 +49,6 @@ require 'thredded/collection_to_strings_with_cache_renderer'
48
49
 
49
50
  require 'thredded/webpack_assets'
50
51
 
51
- if Rails::VERSION::MAJOR < 5
52
- begin
53
- require 'where-or'
54
- rescue LoadError
55
- $stderr.puts "\nthredded: Please add gem 'where-or' to your Gemfile"
56
- exit 1 # rubocop:disable Rails/Exit
57
- end
58
- end
59
-
60
52
  module Thredded # rubocop:disable Metrics/ModuleLength
61
53
  class << self
62
54
  #== User
@@ -252,22 +244,10 @@ module Thredded # rubocop:disable Metrics/ModuleLength
252
244
  .includes(:postable)
253
245
  )
254
246
  end
255
-
256
- # @api private
257
- def rails_gte_51?
258
- @rails_gte_51 = (Rails.gem_version >= Gem::Version.new('5.1.0')) if @rails_gte_51.nil?
259
- @rails_gte_51
260
- end
261
-
262
- # @api private
263
- def rails_supports_csp_nonce?
264
- @rails_supports_csp_nonce = (Rails.gem_version >= Gem::Version.new('5.2.0')) if @rails_supports_csp_nonce.nil?
265
- @rails_supports_csp_nonce
266
- end
267
247
  end
268
248
 
269
249
  self.user_name_column = :name
270
- self.avatar_url = ->(user) { Gravatar.src(user.email, 156, 'mm') }
250
+ self.avatar_url = ->(user) { RailsGravatar.src(user.email, 156, 'mm') }
271
251
  self.admin_column = :admin
272
252
 
273
253
  self.content_visible_while_pending_moderation = true