thredded 0.16.16 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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