From 20740f196cd56f3db776e6439e00b7466f76dcd1 Mon Sep 17 00:00:00 2001 From: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com> Date: Thu, 12 May 2022 10:07:43 -0500 Subject: [PATCH] FIX: handle quote rendering for external Discourse instance (#16722) Gracefully handle quotes from an external discourse instance by stripping quote-controls and including username in the title --- .../discourse/app/widgets/post-cooked.js | 5 ++++ lib/cooked_post_processor.rb | 5 ++-- lib/quote_comparer.rb | 14 +++++++--- spec/lib/cooked_post_processor_spec.rb | 16 +++++++++++ spec/lib/quote_comparer_spec.rb | 28 ++++++++++++++++--- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/discourse/app/widgets/post-cooked.js b/app/assets/javascripts/discourse/app/widgets/post-cooked.js index 8d4029d481e..5ccbde2f92a 100644 --- a/app/assets/javascripts/discourse/app/widgets/post-cooked.js +++ b/app/assets/javascripts/discourse/app/widgets/post-cooked.js @@ -282,6 +282,11 @@ export default class PostCooked { this._updateQuoteElements($aside, "chevron-down"); const $title = $(".title", $aside); + // If post/topic is not found then display username, skip controls + if (e.classList.contains("quote-post-not-found")) { + return (e.querySelector(".title").innerHTML = e.dataset.username); + } + // Unless it's a full quote, allow click to expand if (!($aside.data("full") || $title.data("has-quote-controls"))) { $title.on("click", (e2) => { diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index 1160597ff07..e8f48d73138 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -78,9 +78,8 @@ class CookedPostProcessor q.css('blockquote').text ) - if comparer.modified? - q['class'] = ((q['class'] || '') + " quote-modified").strip - end + q['class'] = ((q['class'] || '') + " quote-post-not-found").strip if comparer.missing? + q['class'] = ((q['class'] || '') + " quote-modified").strip if comparer.modified? end end end diff --git a/lib/quote_comparer.rb b/lib/quote_comparer.rb index 74f39f84a77..5f25c7138f6 100644 --- a/lib/quote_comparer.rb +++ b/lib/quote_comparer.rb @@ -15,12 +15,18 @@ class QuoteComparer # This algorithm is far from perfect, but it follows the Discourse # philosophy of "catch the obvious cases, leave moderation for the # complicated ones" + def missing? + return true if @parent_post.blank? + end + def modified? - return true if @text.blank? || @parent_post.blank? + return true if @text.blank? - parent_text = Nokogiri::HTML5::fragment(@parent_post.cooked).text.delete(QuoteComparer.whitespace) - text = @text.delete(QuoteComparer.whitespace) + if @parent_post + parent_text = Nokogiri::HTML5::fragment(@parent_post.cooked).text.delete(QuoteComparer.whitespace) + text = @text.delete(QuoteComparer.whitespace) - !parent_text.include?(text) + !parent_text.include?(text) + end end end diff --git a/spec/lib/cooked_post_processor_spec.rb b/spec/lib/cooked_post_processor_spec.rb index e27261e1ea6..8f4a2b925b3 100644 --- a/spec/lib/cooked_post_processor_spec.rb +++ b/spec/lib/cooked_post_processor_spec.rb @@ -1753,6 +1753,22 @@ describe CookedPostProcessor do end end + context "external discourse instance quote" do + let(:external_raw) do + <<~RAW.strip + [quote="random_guy_not_from_our_discourse, post:2004, topic:401"] + this quote is not from our discourse + [/quote] + and this is a reply + RAW + end + let(:cp) { Fabricate(:post, raw: external_raw) } + + it "it should be marked as missing" do + cpp.post_process_quotes + expect(cpp.doc.css('aside.quote.quote-post-not-found')).to be_present + end + end end context "full quote on direct reply" do diff --git a/spec/lib/quote_comparer_spec.rb b/spec/lib/quote_comparer_spec.rb index 76e43056017..9e67a59878f 100644 --- a/spec/lib/quote_comparer_spec.rb +++ b/spec/lib/quote_comparer_spec.rb @@ -4,6 +4,30 @@ require 'quote_comparer' describe QuoteComparer do + describe "#missing?" do + fab!(:post) { Fabricate(:post, raw: "This has **text** we _are_ matching") } + + it "returns true for missing topic and post" do + expect(QuoteComparer.new(nil, nil, "test")).to be_missing + end + + it "returns true for missing topic" do + expect(QuoteComparer.new(nil, post.post_number, "test")).to be_missing + end + + it "returns true for missing post" do + expect(QuoteComparer.new(post.topic_id, nil, "test")).to be_missing + end + + it "returns false for only missing text" do + expect(QuoteComparer.new(post.topic_id, post.post_number, nil)).to_not be_missing + end + + it "returns false for no missing topic and post" do + expect(QuoteComparer.new(post.topic_id, post.post_number, "test")).to_not be_missing + end + end + describe "#modified?" do fab!(:post) { Fabricate(:post, raw: "This has **text** we _are_ matching") } @@ -11,10 +35,6 @@ describe QuoteComparer do QuoteComparer.new(post.topic_id, post.post_number, text) end - it "returns true for no post" do - expect(QuoteComparer.new(nil, nil, "test")).to be_modified - end - it "returns true for nil text" do expect(qc(nil)).to be_modified end