FEATURE: Update topic/comment embedding parameters (#20181)
This commit implements many changes to topic and comments embedding. It deprecates the class_name field from EmbeddableHost and suggests using the className parameter. discourse_username parameter has been deprecated and it will fetch it from embedded site from the author or discourse-username meta. See the updated code sample from Admin > Customize > Embedding page. * FEATURE: Add className parameter for Discourse embed * DEV: Hide class_name from EmbeddableHost * DEV: Deprecate class_name field of EmbeddableHost * FEATURE: Use either author or discourse-username meta tag * DEV: Deprecate discourse_username parameter * DEV: Improve embed code sample
This commit is contained in:
parent
4855a2879c
commit
ccb345bd88
|
@ -9,15 +9,6 @@
|
|||
autofocus={{true}}
|
||||
/>
|
||||
</td>
|
||||
<td class="editing-input">
|
||||
<div class="label">{{i18n "admin.embedding.class_name"}}</div>
|
||||
<Input
|
||||
@value={{this.buffered.class_name}}
|
||||
placeholder="class"
|
||||
@enter={{action "save"}}
|
||||
class="class-name"
|
||||
/>
|
||||
</td>
|
||||
<td class="editing-input">
|
||||
<div class="label">{{i18n "admin.embedding.allowed_paths"}}</div>
|
||||
<Input
|
||||
|
@ -54,12 +45,6 @@
|
|||
<div class="label">{{i18n "admin.embedding.host"}}</div>
|
||||
{{this.host.host}}
|
||||
</td>
|
||||
<td>
|
||||
<div class="label">
|
||||
{{i18n "admin.embedding.class_name"}}
|
||||
</div>
|
||||
{{this.host.class_name}}
|
||||
</td>
|
||||
<td>
|
||||
<div class="label">
|
||||
{{i18n "admin.embedding.allowed_paths"}}
|
||||
|
|
|
@ -16,10 +16,14 @@ export default Controller.extend({
|
|||
@discourseComputed("embedding.base_url")
|
||||
embeddingCode(baseUrl) {
|
||||
const html = `<div id='discourse-comments'></div>
|
||||
<meta name='discourse-username' content='DISCOURSE_USERNAME'>
|
||||
|
||||
<script type="text/javascript">
|
||||
DiscourseEmbed = { discourseUrl: '${baseUrl}/',
|
||||
discourseEmbedUrl: 'REPLACE_ME' };
|
||||
DiscourseEmbed = {
|
||||
discourseUrl: '${baseUrl}/',
|
||||
discourseEmbedUrl: 'EMBED_URL',
|
||||
// className: 'CLASS_NAME',
|
||||
};
|
||||
|
||||
(function() {
|
||||
var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
{{#if this.embedding.embeddable_hosts}}
|
||||
<table class="embedding grid">
|
||||
<thead>
|
||||
<th style="width: 25%">{{i18n "admin.embedding.host"}}</th>
|
||||
<th style="width: 15%">{{i18n "admin.embedding.class_name"}}</th>
|
||||
<th style="width: 25%">{{i18n "admin.embedding.allowed_paths"}}</th>
|
||||
<th style="width: 25%">{{i18n "admin.embedding.category"}}</th>
|
||||
<th style="width: 30%">{{i18n "admin.embedding.host"}}</th>
|
||||
<th style="width: 30%">{{i18n "admin.embedding.allowed_paths"}}</th>
|
||||
<th style="width: 30%">{{i18n "admin.embedding.category"}}</th>
|
||||
<th style="width: 10%"> </th>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -28,7 +27,7 @@
|
|||
|
||||
{{#if this.showSecondary}}
|
||||
<div class="embedding-secondary">
|
||||
<p>{{html-safe (i18n "admin.embedding.sample")}}</p>
|
||||
{{html-safe (i18n "admin.embedding.sample")}}
|
||||
<HighlightedCode @code={{this.embeddingCode}} @lang="html" />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ class Admin::EmbeddableHostsController < Admin::AdminController
|
|||
def save_host(host, action)
|
||||
host.host = params[:embeddable_host][:host]
|
||||
host.allowed_paths = params[:embeddable_host][:allowed_paths]
|
||||
host.class_name = params[:embeddable_host][:class_name]
|
||||
host.category_id = params[:embeddable_host][:category_id]
|
||||
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
|
||||
|
||||
|
|
|
@ -160,14 +160,30 @@ class EmbedController < ApplicationController
|
|||
|
||||
def prepare_embeddable
|
||||
response.headers.delete("X-Frame-Options")
|
||||
@embeddable_css_class = ""
|
||||
embeddable_host = EmbeddableHost.record_for_url(request.referer)
|
||||
@embeddable_css_class =
|
||||
" class=\"#{embeddable_host.class_name}\"" if embeddable_host.present? &&
|
||||
embeddable_host.class_name.present?
|
||||
|
||||
@data_referer = request.referer
|
||||
@data_referer = "*" if SiteSetting.embed_any_origin? && @data_referer.blank?
|
||||
embeddable_host = EmbeddableHost.record_for_url(request.referer)
|
||||
|
||||
@embeddable_css_class =
|
||||
if params[:class_name]
|
||||
" class=\"#{CGI.escapeHTML(params[:class_name])}\""
|
||||
elsif embeddable_host.present? && embeddable_host.class_name.present?
|
||||
Discourse.deprecate(
|
||||
"class_name field of EmbeddableHost has been deprecated. Prefer passing class_name as a parameter.",
|
||||
since: "3.1.0.beta1",
|
||||
drop_from: "3.2",
|
||||
)
|
||||
|
||||
" class=\"#{CGI.escapeHTML(embeddable_host.class_name)}\""
|
||||
else
|
||||
""
|
||||
end
|
||||
|
||||
@data_referer =
|
||||
if SiteSetting.embed_any_origin? && @data_referer.blank?
|
||||
"*"
|
||||
else
|
||||
request.referer
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_api_request
|
||||
|
|
|
@ -145,7 +145,8 @@ class TopicEmbed < ActiveRecord::Base
|
|||
end
|
||||
|
||||
raw_doc = Nokogiri.HTML5(html)
|
||||
auth_element = raw_doc.at('meta[@name="author"]')
|
||||
auth_element =
|
||||
raw_doc.at('meta[@name="discourse-username"]') || raw_doc.at('meta[@name="author"]')
|
||||
if auth_element.present?
|
||||
response.author = User.where(username_lower: auth_element[:content].strip).first
|
||||
end
|
||||
|
@ -202,12 +203,13 @@ class TopicEmbed < ActiveRecord::Base
|
|||
response
|
||||
end
|
||||
|
||||
def self.import_remote(import_user, url, opts = nil)
|
||||
def self.import_remote(url, opts = nil)
|
||||
opts = opts || {}
|
||||
response = find_remote(url)
|
||||
return if response.nil?
|
||||
|
||||
response.title = opts[:title] if opts[:title].present?
|
||||
import_user = opts[:user] if opts[:user].present?
|
||||
import_user = response.author if response.author.present?
|
||||
|
||||
TopicEmbed.import(import_user, url, response.title, response.body)
|
||||
|
|
|
@ -6113,10 +6113,14 @@ en:
|
|||
embedding:
|
||||
get_started: "If you'd like to embed Discourse on another website, begin by adding its host."
|
||||
confirm_delete: "Are you sure you want to delete that host?"
|
||||
sample: "Paste the following HTML code into your site to create and embed Discourse topics. Replace <b>REPLACE_ME</b> with the canonical URL of the page you are embedding it on."
|
||||
sample: |
|
||||
<p>Paste the following HTML code into your site to create and embed Discourse topics. Replace <b>EMBED_URL</b> with the canonical URL of the page you are embedding it on.</p>
|
||||
|
||||
<p>If you want to customize the style, uncomment and replace <b>CLASS_NAME</b> with a CSS class defined in the <i>Embedded CSS</i> of your theme.</p>
|
||||
|
||||
<p>Replace <b>DISCOURSE_USERNAME</b> with the Discourse username of the author that should create the topic. Discourse will automatically lookup the user by the <code>content</code> attribute of the <code><meta></code> tags with <code>name</code> attribute set to <code>discourse-username</code> or <code>author</code>. The <code>discourseUserName</code> parameter has been deprecated and will be removed in Discourse 3.2.</p>
|
||||
title: "Embedding"
|
||||
host: "Allowed Hosts"
|
||||
class_name: "Class Name"
|
||||
allowed_paths: "Path Allowlist"
|
||||
edit: "edit"
|
||||
category: "Post to Category"
|
||||
|
|
|
@ -4,7 +4,6 @@ class TopicRetriever
|
|||
def initialize(embed_url, opts = nil)
|
||||
@embed_url = embed_url
|
||||
@opts = opts || {}
|
||||
@author_username = @opts[:author_username]
|
||||
end
|
||||
|
||||
def retrieve
|
||||
|
@ -35,21 +34,18 @@ class TopicRetriever
|
|||
# It's possible another process or job found the embed already. So if that happened bail out.
|
||||
return if TopicEmbed.where(embed_url: @embed_url).exists?
|
||||
|
||||
fetch_http
|
||||
end
|
||||
|
||||
def fetch_http
|
||||
if @author_username.nil?
|
||||
username =
|
||||
SiteSetting.embed_by_username.presence || SiteSetting.site_contact_username.presence ||
|
||||
Discourse.system_user.username
|
||||
else
|
||||
username = @author_username
|
||||
if @opts[:author_username].present?
|
||||
Discourse.deprecate(
|
||||
"discourse_username parameter has been deprecated. Prefer passing author name using a <meta> tag.",
|
||||
since: "3.1.0.beta1",
|
||||
drop_from: "3.2",
|
||||
)
|
||||
end
|
||||
|
||||
user = User.where(username_lower: username.downcase).first
|
||||
return if user.blank?
|
||||
username =
|
||||
@opts[:author_username].present || SiteSetting.embed_by_username.presence ||
|
||||
SiteSetting.site_contact_username.presence || Discourse.system_user.username
|
||||
|
||||
TopicEmbed.import_remote(user, @embed_url)
|
||||
TopicEmbed.import_remote(@embed_url, user: User.find_by(username_lower: username.downcase))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
queryParams.topic_id = DE.topicId;
|
||||
}
|
||||
|
||||
if (DE.className) {
|
||||
queryParams.class_name = DE.className;
|
||||
}
|
||||
|
||||
var src = DE.discourseUrl + "embed/comments";
|
||||
var keys = Object.keys(queryParams);
|
||||
if (keys.length > 0) {
|
||||
|
|
|
@ -64,7 +64,7 @@ class ImportScripts::Disqus < ImportScripts::Base
|
|||
|
||||
topic_user = find_existing_user(t[:author_email], t[:author_username])
|
||||
begin
|
||||
post = TopicEmbed.import_remote(topic_user, t[:link], title: title)
|
||||
post = TopicEmbed.import_remote(t[:link], title: title, user: topic_user)
|
||||
post.topic.update_column(:category_id, @category.id)
|
||||
rescue OpenURI::HTTPError
|
||||
post = nil
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
RSpec.describe TopicRetriever do
|
||||
let(:embed_url) { "http://eviltrout.com/2013/02/10/why-discourse-uses-emberjs.html" }
|
||||
let(:author_username) { "eviltrout" }
|
||||
let(:topic_retriever) { TopicRetriever.new(embed_url, author_username: author_username) }
|
||||
let(:topic_retriever) { TopicRetriever.new(embed_url) }
|
||||
|
||||
it "can initialize without optional parameters" do
|
||||
t = TopicRetriever.new(embed_url)
|
||||
|
|
|
@ -56,7 +56,6 @@ RSpec.describe Admin::EmbeddableHostsController do
|
|||
params: {
|
||||
embeddable_host: {
|
||||
host: "test.com",
|
||||
class_name: "test-class",
|
||||
category_id: category.id,
|
||||
},
|
||||
}
|
||||
|
@ -67,7 +66,7 @@ RSpec.describe Admin::EmbeddableHostsController do
|
|||
UserHistory.where(
|
||||
acting_user_id: admin.id,
|
||||
action: UserHistory.actions[:embeddable_host_update],
|
||||
new_value: "category_id: #{category.id}, class_name: test-class, host: test.com",
|
||||
new_value: "category_id: #{category.id}, host: test.com",
|
||||
).exists?
|
||||
|
||||
expect(history_exists).to eq(true)
|
||||
|
@ -82,7 +81,6 @@ RSpec.describe Admin::EmbeddableHostsController do
|
|||
params: {
|
||||
embeddable_host: {
|
||||
host: "test.com",
|
||||
class_name: "test-class",
|
||||
category_id: category.id,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -5,32 +5,13 @@ RSpec.describe EmbedController do
|
|||
let(:embed_url_secure) { "https://eviltrout.com/2013/02/10/why-discourse-uses-emberjs.html" }
|
||||
let(:discourse_username) { "eviltrout" }
|
||||
|
||||
it "is 404 without an embed_url" do
|
||||
get "/embed/comments"
|
||||
expect(response.body).to match(I18n.t("embed.error"))
|
||||
end
|
||||
|
||||
it "raises an error with a missing host" do
|
||||
get "/embed/comments", params: { embed_url: embed_url }
|
||||
expect(response.body).to match(I18n.t("embed.error"))
|
||||
end
|
||||
|
||||
describe "by topic id" do
|
||||
let(:headers) { { "REFERER" => "http://eviltrout.com/some-page" } }
|
||||
|
||||
before { Fabricate(:embeddable_host) }
|
||||
|
||||
it "allows a topic to be embedded by id" do
|
||||
topic = Fabricate(:topic)
|
||||
get "/embed/comments", params: { topic_id: topic.id }, headers: headers
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
fab!(:topic) { Fabricate(:topic) }
|
||||
|
||||
describe "#info" do
|
||||
context "without api key" do
|
||||
it "fails" do
|
||||
get "/embed/info.json"
|
||||
|
||||
expect(response.body).to match(I18n.t("embed.error"))
|
||||
end
|
||||
end
|
||||
|
@ -51,10 +32,9 @@ RSpec.describe EmbedController do
|
|||
HTTP_API_USERNAME: "system",
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
expect(json["topic_id"]).to eq(topic_embed.topic.id)
|
||||
expect(json["post_id"]).to eq(topic_embed.post.id)
|
||||
expect(json["topic_slug"]).to eq(topic_embed.topic.slug)
|
||||
expect(response.parsed_body["topic_id"]).to eq(topic_embed.topic.id)
|
||||
expect(response.parsed_body["post_id"]).to eq(topic_embed.post.id)
|
||||
expect(response.parsed_body["topic_slug"]).to eq(topic_embed.topic.slug)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -79,6 +59,7 @@ RSpec.describe EmbedController do
|
|||
describe "#topics" do
|
||||
it "raises an error when not enabled" do
|
||||
get "/embed/topics?embed_id=de-1234"
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
|
@ -87,15 +68,16 @@ RSpec.describe EmbedController do
|
|||
|
||||
it "raises an error with a weird id" do
|
||||
get "/embed/topics?discourse_embed_id=../asdf/-1234", headers: headers
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "returns a list of topics" do
|
||||
topic = Fabricate(:topic)
|
||||
get "/embed/topics?discourse_embed_id=de-1234",
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/evil-trout",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).to match("data-embed-id=\"de-1234\"")
|
||||
|
@ -104,7 +86,6 @@ RSpec.describe EmbedController do
|
|||
end
|
||||
|
||||
it "returns a list of top topics" do
|
||||
bad_topic = Fabricate(:topic)
|
||||
good_topic = Fabricate(:topic, like_count: 1000, posts_count: 100)
|
||||
TopTopic.refresh!
|
||||
|
||||
|
@ -116,13 +97,11 @@ RSpec.describe EmbedController do
|
|||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).to match("data-embed-id=\"de-1234\"")
|
||||
expect(response.body).to match("data-topic-id=\"#{good_topic.id}\"")
|
||||
expect(response.body).not_to match("data-topic-id=\"#{bad_topic.id}\"")
|
||||
expect(response.body).not_to match("data-topic-id=\"#{topic.id}\"")
|
||||
expect(response.body).to match("data-referer=\"https://example.com/evil-trout\"")
|
||||
end
|
||||
|
||||
it "returns a list of topics if the top_period is not valid" do
|
||||
topic1 = Fabricate(:topic)
|
||||
topic2 = Fabricate(:topic)
|
||||
good_topic = Fabricate(:topic, like_count: 1000, posts_count: 100)
|
||||
TopTopic.refresh!
|
||||
TopicQuery.any_instance.expects(:list_top_for).never
|
||||
|
@ -131,21 +110,21 @@ RSpec.describe EmbedController do
|
|||
headers: {
|
||||
"REFERER" => "https://example.com/evil-trout",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).to match("data-embed-id=\"de-1234\"")
|
||||
expect(response.body).to match("data-topic-id=\"#{good_topic.id}\"")
|
||||
expect(response.body).to match("data-topic-id=\"#{topic1.id}\"")
|
||||
expect(response.body).to match("data-topic-id=\"#{topic2.id}\"")
|
||||
expect(response.body).to match("data-topic-id=\"#{topic.id}\"")
|
||||
expect(response.body).to match("data-referer=\"https://example.com/evil-trout\"")
|
||||
end
|
||||
|
||||
it "wraps the list in a custom class" do
|
||||
topic = Fabricate(:topic)
|
||||
get "/embed/topics?discourse_embed_id=de-1234&embed_class=my-special-class",
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/evil-trout",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).to match("class='topics-list my-special-class'")
|
||||
|
@ -153,222 +132,293 @@ RSpec.describe EmbedController do
|
|||
|
||||
it "returns no referer if not supplied" do
|
||||
get "/embed/topics?discourse_embed_id=de-1234"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to match("data-referer=\"\"")
|
||||
end
|
||||
|
||||
it "returns * for the referer if `embed_any_origin` is set" do
|
||||
SiteSetting.embed_any_origin = true
|
||||
|
||||
get "/embed/topics?discourse_embed_id=de-1234"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to match("data-referer=\"\\*\"")
|
||||
end
|
||||
|
||||
it "disallows indexing the embed topic list" do
|
||||
topic = Fabricate(:topic)
|
||||
get "/embed/topics?discourse_embed_id=de-1234",
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/evil-trout",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Robots-Tag"]).to match(/noindex/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a host" do
|
||||
let!(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||
let(:headers) { { "REFERER" => embed_url } }
|
||||
describe "#comments" do
|
||||
it "is 404 without an embed_url" do
|
||||
get "/embed/comments"
|
||||
|
||||
before { Jobs.run_immediately! }
|
||||
expect(response.body).to match(I18n.t("embed.error"))
|
||||
end
|
||||
|
||||
it "doesn't raises an error with no referer" do
|
||||
it "raises an error with a missing host" do
|
||||
get "/embed/comments", params: { embed_url: embed_url }
|
||||
expect(response.body).not_to match(I18n.t("embed.error"))
|
||||
|
||||
expect(response.body).to match(I18n.t("embed.error"))
|
||||
end
|
||||
|
||||
it "includes CSS from embedded_scss field" do
|
||||
theme = Fabricate(:theme)
|
||||
theme.set_default!
|
||||
describe "by topic id" do
|
||||
fab!(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||
|
||||
ThemeField.create!(
|
||||
theme_id: theme.id,
|
||||
name: "embedded_scss",
|
||||
target_id: 0,
|
||||
type_id: 1,
|
||||
value: ".test-osama-15 {\n" + " color: red;\n" + "}\n",
|
||||
)
|
||||
it "allows a topic to be embedded by id" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
topic_id: topic.id,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/some-page",
|
||||
}
|
||||
|
||||
topic_embed = Fabricate(:topic_embed, embed_url: embed_url)
|
||||
post = Fabricate(:post, topic: topic_embed.topic)
|
||||
|
||||
get "/embed/comments", params: { embed_url: embed_url }, headers: headers
|
||||
|
||||
html = Nokogiri::HTML5.fragment(response.body)
|
||||
css_link = html.at("link[data-target=embedded_theme]").attribute("href").value
|
||||
|
||||
get css_link
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to include(".test-osama-15")
|
||||
end
|
||||
|
||||
it "includes HTML from embedded_header field" do
|
||||
theme = Fabricate(:theme)
|
||||
theme.set_default!
|
||||
|
||||
ThemeField.create!(
|
||||
theme_id: theme.id,
|
||||
name: "embedded_header",
|
||||
target_id: 0,
|
||||
type_id: 0,
|
||||
value: "<strong class='custom-text'>hey there!</strong>\n",
|
||||
)
|
||||
|
||||
topic_embed = Fabricate(:topic_embed, embed_url: embed_url)
|
||||
post = Fabricate(:post, topic: topic_embed.topic)
|
||||
|
||||
get "/embed/comments", params: { embed_url: embed_url }, headers: headers
|
||||
|
||||
html = Nokogiri::HTML5.fragment(response.body)
|
||||
custom_header = html.at(".custom-text")
|
||||
|
||||
expect(custom_header.name).to eq("strong")
|
||||
expect(custom_header.text).to eq("hey there!")
|
||||
end
|
||||
|
||||
context "with success" do
|
||||
after do
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "with a host" do
|
||||
fab!(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||
|
||||
before { Jobs.run_immediately! }
|
||||
|
||||
it "doesn't raise an error with no referer" do
|
||||
get "/embed/comments", params: { embed_url: embed_url }
|
||||
|
||||
expect(response.body).not_to match(I18n.t("embed.error"))
|
||||
end
|
||||
|
||||
it "tells the topic retriever to work when no previous embed is found" do
|
||||
TopicEmbed.expects(:topic_id_for_embed).returns(nil)
|
||||
retriever = mock
|
||||
TopicRetriever.expects(:new).returns(retriever)
|
||||
retriever.expects(:retrieve)
|
||||
get "/embed/comments", params: { embed_url: embed_url }, headers: headers
|
||||
end
|
||||
it "includes CSS from embedded_scss field" do
|
||||
theme = Fabricate(:theme)
|
||||
theme.set_default!
|
||||
|
||||
ThemeField.create!(
|
||||
theme_id: theme.id,
|
||||
name: "embedded_scss",
|
||||
target_id: 0,
|
||||
type_id: 1,
|
||||
value: ".test-osama-15 { color: red }",
|
||||
)
|
||||
|
||||
it "displays the right view" do
|
||||
topic_embed = Fabricate(:topic_embed, embed_url: embed_url)
|
||||
post = Fabricate(:post, topic: topic_embed.topic)
|
||||
|
||||
get "/embed/comments", params: { embed_url: embed_url_secure }, headers: headers
|
||||
get "/embed/comments", params: { embed_url: embed_url }, headers: { "REFERER" => embed_url }
|
||||
|
||||
expect(response.body).to match(I18n.t("embed.start_discussion"))
|
||||
html = Nokogiri::HTML5.fragment(response.body)
|
||||
get html.at("link[data-target=embedded_theme]").attribute("href").value
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to include(".test-osama-15")
|
||||
end
|
||||
|
||||
it "creates a topic view when a topic_id is found" do
|
||||
it "includes HTML from embedded_header field" do
|
||||
theme = Fabricate(:theme)
|
||||
theme.set_default!
|
||||
|
||||
ThemeField.create!(
|
||||
theme_id: theme.id,
|
||||
name: "embedded_header",
|
||||
target_id: 0,
|
||||
type_id: 0,
|
||||
value: "<strong class='custom-text'>hey there!</strong>\n",
|
||||
)
|
||||
|
||||
topic_embed = Fabricate(:topic_embed, embed_url: embed_url)
|
||||
post = Fabricate(:post, topic: topic_embed.topic)
|
||||
|
||||
get "/embed/comments", params: { embed_url: embed_url }, headers: headers
|
||||
|
||||
expect(response.body).to match(I18n.t("embed.continue"))
|
||||
expect(response.body).to match(post.cooked)
|
||||
expect(response.body).to match("<span class='replies'>1 reply</span>")
|
||||
html = Nokogiri::HTML5.fragment(response.body)
|
||||
custom_header = html.at(".custom-text")
|
||||
|
||||
small_action = Fabricate(:small_action, topic: topic_embed.topic)
|
||||
|
||||
get "/embed/comments", params: { embed_url: embed_url }, headers: headers
|
||||
|
||||
expect(response.body).not_to match("post-#{small_action.id}")
|
||||
expect(response.body).to match("<span class='replies'>1 reply</span>")
|
||||
expect(custom_header.name).to eq("strong")
|
||||
expect(custom_header.text).to eq("hey there!")
|
||||
end
|
||||
|
||||
it "provides the topic retriever with the discourse username when provided" do
|
||||
retriever = mock
|
||||
retriever.expects(:retrieve).returns(nil)
|
||||
TopicRetriever
|
||||
.expects(:new)
|
||||
.with(embed_url, has_entry(author_username: discourse_username))
|
||||
.returns(retriever)
|
||||
context "with success" do
|
||||
it "tells the topic retriever to work when no previous embed is found" do
|
||||
TopicRetriever.any_instance.expects(:retrieve)
|
||||
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
discourse_username: discourse_username,
|
||||
},
|
||||
headers: headers
|
||||
end
|
||||
end
|
||||
end
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => embed_url,
|
||||
}
|
||||
|
||||
context "with multiple hosts" do
|
||||
before do
|
||||
Fabricate(:embeddable_host)
|
||||
Fabricate(:embeddable_host, host: "http://discourse.org")
|
||||
Fabricate(:embeddable_host, host: "https://example.com/1234", class_name: "example")
|
||||
end
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
end
|
||||
|
||||
context "with success" do
|
||||
it "works with the first host" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/wat/1-2-3.html",
|
||||
}
|
||||
it "displays the right view" do
|
||||
topic_embed = Fabricate(:topic_embed, embed_url: embed_url)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url_secure,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => embed_url,
|
||||
}
|
||||
|
||||
it "works with the second host" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/wat/1-2-3.html",
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).to match(I18n.t("embed.start_discussion"))
|
||||
end
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
it "creates a topic view when a topic_id is found" do
|
||||
topic_embed = Fabricate(:topic_embed, embed_url: embed_url)
|
||||
|
||||
it "works with a host with a path" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/some-other-path",
|
||||
}
|
||||
post = Fabricate(:post, topic: topic_embed.topic)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => embed_url,
|
||||
}
|
||||
|
||||
it "contains custom class name" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/some-other-path",
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).to match(I18n.t("embed.continue"))
|
||||
expect(response.body).to match(post.cooked)
|
||||
expect(response.body).to match("<span class='replies'>1 reply</span>")
|
||||
|
||||
expect(response.body).to match('class="example"')
|
||||
small_action = Fabricate(:small_action, topic: topic_embed.topic)
|
||||
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => embed_url,
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
expect(response.body).not_to match("post-#{small_action.id}")
|
||||
expect(response.body).to match("<span class='replies'>1 reply</span>")
|
||||
end
|
||||
|
||||
it "provides the topic retriever with the discourse username when provided" do
|
||||
TopicRetriever.any_instance.expects(:retrieve).returns(nil)
|
||||
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
discourse_username: discourse_username,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => embed_url,
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers["X-Frame-Options"]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with CSP frame-ancestors enabled" do
|
||||
before { SiteSetting.content_security_policy_frame_ancestors = true }
|
||||
context "with multiple hosts" do
|
||||
fab!(:embeddable_host_1) { Fabricate(:embeddable_host) }
|
||||
fab!(:embeddable_host_2) { Fabricate(:embeddable_host, host: "http://discourse.org") }
|
||||
fab!(:embeddable_host_3) do
|
||||
Fabricate(:embeddable_host, host: "https://example.com/1234", class_name: "example")
|
||||
end
|
||||
|
||||
it "includes all the hosts" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/wat/1-2-3.html",
|
||||
}
|
||||
context "with success" do
|
||||
it "works with the first host" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/wat/1-2-3.html",
|
||||
}
|
||||
|
||||
expect(response.headers["Content-Security-Policy"]).to match(
|
||||
%r{frame-ancestors.*https://discourse\.org},
|
||||
)
|
||||
expect(response.headers["Content-Security-Policy"]).to match(
|
||||
%r{frame-ancestors.*https://example\.com},
|
||||
)
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "works with the second host" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/wat/1-2-3.html",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "works with a host with a path" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/some-other-path",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "contains custom class name" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/some-other-path",
|
||||
}
|
||||
|
||||
expect(response.body).to match('class="example"')
|
||||
end
|
||||
|
||||
it "contains custom class name from params" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
class_name: "param-class-name",
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "https://example.com/some-other-path",
|
||||
}
|
||||
|
||||
expect(response.body).to match('class="param-class-name"')
|
||||
end
|
||||
end
|
||||
|
||||
context "with CSP frame-ancestors enabled" do
|
||||
before { SiteSetting.content_security_policy_frame_ancestors = true }
|
||||
|
||||
it "includes all the hosts" do
|
||||
get "/embed/comments",
|
||||
params: {
|
||||
embed_url: embed_url,
|
||||
},
|
||||
headers: {
|
||||
"REFERER" => "http://eviltrout.com/wat/1-2-3.html",
|
||||
}
|
||||
|
||||
expect(response.headers["Content-Security-Policy"]).to match(
|
||||
%r{frame-ancestors.*https://discourse\.org},
|
||||
)
|
||||
expect(response.headers["Content-Security-Policy"]).to match(
|
||||
%r{frame-ancestors.*https://example\.com},
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue