Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport 'Restrict comments replies tree including polymorphism' to v0.27 #12305

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions decidim-comments/app/models/decidim/comments/comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Comment < ApplicationRecord
translatable_fields :body

parent_item_foreign_key :decidim_commentable_id
parent_item_polymorphic_type_key :decidim_commentable_type

belongs_to :commentable, foreign_key: "decidim_commentable_id", foreign_type: "decidim_commentable_type", polymorphic: true
belongs_to :root_commentable, foreign_key: "decidim_root_commentable_id", foreign_type: "decidim_root_commentable_type", polymorphic: true, touch: true
Expand Down
15 changes: 14 additions & 1 deletion decidim-core/lib/decidim/acts_as_tree.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

module Decidim
# Adapted from https://hashrocket.com/blog/posts/recursive-sql-in-activerecord
module ActsAsTree
extend ActiveSupport::Concern

Expand All @@ -15,10 +16,22 @@ def parent_item_foreign_key(name = nil)
@parent_item_foreign_key = name
end

def parent_item_polymorphic_type_key(name = nil)
return @parent_item_polymorphic_type_key unless name

@parent_item_polymorphic_type_key = name
end

def tree_for(item)
where(Arel.sql("#{table_name}.id IN (#{tree_sql_for(item)})")).order("#{table_name}.id")
end

def polymorphic_condition(item)
return "" if parent_item_polymorphic_type_key.blank?

"AND #{table_name}.#{parent_item_polymorphic_type_key} = '#{item.class.name}'"
end

def tree_sql_for(item)
<<-SQL.squish
WITH RECURSIVE search_tree(id, path) AS (
Expand All @@ -28,7 +41,7 @@ def tree_sql_for(item)
UNION ALL
SELECT #{table_name}.id, path || #{table_name}.id
FROM search_tree
JOIN #{table_name} ON #{table_name}.#{parent_item_foreign_key} = search_tree.id
JOIN #{table_name} ON #{table_name}.#{parent_item_foreign_key} = search_tree.id #{polymorphic_condition(item)}
WHERE NOT #{table_name}.id = ANY(path)
)
SELECT id FROM search_tree ORDER BY path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,38 @@
expect(page).to have_css(".comments > div:nth-child(2)", text: "Most Rated Comment")
end

context "when there are comments and replies" do
let!(:single_comment) { create(:comment, commentable: commentable) }
let!(:reply) { create(:comment, commentable: single_comment, root_commentable: commentable) }

it "displays the show replies link on comment with reply" do
visit resource_path
expect(page).not_to have_content("Comments are disabled at this time")
expect(page).to have_css(".comment", minimum: 1)

within "#comment_#{single_comment.id}" do
expect(page).to have_content "Hide replies"
end
end

context "when there is a comment with the same parent id but different type with replies" do
let!(:other_component) { create(:component, manifest_name: :dummy, organization: organization) }
let!(:other_commentable) { create(:dummy_resource, component: other_component, author: user, id: single_comment.id) }
let!(:reply) { create(:comment, commentable: other_commentable, root_commentable: other_commentable) }
let!(:other_reply) { create(:comment, commentable: reply, root_commentable: other_commentable) }

it "displays the show replies link on comment with reply" do
visit resource_path
expect(page).not_to have_content("Comments are disabled at this time")
expect(page).to have_css(".comment", minimum: 1)

within "#comment_#{single_comment.id}" do
expect(page).not_to have_content "Hide replies"
end
end
end
end

context "when there are deleted comments" do
let(:deleted_comment) { comments[0] }

Expand Down
Loading