From c02d97736da1c319a1be70a53437e6888787e2de Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 22 Feb 2019 14:50:11 -0600 Subject: [PATCH 01/12] Add basic post reporting functionality --- app/controllers/thredded/posts_controller.rb | 14 +++++++++++++- app/view_models/thredded/post_view.rb | 4 ++++ app/views/thredded/posts_common/_actions.html.erb | 4 ++++ .../thredded/posts_common/actions/_report.html.erb | 3 +++ config/locales/en.yml | 2 ++ config/routes.rb | 2 ++ 6 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 app/views/thredded/posts_common/actions/_report.html.erb diff --git a/app/controllers/thredded/posts_controller.rb b/app/controllers/thredded/posts_controller.rb index c2f5bd49e..13c8518a9 100644 --- a/app/controllers/thredded/posts_controller.rb +++ b/app/controllers/thredded/posts_controller.rb @@ -7,7 +7,7 @@ class PostsController < Thredded::ApplicationController include Thredded::NewPostParams helper_method :topic - before_action :assign_messageboard_for_actions, only: %i[mark_as_read mark_as_unread] + before_action :assign_messageboard_for_actions, only: %i[mark_as_read mark_as_unread report] after_action :update_user_activity after_action :verify_authorized @@ -77,6 +77,18 @@ def quote render plain: Thredded::ContentFormatter.quote_content(post.content) end + def report + authorize_reading post + post.update(moderation_state: 'pending_moderation') + respond_to do |format| + format.html { + redirect_back fallback_location: post_path(post, user: thredded_current_user), + notice: I18n.t('thredded.posts.reported_post') + } + format.json { render(json: { reported: true }) } + end + end + private def canonical_topic_params diff --git a/app/view_models/thredded/post_view.rb b/app/view_models/thredded/post_view.rb index d4dafe4e2..d9ec9d2da 100644 --- a/app/view_models/thredded/post_view.rb +++ b/app/view_models/thredded/post_view.rb @@ -65,6 +65,10 @@ def mark_unread_path Thredded::UrlsHelper.mark_unread_path(@post) end + def report_path + Thredded::UrlsHelper.report_post_path(@post) + end + def destroy_path Thredded::UrlsHelper.delete_post_path(@post) end diff --git a/app/views/thredded/posts_common/_actions.html.erb b/app/views/thredded/posts_common/_actions.html.erb index 987514215..3255bedac 100644 --- a/app/views/thredded/posts_common/_actions.html.erb +++ b/app/views/thredded/posts_common/_actions.html.erb @@ -10,6 +10,10 @@ <% if post.can_destroy? %> <%= render 'thredded/posts_common/actions/delete', post: post %> <% end %> + <%# TODO: if user_signed_in? %> + <% if true %> + <%= render 'thredded/posts_common/actions/report', post: post %> + <% end %> <% if post.read_state %> <%= view_hooks.post_common.mark_as_unread.render self, post: post do %> <%= render 'thredded/posts_common/actions/mark_as_unread', post: post %> diff --git a/app/views/thredded/posts_common/actions/_report.html.erb b/app/views/thredded/posts_common/actions/_report.html.erb new file mode 100644 index 000000000..45b7dd19b --- /dev/null +++ b/app/views/thredded/posts_common/actions/_report.html.erb @@ -0,0 +1,3 @@ +<%= button_to t('thredded.posts.report'), post.report_path, + method: :post, + class: 'thredded--post--report thredded--post--dropdown--actions--item' %> diff --git a/config/locales/en.yml b/config/locales/en.yml index da3da26a3..c6bf42ba2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -136,6 +136,8 @@ en: update_btn_submitting: :thredded.form.update_btn_submitting pending_moderation_notice: Your post will be published when it has been reviewed by a moderator. quote_btn: Quote + report: Report Post + reported_post: This post has been reported to moderation for review. spoiler_summary: Spoiler - click to show. spoiler_summary_for_email: 'Spoiler - select the contents below to see:' preferences: diff --git a/config/routes.rb b/config/routes.rb index 33406b7c0..2a47fc77b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -94,6 +94,7 @@ member do post 'mark_as_read' post 'mark_as_unread' + post 'report' end end @@ -101,6 +102,7 @@ member do post 'mark_as_read' post 'mark_as_unread' + post 'report' end end end From 09909e7c4e6bce9195ac40aec770ce7987ce58c0 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 22 Feb 2019 14:54:55 -0600 Subject: [PATCH 02/12] Limit post reporting to users signed in --- app/views/thredded/posts_common/_actions.html.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/thredded/posts_common/_actions.html.erb b/app/views/thredded/posts_common/_actions.html.erb index 3255bedac..ddf051778 100644 --- a/app/views/thredded/posts_common/_actions.html.erb +++ b/app/views/thredded/posts_common/_actions.html.erb @@ -10,8 +10,7 @@ <% if post.can_destroy? %> <%= render 'thredded/posts_common/actions/delete', post: post %> <% end %> - <%# TODO: if user_signed_in? %> - <% if true %> + <% if thredded_signed_in? %> <%= render 'thredded/posts_common/actions/report', post: post %> <% end %> <% if post.read_state %> From a3d791d38d8a3f5bbe36d3bd730f9b6eb1c712dc Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 22 Feb 2019 15:06:41 -0600 Subject: [PATCH 03/12] follow standard for post policies --- app/policies/thredded/post_policy.rb | 5 +++++ app/view_models/thredded/post_view.rb | 4 ++++ app/views/thredded/posts_common/_actions.html.erb | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/policies/thredded/post_policy.rb b/app/policies/thredded/post_policy.rb index 2619f5015..506fb14c7 100644 --- a/app/policies/thredded/post_policy.rb +++ b/app/policies/thredded/post_policy.rb @@ -45,6 +45,11 @@ def destroy? !@post.first_post_in_topic? && update? end + def report? + # Only allow logged-in users to report posts + !(@user.is_a?(Thredded::NullUser)) + end + def anonymous? @user.thredded_anonymous? end diff --git a/app/view_models/thredded/post_view.rb b/app/view_models/thredded/post_view.rb index d9ec9d2da..225ecbdf0 100644 --- a/app/view_models/thredded/post_view.rb +++ b/app/view_models/thredded/post_view.rb @@ -45,6 +45,10 @@ def can_moderate? @can_moderate ||= @policy.moderate? end + def can_report? + @can_report ||= @policy.report? + end + def quote_url_params if @post.private_topic_post? { post: { quote_private_post_id: @post.id } } diff --git a/app/views/thredded/posts_common/_actions.html.erb b/app/views/thredded/posts_common/_actions.html.erb index ddf051778..af5f839f4 100644 --- a/app/views/thredded/posts_common/_actions.html.erb +++ b/app/views/thredded/posts_common/_actions.html.erb @@ -10,7 +10,7 @@ <% if post.can_destroy? %> <%= render 'thredded/posts_common/actions/delete', post: post %> <% end %> - <% if thredded_signed_in? %> + <% if post.can_report? %> <%= render 'thredded/posts_common/actions/report', post: post %> <% end %> <% if post.read_state %> From 4578ebb60d258cb8dc67dae1d1d1476526519ed0 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 22 Feb 2019 15:14:08 -0600 Subject: [PATCH 04/12] Address rubocop concerns --- app/controllers/thredded/posts_controller.rb | 4 ++-- app/policies/thredded/post_policy.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/thredded/posts_controller.rb b/app/controllers/thredded/posts_controller.rb index 13c8518a9..226e235fc 100644 --- a/app/controllers/thredded/posts_controller.rb +++ b/app/controllers/thredded/posts_controller.rb @@ -81,10 +81,10 @@ def report authorize_reading post post.update(moderation_state: 'pending_moderation') respond_to do |format| - format.html { + format.html do redirect_back fallback_location: post_path(post, user: thredded_current_user), notice: I18n.t('thredded.posts.reported_post') - } + end format.json { render(json: { reported: true }) } end end diff --git a/app/policies/thredded/post_policy.rb b/app/policies/thredded/post_policy.rb index 506fb14c7..d0b298fde 100644 --- a/app/policies/thredded/post_policy.rb +++ b/app/policies/thredded/post_policy.rb @@ -47,7 +47,7 @@ def destroy? def report? # Only allow logged-in users to report posts - !(@user.is_a?(Thredded::NullUser)) + !(@user.is_a? Thredded::NullUser) end def anonymous? From f3917c8465d8a02eff38fe11282575c81c810003 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 13 Aug 2019 16:42:05 -0500 Subject: [PATCH 05/12] don't require post preloading when a preloader isn't found --- app/models/concerns/thredded/post_common.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/concerns/thredded/post_common.rb b/app/models/concerns/thredded/post_common.rb index 379be7f26..8a765d755 100644 --- a/app/models/concerns/thredded/post_common.rb +++ b/app/models/concerns/thredded/post_common.rb @@ -30,6 +30,10 @@ module PostCommon SELECT MAX(p2.created_at) from #{posts_table_name} p2 WHERE p2.postable_id = #{posts_table_name}.postable_id) SQL ) + + # When we can't load a preloader, return the results as-is + return result if preloader.empty? + preloader[0].preloaded_records.each do |post| topic = owners_by_id.delete(post.postable_id) next unless topic From a558800725d701cc341194b446744cb7292c1a65 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 13 Aug 2019 22:25:23 -0500 Subject: [PATCH 06/12] don't report? in pms --- app/view_models/thredded/post_view.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/view_models/thredded/post_view.rb b/app/view_models/thredded/post_view.rb index 225ecbdf0..46a774486 100644 --- a/app/view_models/thredded/post_view.rb +++ b/app/view_models/thredded/post_view.rb @@ -46,7 +46,7 @@ def can_moderate? end def can_report? - @can_report ||= @policy.report? + @can_report ||= !!@policy.try(:report?) end def quote_url_params From 76dad671791bd3761271a66e5819859bd0b3a780 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 29 Aug 2019 19:07:27 -0500 Subject: [PATCH 07/12] Require data-confirm confirmation on report selection --- app/views/thredded/posts_common/actions/_report.html.erb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/thredded/posts_common/actions/_report.html.erb b/app/views/thredded/posts_common/actions/_report.html.erb index 45b7dd19b..963b431c6 100644 --- a/app/views/thredded/posts_common/actions/_report.html.erb +++ b/app/views/thredded/posts_common/actions/_report.html.erb @@ -1,3 +1,5 @@ <%= button_to t('thredded.posts.report'), post.report_path, method: :post, - class: 'thredded--post--report thredded--post--dropdown--actions--item' %> + class: 'thredded--post--report thredded--post--dropdown--actions--item', + data: { confirm: "Are you sure you want to report this post?" } +%> From 20c41ae6c25ffc1a0b01d3a761fc4f4746cd3f68 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 29 Aug 2019 19:19:12 -0500 Subject: [PATCH 08/12] Use i18n for confirm message --- app/views/thredded/posts_common/actions/_report.html.erb | 2 +- config/locales/en.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/thredded/posts_common/actions/_report.html.erb b/app/views/thredded/posts_common/actions/_report.html.erb index 963b431c6..d8a9157f0 100644 --- a/app/views/thredded/posts_common/actions/_report.html.erb +++ b/app/views/thredded/posts_common/actions/_report.html.erb @@ -1,5 +1,5 @@ <%= button_to t('thredded.posts.report'), post.report_path, method: :post, class: 'thredded--post--report thredded--post--dropdown--actions--item', - data: { confirm: "Are you sure you want to report this post?" } + data: { confirm: I18n.t('thredded.posts.report_confirm') } %> diff --git a/config/locales/en.yml b/config/locales/en.yml index f4e0e8997..41eb2298e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -142,6 +142,7 @@ en: pending_moderation_notice: Your post will be published when it has been reviewed by a moderator. quote_btn: Quote report: Report Post + report_confirm: Are you sure you want to report this post? reported_post: This post has been reported to moderation for review. spoiler_summary: Spoiler - click to show. spoiler_summary_for_email: 'Spoiler - select the contents below to see:' From b9ff1fd902d28d5cc774fe1e8637e49a0b0b1c10 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 4 Jan 2020 11:41:49 +0000 Subject: [PATCH 09/12] PostView: Also delegate `cache_version` This may be needed as of https://github.com/rails/rails/commit/4f2ac80d4cdb01c4d3c1765637bed76cc91c1e35 Refs #839 --- app/view_models/thredded/post_view.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/view_models/thredded/post_view.rb b/app/view_models/thredded/post_view.rb index d4dafe4e2..9e7e0b5e1 100644 --- a/app/view_models/thredded/post_view.rb +++ b/app/view_models/thredded/post_view.rb @@ -13,6 +13,7 @@ class PostView :blocked?, :last_moderation_record, :cache_key, + :cache_version, :cache_key_with_version, to: :@post From d8dc158dcf23b0f76daffff7f2fab225209ee446 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 4 Jan 2020 11:42:39 +0000 Subject: [PATCH 10/12] collection renderer: Use Rails 6 cache methods Use `combined_fragment_cache_key` instead of the older methods. This may be slightly slower but is easier to maintain going forward. Refs #839 --- ...llection_to_strings_with_cache_renderer.rb | 89 +++++++++++++++---- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/lib/thredded/collection_to_strings_with_cache_renderer.rb b/lib/thredded/collection_to_strings_with_cache_renderer.rb index ac00b2c34..cc2343ce7 100644 --- a/lib/thredded/collection_to_strings_with_cache_renderer.rb +++ b/lib/thredded/collection_to_strings_with_cache_renderer.rb @@ -26,13 +26,12 @@ def render_collection_to_strings_with_cache( # rubocop:disable Metrics/Parameter collection = collection.to_a instrument(:collection, identifier: template.identifier, count: collection.size) do |instrumentation_payload| return [] if collection.blank? - keyed_collection = collection.each_with_object({}) do |item, hash| - key = ActiveSupport::Cache.expand_cache_key( - view_context.cache_fragment_name(item, virtual_path: template.virtual_path), :views - ) - # #read_multi & #write may require key mutability, Dalli 2.6.0. - hash[key.frozen? ? key.dup : key] = item - end + + # Result is a hash with the key represents the + # key used for cache lookup and the value is the item + # on which the partial is being rendered + keyed_collection, ordered_keys = collection_by_cache_keys(collection, view_context, template) + cache = collection_cache cached_partials = cache.read_multi(*keyed_collection.keys) instrumentation_payload[:cache_hits] = cached_partials.size if instrumentation_payload @@ -44,9 +43,9 @@ def render_collection_to_strings_with_cache( # rubocop:disable Metrics/Parameter partial: partial, locals: locals, **opts ).each - keyed_collection.map do |cache_key, item| - [item, cached_partials[cache_key] || rendered_partials.next.tap do |rendered| - cache.write(cache_key, rendered, expires_in: expires_in) + ordered_keys.map do |cache_key| + [keyed_collection[cache_key], cached_partials[cache_key] || rendered_partials.next.tap do |rendered| + cached_partials[cache_key] = cache.write(cache_key, rendered, expires_in: expires_in) end] end end @@ -54,16 +53,24 @@ def render_collection_to_strings_with_cache( # rubocop:disable Metrics/Parameter private - def collection_cache - if ActionView::PartialRenderer.respond_to?(:collection_cache) - # Rails 5.0+ - ActionView::PartialRenderer.collection_cache - else - # Rails 4.2.x - Rails.application.config.action_controller.cache_store + def collection_by_cache_keys(collection, view, template) + digest_path = digest_path_from_template(view, template) + + collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)| + key = expanded_cache_key(item, view, template, digest_path) + ordered_keys << key + hash[key] = item end end + def expanded_cache_key(key, view, template, digest_path) + key = combined_fragment_cache_key( + view, + cache_fragment_name(view, key, virtual_path: template.virtual_path, digest_path: digest_path) + ) + key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0. + end + # @return [Array] def render_partials(view_context, collection:, render_threads:, **opts) return [] if collection.empty? @@ -86,11 +93,59 @@ def render_partials_serial(view_context, collection, opts) collection.map { |object| render_partial(partial_renderer, view_context, opts.merge(object: object)) } end + if Rails::VERSION::MAJOR >= 5 + def collection_cache + ActionView::PartialRenderer.collection_cache + end + else + def collection_cache + Rails.application.config.action_controller.cache_store + end + end + + if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2) + def combined_fragment_cache_key(view, key) + view.combined_fragment_cache_key(key) + end + elsif Rails::VERSION::MAJOR >= 5 + def combined_fragment_cache_key(view, key) + view.fragment_cache_key(key) + end + else + def combined_fragment_cache_key(view, key) + view.controller.fragment_cache_key(key) + end + end + if Rails::VERSION::MAJOR >= 6 + def cache_fragment_name(view, key, virtual_path:, digest_path:) + view.cache_fragment_name(key, virtual_path: virtual_path, digest_path: digest_path) + end + + def digest_path_from_template(view, template) + view.digest_path_from_template(template) + end + def render_partial(partial_renderer, view_context, opts) partial_renderer.render(view_context, opts, nil).body end else + def cache_fragment_name(_view, key, virtual_path:, digest_path:) + if digest_path + ["#{virtual_path}:#{digest_path}", key] + else + [virtual_path, key] + end + end + + def digest_path_from_template(view, template) + ActionView::Digestor.digest( + name: template.virtual_path, + finder: @lookup_context, + dependencies: view.view_cache_dependencies + ).presence + end + def render_partial(partial_renderer, view_context, opts) partial_renderer.render(view_context, opts, nil) end From f5200ea1a4311791552b99c9ce8c2f023c521bf1 Mon Sep 17 00:00:00 2001 From: drusepth Date: Tue, 16 Aug 2022 17:13:33 -0700 Subject: [PATCH 11/12] add flagging directly from post dropdown for moderators --- .../thredded/moderation/_post_moderation_record.html.erb | 2 +- app/views/thredded/posts_common/_actions.html.erb | 3 +++ app/views/thredded/posts_common/actions/_flag.html.erb | 5 +++++ config/locales/en.yml | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 app/views/thredded/posts_common/actions/_flag.html.erb diff --git a/app/views/thredded/moderation/_post_moderation_record.html.erb b/app/views/thredded/moderation/_post_moderation_record.html.erb index 4a5426d23..644f74de2 100644 --- a/app/views/thredded/moderation/_post_moderation_record.html.erb +++ b/app/views/thredded/moderation/_post_moderation_record.html.erb @@ -13,7 +13,7 @@ time_ago: time_ago(record.created_at) } %> -
+

<% if record.approved? %> diff --git a/app/views/thredded/posts_common/_actions.html.erb b/app/views/thredded/posts_common/_actions.html.erb index 21bc77ade..1cb363ec5 100644 --- a/app/views/thredded/posts_common/_actions.html.erb +++ b/app/views/thredded/posts_common/_actions.html.erb @@ -18,6 +18,9 @@ <%= render 'thredded/posts_common/actions/mark_as_unread', post: post %> <% end %> <% end %> + <% if user_signed_in? && current_user.forum_moderator? %> + <%= render 'thredded/moderation/post_moderation_actions', post: post %> + <% end %> <% end %> <% end %> diff --git a/app/views/thredded/posts_common/actions/_flag.html.erb b/app/views/thredded/posts_common/actions/_flag.html.erb new file mode 100644 index 000000000..406efe936 --- /dev/null +++ b/app/views/thredded/posts_common/actions/_flag.html.erb @@ -0,0 +1,5 @@ +<%= button_to t('thredded.posts.flag'), post.flag_path, + method: :post, + class: 'thredded--post--report thredded--post--dropdown--actions--item', + data: { confirm: I18n.t('thredded.posts.flag_confirm') } +%> diff --git a/config/locales/en.yml b/config/locales/en.yml index 6e59a8f02..5f70b79d4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -136,6 +136,8 @@ en: delete_confirm: Are you sure you want to delete this post? deleted_notice: Your post has been deleted. edit: :thredded.nav.edit_post + flag: Flag Post as Spam + flag_confirm: Are you sure you want to flag this post as spam? form: content_label: Content create_btn: Submit Reply From edd58a785cd18fb2d7967c1f74aeaa92d3d82fbd Mon Sep 17 00:00:00 2001 From: drusepth Date: Thu, 18 Aug 2022 00:06:39 -0700 Subject: [PATCH 12/12] only show moderation actions on public Posts, not PMs --- app/views/thredded/posts_common/_actions.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/thredded/posts_common/_actions.html.erb b/app/views/thredded/posts_common/_actions.html.erb index 1cb363ec5..8104562f9 100644 --- a/app/views/thredded/posts_common/_actions.html.erb +++ b/app/views/thredded/posts_common/_actions.html.erb @@ -18,7 +18,7 @@ <%= render 'thredded/posts_common/actions/mark_as_unread', post: post %> <% end %> <% end %> - <% if user_signed_in? && current_user.forum_moderator? %> + <% if user_signed_in? && current_user.forum_moderator? && post.instance_variable_get(:@post).is_a?(Thredded::Post) %> <%= render 'thredded/moderation/post_moderation_actions', post: post %> <% end %> <% end %>