thredded 1.0.1 → 1.2.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.
- checksums.yaml +4 -4
- data/README.md +48 -29
- data/app/helpers/thredded/urls_helper.rb +2 -0
- data/app/models/concerns/thredded/moderation_state.rb +1 -1
- data/app/models/concerns/thredded/topics_search.rb +1 -1
- data/app/models/concerns/thredded/union_scope.rb +37 -0
- data/app/models/thredded/post_moderation_record.rb +3 -1
- data/app/models/thredded/topic.rb +7 -0
- data/app/models/thredded/user_topic_follow.rb +3 -2
- data/app/view_models/thredded/topics_page_view.rb +1 -0
- data/bin/run_specs_repeatedly +9 -0
- data/config/locales/uk.yml +284 -0
- data/lib/thredded/arel_compat.rb +16 -0
- data/lib/thredded/collection_to_strings_with_cache_renderer.rb +37 -58
- data/lib/thredded/compat.rb +12 -20
- data/lib/thredded/content_formatter.rb +11 -18
- data/lib/thredded/database_seeder.rb +26 -11
- data/lib/thredded/db_tools.rb +1 -5
- data/lib/thredded/engine.rb +9 -0
- data/lib/thredded/version.rb +1 -1
- data/lib/thredded.rb +0 -2
- metadata +18 -380
- data/lib/thredded/rails_lt_5_2_arel_case_node.rb +0 -119
@@ -47,7 +47,8 @@ module Thredded
|
|
47
47
|
|
48
48
|
ordered_keys.map do |cache_key|
|
49
49
|
[keyed_collection[cache_key], cached_partials[cache_key] || rendered_partials.next.tap do |rendered|
|
50
|
-
|
50
|
+
cache.write(cache_key, rendered, expires_in: expires_in)
|
51
|
+
cached_partials[cache_key] = rendered
|
51
52
|
end]
|
52
53
|
end
|
53
54
|
end
|
@@ -59,16 +60,16 @@ module Thredded
|
|
59
60
|
digest_path = digest_path_from_template(view, template)
|
60
61
|
|
61
62
|
collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
|
62
|
-
key = expanded_cache_key(item, view,
|
63
|
+
key = expanded_cache_key(item, view, digest_path)
|
63
64
|
ordered_keys << key
|
64
65
|
hash[key] = item
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
68
|
-
def expanded_cache_key(key, view,
|
69
|
+
def expanded_cache_key(key, view, digest_path)
|
69
70
|
key = combined_fragment_cache_key(
|
70
71
|
view,
|
71
|
-
cache_fragment_name(view, key,
|
72
|
+
cache_fragment_name(view, key, digest_path: digest_path)
|
72
73
|
)
|
73
74
|
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
|
74
75
|
end
|
@@ -82,34 +83,38 @@ module Thredded
|
|
82
83
|
else
|
83
84
|
collection.each_slice(collection.size / num_threads).map do |slice|
|
84
85
|
Thread.start do
|
85
|
-
# `ActionView::PartialRenderer` mutates the contents of `opts[:locals]`, `opts[:locals][:as]` in particular:
|
86
|
-
# https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L379
|
87
|
-
# https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L348-L356
|
88
|
-
opts[:locals] = opts[:locals].dup if opts[:locals]
|
89
86
|
ActiveRecord::Base.connection_pool.with_connection do
|
90
|
-
render_partials_serial(view_context.dup, slice, opts)
|
87
|
+
render_partials_serial(view_context.dup, slice, dup_opts(opts))
|
91
88
|
end
|
92
89
|
end
|
93
90
|
end.flat_map(&:value)
|
94
91
|
end
|
95
92
|
end
|
96
93
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
#
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
94
|
+
# `ActionView::PartialRenderer` mutates the contents of `opts[:locals]`, `opts[:locals][:as]` in particular:
|
95
|
+
# https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L379
|
96
|
+
# https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L348-L356
|
97
|
+
def dup_opts(opts)
|
98
|
+
if opts[:locals]
|
99
|
+
opts = opts.dup
|
100
|
+
# sometimes have a thread safe :users_provider, preserve that for performance reasons
|
101
|
+
# hence not doing a deep_dup
|
102
|
+
opts[:locals] = opts[:locals].dup
|
103
|
+
opts
|
104
|
+
else
|
105
|
+
opts.dup
|
108
106
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param [Array<Object>] collection
|
110
|
+
# @param [Hash] opts
|
111
|
+
# @param view_context
|
112
|
+
# @return [Array<String>]
|
113
|
+
def render_partials_serial(view_context, collection, opts)
|
114
|
+
# https://github.com/rails/rails/pull/38594
|
115
|
+
collection.map do |object|
|
116
|
+
renderer = ActionView::ObjectRenderer.new(@lookup_context, opts)
|
117
|
+
renderer.render_object_with_partial(object, opts[:partial], view_context, nil).body
|
113
118
|
end
|
114
119
|
end
|
115
120
|
|
@@ -121,42 +126,16 @@ module Thredded
|
|
121
126
|
view.combined_fragment_cache_key(key)
|
122
127
|
end
|
123
128
|
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
131
|
-
end
|
132
|
-
|
133
|
-
def digest_path_from_template(view, template)
|
134
|
-
view.digest_path_from_template(template)
|
135
|
-
end
|
136
|
-
|
137
|
-
def render_partial(partial_renderer, view_context, opts)
|
138
|
-
partial_renderer.render(view_context, opts, nil).body
|
139
|
-
end
|
140
|
-
else
|
141
|
-
def cache_fragment_name(_view, key, virtual_path:, digest_path:)
|
142
|
-
if digest_path
|
143
|
-
["#{virtual_path}:#{digest_path}", key]
|
144
|
-
else
|
145
|
-
[virtual_path, key]
|
146
|
-
end
|
147
|
-
end
|
129
|
+
def cache_fragment_name(view, key, digest_path:)
|
130
|
+
view.cache_fragment_name(key, digest_path: digest_path)
|
131
|
+
end
|
148
132
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
finder: @lookup_context,
|
153
|
-
dependencies: view.view_cache_dependencies
|
154
|
-
).presence
|
155
|
-
end
|
133
|
+
def digest_path_from_template(view, template)
|
134
|
+
view.digest_path_from_template(template)
|
135
|
+
end
|
156
136
|
|
157
|
-
|
158
|
-
|
159
|
-
end
|
137
|
+
def render_partial(partial_renderer, view_context, opts)
|
138
|
+
partial_renderer.render(view_context, opts, nil).body
|
160
139
|
end
|
161
140
|
end
|
162
141
|
end
|
data/lib/thredded/compat.rb
CHANGED
@@ -4,31 +4,23 @@ module Thredded
|
|
4
4
|
module Compat
|
5
5
|
class << self
|
6
6
|
# @api private
|
7
|
-
def
|
8
|
-
@
|
9
|
-
@
|
7
|
+
def rails_gte_71?
|
8
|
+
@rails_gte_71 = (Rails.gem_version >= Gem::Version.new('7.1.0')) if @rails_gte_71.nil?
|
9
|
+
@rails_gte_71
|
10
10
|
end
|
11
11
|
|
12
12
|
# @api private
|
13
|
-
def
|
14
|
-
@
|
15
|
-
@
|
13
|
+
def rails_gte_72?
|
14
|
+
@rails_gte_72 = (Rails.gem_version >= Gem::Version.new('7.2.0')) if @rails_gte_72.nil?
|
15
|
+
@rails_gte_72
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
18
|
+
# @api private
|
19
|
+
# TODO: inline this or put it somewhere else as it's no longer specific to a rails version
|
20
|
+
def association_preloader(records:, associations:, scope:)
|
21
|
+
ActiveRecord::Associations::Preloader.new(
|
22
|
+
records: records, associations: associations, scope: scope
|
23
|
+
).call
|
32
24
|
end
|
33
25
|
end
|
34
26
|
end
|
@@ -8,7 +8,15 @@ module Thredded
|
|
8
8
|
attr_accessor :allowlist
|
9
9
|
|
10
10
|
# TODO: v2.0: drop alias and just use allowlist
|
11
|
-
|
11
|
+
# @deprecated use allowlist
|
12
|
+
def whitelist
|
13
|
+
ActiveSupport::Deprecation.warn(<<~MESSAGE.squish)
|
14
|
+
ContentFormatter.whitelist is deprecated and will be removed in Thredded 2.0.
|
15
|
+
Use ContentFormatter.allowlist instead
|
16
|
+
MESSAGE
|
17
|
+
|
18
|
+
allowlist
|
19
|
+
end
|
12
20
|
|
13
21
|
# Filters that run before processing the markup.
|
14
22
|
# input: markup, output: markup.
|
@@ -30,17 +38,8 @@ module Thredded
|
|
30
38
|
# input: sanitized html, output: html
|
31
39
|
attr_accessor :after_sanitization_filters
|
32
40
|
|
33
|
-
# TODO: v1.1: only allow html-pipeline >= 2.14.1 and drop this
|
34
|
-
def sanitization_filter_uses_allowlist?
|
35
|
-
defined?(HTML::Pipeline::SanitizationFilter::ALLOWLIST)
|
36
|
-
end
|
37
|
-
|
38
41
|
def sanitization_filter_allowlist_config
|
39
|
-
|
40
|
-
HTML::Pipeline::SanitizationFilter::ALLOWLIST
|
41
|
-
else
|
42
|
-
HTML::Pipeline::SanitizationFilter::WHITELIST
|
43
|
-
end
|
42
|
+
HTML::Pipeline::SanitizationFilter::ALLOWLIST
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
@@ -150,15 +149,9 @@ module Thredded
|
|
150
149
|
|
151
150
|
# @return [Hash] options for HTML::Pipeline.new
|
152
151
|
def content_pipeline_options
|
153
|
-
option = if self.class.sanitization_filter_uses_allowlist?
|
154
|
-
:allowlist
|
155
|
-
else
|
156
|
-
# TODO: v1.1: only allow html-pipeline >= 2.14.1 and drop this
|
157
|
-
:whitelist
|
158
|
-
end
|
159
152
|
{
|
160
153
|
asset_root: Rails.application.config.action_controller.asset_host || '',
|
161
|
-
|
154
|
+
allowlist: ContentFormatter.allowlist
|
162
155
|
}
|
163
156
|
end
|
164
157
|
end
|
@@ -10,6 +10,7 @@ rescue NameError
|
|
10
10
|
end
|
11
11
|
# rubocop:enable HandleExceptions
|
12
12
|
module Thredded
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
13
14
|
class DatabaseSeeder # rubocop:disable Metrics/ClassLength
|
14
15
|
module LogTime
|
15
16
|
def self.included(base)
|
@@ -60,8 +61,16 @@ module Thredded
|
|
60
61
|
def self.with_seeder_tweaks
|
61
62
|
# Disable callbacks to avoid creating notifications and performing unnecessary updates
|
62
63
|
DISABLE_COUNTER_CACHE.each do |klass|
|
63
|
-
|
64
|
-
|
64
|
+
if Thredded::Compat.rails_gte_72?
|
65
|
+
klass.class_eval do
|
66
|
+
class_attribute :original_counter_cached_association_names
|
67
|
+
self.original_counter_cached_association_names = counter_cached_association_names
|
68
|
+
self.counter_cached_association_names = []
|
69
|
+
end
|
70
|
+
else
|
71
|
+
klass.send(:alias_method, :original_each_counter_cached_associations, :each_counter_cached_associations)
|
72
|
+
klass.send(:define_method, :each_counter_cached_associations) {}
|
73
|
+
end
|
65
74
|
end
|
66
75
|
SKIP_CALLBACKS.each { |(klass, *args)| delete_callbacks(klass, *args) }
|
67
76
|
WRITEABLE_READONLY_ATTRIBUTES.each { |(klass, attr)| klass.readonly_attributes.delete(attr) }
|
@@ -71,9 +80,15 @@ module Thredded
|
|
71
80
|
ensure
|
72
81
|
# Re-enable callbacks and counter cache
|
73
82
|
DISABLE_COUNTER_CACHE.each do |klass|
|
74
|
-
|
75
|
-
|
76
|
-
|
83
|
+
if Thredded::Compat.rails_gte_72?
|
84
|
+
klass.class_eval do
|
85
|
+
self.counter_cached_association_names = original_counter_cached_association_names
|
86
|
+
end
|
87
|
+
else
|
88
|
+
klass.send(:remove_method, :each_counter_cached_associations)
|
89
|
+
klass.send(:alias_method, :each_counter_cached_associations, :original_each_counter_cached_associations)
|
90
|
+
klass.send(:remove_method, :original_each_counter_cached_associations)
|
91
|
+
end
|
77
92
|
end
|
78
93
|
SKIP_CALLBACKS.each do |(klass, *args)|
|
79
94
|
args = args.dup
|
@@ -150,9 +165,7 @@ module Thredded
|
|
150
165
|
end
|
151
166
|
|
152
167
|
def user_details
|
153
|
-
@user_details ||= users.
|
154
|
-
hash[user] = user.thredded_user_detail
|
155
|
-
end
|
168
|
+
@user_details ||= users.index_with(&:thredded_user_detail)
|
156
169
|
end
|
157
170
|
|
158
171
|
def first_messageboard
|
@@ -175,7 +188,7 @@ module Thredded
|
|
175
188
|
end
|
176
189
|
end
|
177
190
|
|
178
|
-
log_method_time def update_messageboards_data(**)
|
191
|
+
log_method_time def update_messageboards_data(**)
|
179
192
|
log 'Updating messageboards data...'
|
180
193
|
Messageboard.all.each do |messageboard|
|
181
194
|
messageboard.update_last_topic!
|
@@ -330,7 +343,7 @@ module Thredded
|
|
330
343
|
class FirstMessageboard < FirstSeedData
|
331
344
|
MODEL_CLASS = Messageboard
|
332
345
|
|
333
|
-
log_method_time def create(**)
|
346
|
+
log_method_time def create(**)
|
334
347
|
log 'Creating a messageboard...'
|
335
348
|
@first_messageboard = FactoryBot.create(
|
336
349
|
:messageboard,
|
@@ -384,7 +397,8 @@ module Thredded
|
|
384
397
|
class Posts < CollectionSeedData
|
385
398
|
MODEL_CLASS = Post
|
386
399
|
|
387
|
-
log_method_time def create(count: (1..1), topics: seeder.topics)
|
400
|
+
log_method_time def create(count: (1..1), topics: seeder.topics)
|
401
|
+
# rubocop:disable Metrics/MethodLength
|
388
402
|
log "Creating #{count} additional posts in each topic..."
|
389
403
|
topics.flat_map do |topic|
|
390
404
|
last_post_at = random_duration(0..256.hours).ago
|
@@ -446,4 +460,5 @@ module Thredded
|
|
446
460
|
end
|
447
461
|
end
|
448
462
|
end
|
463
|
+
# rubocop:enable Metrics/MethodLength
|
449
464
|
end
|
data/lib/thredded/db_tools.rb
CHANGED
@@ -10,11 +10,7 @@ module Thredded
|
|
10
10
|
verbose_was = ActiveRecord::Migration.verbose
|
11
11
|
ActiveRecord::Migration.verbose = !quiet
|
12
12
|
migrate =
|
13
|
-
|
14
|
-
-> { ActiveRecord::MigrationContext.new(paths, ActiveRecord::SchemaMigration).migrate(nil, &filter) }
|
15
|
-
else # Rails 5.2
|
16
|
-
-> { ActiveRecord::MigrationContext.new(paths).migrate(nil, &filter) }
|
17
|
-
end
|
13
|
+
-> { ActiveRecord::MigrationContext.new(paths).migrate(nil, &filter) }
|
18
14
|
if quiet
|
19
15
|
silence_active_record(&migrate)
|
20
16
|
else
|
data/lib/thredded/engine.rb
CHANGED
@@ -20,5 +20,14 @@ module Thredded
|
|
20
20
|
thredded_manifest.js
|
21
21
|
]
|
22
22
|
end
|
23
|
+
|
24
|
+
config.after_initialize do |app|
|
25
|
+
if !Rails.env.production? && app.assets.preprocessors.keys.exclude?('text/scss')
|
26
|
+
fail [
|
27
|
+
'Thredded requires a Sass compiler to be registered in Sprockets.',
|
28
|
+
%(Please add "sassc-rails" or "dartsass-sprockets" to your application Gemfile.),
|
29
|
+
].join(' ')
|
30
|
+
end
|
31
|
+
end
|
23
32
|
end
|
24
33
|
end
|
data/lib/thredded/version.rb
CHANGED
data/lib/thredded.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
# Backend
|
4
4
|
require 'pundit'
|
5
|
-
require 'active_record_union'
|
6
5
|
require 'db_text_search'
|
7
6
|
require 'friendly_id'
|
8
7
|
require 'html/pipeline'
|
@@ -29,7 +28,6 @@ require 'thredded/users_provider'
|
|
29
28
|
require 'autoprefixer-rails'
|
30
29
|
require 'timeago_js'
|
31
30
|
require 'sprockets/es6'
|
32
|
-
require 'sassc-rails'
|
33
31
|
|
34
32
|
require 'thredded/version'
|
35
33
|
require 'thredded/compat'
|