From a78df3d57df2b74218d1f06c06f60875c1811895 Mon Sep 17 00:00:00 2001 From: Justin Leveck Date: Thu, 24 Apr 2014 09:48:45 -0700 Subject: [PATCH] Add custom embed_by_username feature Feature to allow each imported post to be created using a different discourse username. A possible use case of this is a multi-author blog where discourse is being used to track comments. This feature allows authors to receive updates when someone leaves a comment on one of their articles because each of the imported posts can be created using the discourse username of the author. --- app/jobs/regular/retrieve_topic.rb | 3 +- app/jobs/scheduled/poll_feed.rb | 116 +++++++++++++++++++++--- config/locales/server.en.yml | 1 + config/site_settings.yml | 1 + lib/topic_retriever.rb | 9 +- public/javascripts/embed.js | 20 +++- spec/components/topic_retriever_spec.rb | 76 ++++++++++------ spec/jobs/poll_feed_spec.rb | 23 ++--- 8 files changed, 184 insertions(+), 65 deletions(-) diff --git a/app/jobs/regular/retrieve_topic.rb b/app/jobs/regular/retrieve_topic.rb index 0b8e6dd2bb1..2820f05e310 100644 --- a/app/jobs/regular/retrieve_topic.rb +++ b/app/jobs/regular/retrieve_topic.rb @@ -13,8 +13,7 @@ module Jobs if args[:user_id] user = User.find_by(id: args[:user_id]) end - - TopicRetriever.new(args[:embed_url], no_throttle: user.try(:staff?)).retrieve + TopicRetriever.new(args[:embed_url], author_username: args[:author_username], no_throttle: user.try(:staff?)).retrieve end end diff --git a/app/jobs/scheduled/poll_feed.rb b/app/jobs/scheduled/poll_feed.rb index fe88c920759..3219fc09be8 100644 --- a/app/jobs/scheduled/poll_feed.rb +++ b/app/jobs/scheduled/poll_feed.rb @@ -14,8 +14,7 @@ module Jobs def execute(args) poll_feed if SiteSetting.feed_polling_enabled? && - SiteSetting.feed_polling_url.present? && - SiteSetting.embed_by_username.present? + SiteSetting.feed_polling_url.present? end def feed_key @@ -23,22 +22,111 @@ module Jobs end def poll_feed - user = User.find_by(username_lower: SiteSetting.embed_by_username.downcase) - return if user.blank? + feed = Feed.new + import_topics(feed.topics) + end - require 'simple-rss' - rss = SimpleRSS.parse open(SiteSetting.feed_polling_url) + private - rss.items.each do |i| - url = i.link - url = i.id if url.blank? || url !~ /^https?\:\/\// - - content = i.content || i.description - if content - TopicEmbed.import(user, url, i.title, CGI.unescapeHTML(content.scrub)) - end + def import_topics(feed_topics) + feed_topics.each do |topic| + import_topic(topic) end end + def import_topic(topic) + if topic.user + TopicEmbed.import(topic.user, topic.url, topic.title, CGI.unescapeHTML(topic.content.scrub)) + end + end + + class Feed + require 'simple-rss' + SimpleRSS.item_tags << SiteSetting.embed_username_key_from_feed.to_sym + + def initialize + @feed_url = SiteSetting.feed_polling_url + end + + def topics + feed_topics = [] + + rss.items.each do |i| + current_feed_topic = FeedTopic.new(i) + feed_topics << current_feed_topic if current_feed_topic.content + end + + return feed_topics + end + + private + + def rss + SimpleRSS.parse open(@feed_url) + end + + end + + class FeedTopic + def initialize(article_rss_item) + @article_rss_item = article_rss_item + end + + def url + link = @article_rss_item.link + if url?(link) + return link + else + return @article_rss_item.id + end + end + + def content + @article_rss_item.content || @article_rss_item.description + end + + def title + @article_rss_item.title + end + + def user + author_user || default_user + end + + private + + def url?(link) + if link.blank? || link !~ /^https?\:\/\// + return false + else + return true + end + end + + def author_username + begin + @article_rss_item.send(SiteSetting.embed_username_key_from_feed.to_sym) + rescue + nil + end + end + + def default_user + find_user(SiteSetting.embed_by_username.downcase) + end + + def author_user + return nil if !author_username.present? + + find_user(author_username) + end + + def find_user(user_name) + User.where(username_lower: user_name).first + end + + end + end + end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 33465cbdfb3..75631b1f16f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -899,6 +899,7 @@ en: feed_polling_enabled: "Whether to import a RSS/ATOM feed as posts" feed_polling_url: "URL of RSS/ATOM feed to import" embed_by_username: "Discourse username of the user who creates the topics" + embed_username_key_from_feed: "Key to pull discourse username from feed" embed_truncate: "Truncate the imported posts" embed_category: "Category of created topics" embed_post_limit: "Maximum number of posts to embed" diff --git a/config/site_settings.yml b/config/site_settings.yml index 03fbb92089d..163b90a6e81 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -469,6 +469,7 @@ embedding: feed_polling_enabled: false feed_polling_url: '' embed_by_username: '' + embed_username_key_from_feed: '' embed_category: '' embed_post_limit: 100 embed_truncate: false diff --git a/lib/topic_retriever.rb b/lib/topic_retriever.rb index 3fb9195f8b2..161f362be4d 100644 --- a/lib/topic_retriever.rb +++ b/lib/topic_retriever.rb @@ -2,6 +2,7 @@ class TopicRetriever def initialize(embed_url, opts=nil) @embed_url = embed_url + @author_username = opts[:author_username] @opts = opts || {} end @@ -46,7 +47,13 @@ class TopicRetriever end def fetch_http - user = User.find_by(username_lower: SiteSetting.embed_by_username.downcase) + if @author_username.nil? + username = SiteSetting.embed_by_username.downcase + else + username = @author_username + end + + user = User.where(username_lower: username.downcase).first return if user.blank? TopicEmbed.import_remote(user, @embed_url) diff --git a/public/javascripts/embed.js b/public/javascripts/embed.js index 5349d6d63d6..e299ae54763 100644 --- a/public/javascripts/embed.js +++ b/public/javascripts/embed.js @@ -1,10 +1,24 @@ /* global discourseUrl */ +/* global discourseUserName */ /* global discourseEmbedUrl */ (function() { - var comments = document.getElementById('discourse-comments'), - iframe = document.createElement('iframe'); - iframe.src = discourseUrl + "embed/comments?embed_url=" + encodeURIComponent(discourseEmbedUrl); + iframe = document.createElement('iframe'); + if (typeof discourseUserName === 'undefined') { + iframe.src = + [ discourseUrl, + 'embed/comments?embed_url=', + encodeURIComponent(discourseEmbedUrl) + ].join(''); + } else { + iframe.src = + [ discourseUrl, + 'embed/comments?embed_url=', + encodeURIComponent(discourseEmbedUrl), + '&discourse_username=', + discourseUserName + ].join(''); + } iframe.id = 'discourse-embed-frame'; iframe.width = "100%"; iframe.frameBorder = "0"; diff --git a/spec/components/topic_retriever_spec.rb b/spec/components/topic_retriever_spec.rb index 7a42d1e77b8..ac1f04622b3 100644 --- a/spec/components/topic_retriever_spec.rb +++ b/spec/components/topic_retriever_spec.rb @@ -4,43 +4,59 @@ require_dependency 'topic_retriever' describe TopicRetriever do let(:embed_url) { "http://eviltrout.com/2013/02/10/why-discourse-uses-emberjs.html" } - let(:topic_retriever) { TopicRetriever.new(embed_url) } + let(:author_username) { "eviltrout" } + let(:topic_retriever) { TopicRetriever.new(embed_url, author_username: author_username) } - it "does not call perform_retrieve when embeddable_host is not set" do - SiteSetting.stubs(:embeddable_host).returns(nil) - topic_retriever.expects(:perform_retrieve).never - topic_retriever.retrieve - end + describe "#retrieve" do + context "when host is invalid" do + before do + topic_retriever.stubs(:invalid_host?).returns(true) + end - it "does not call perform_retrieve when embeddable_host is different than the host of the URL" do - SiteSetting.stubs(:embeddable_host).returns("eviltuna.com") - topic_retriever.expects(:perform_retrieve).never - topic_retriever.retrieve - end - - it "does not call perform_retrieve when the embed url is not a url" do - r = TopicRetriever.new("not a url") - r.expects(:perform_retrieve).never - r.retrieve - end - - context "with a valid host" do - before do - SiteSetting.stubs(:embeddable_host).returns("eviltrout.com") + it "does not perform_retrieve" do + topic_retriever.expects(:perform_retrieve).never + topic_retriever.retrieve + end end - it "calls perform_retrieve if it hasn't been retrieved recently" do - topic_retriever.expects(:perform_retrieve).once - topic_retriever.expects(:retrieved_recently?).returns(false) - topic_retriever.retrieve + context "when topics have been retrieived recently" do + before do + topic_retriever.stubs(:retrieved_recently?).returns(true) + end + + it "does not perform_retrieve" do + topic_retriever.expects(:perform_retrieve).never + topic_retriever.retrieve + end end - it "doesn't call perform_retrieve if it's been retrieved recently" do - topic_retriever.expects(:perform_retrieve).never - topic_retriever.expects(:retrieved_recently?).returns(true) - topic_retriever.retrieve - end + context "when host is not invalid" do + before do + topic_retriever.stubs(:invalid_host?).returns(false) + end + context "when topics have been retrieived recently" do + before do + topic_retriever.stubs(:retrieved_recently?).returns(true) + end + + it "does not perform_retrieve" do + topic_retriever.expects(:perform_retrieve).never + topic_retriever.retrieve + end + end + + context "when topics have not been retrieived recently" do + before do + topic_retriever.stubs(:retrieved_recently?).returns(false) + end + + it "does perform_retrieve" do + topic_retriever.expects(:perform_retrieve).once + topic_retriever.retrieve + end + end + end end end diff --git a/spec/jobs/poll_feed_spec.rb b/spec/jobs/poll_feed_spec.rb index e2648338b73..935965e61f9 100644 --- a/spec/jobs/poll_feed_spec.rb +++ b/spec/jobs/poll_feed_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' require_dependency 'jobs/regular/process_post' describe Jobs::PollFeed do - let(:poller) { Jobs::PollFeed.new } context "execute" do @@ -10,28 +9,22 @@ describe Jobs::PollFeed do let(:embed_by_username) { "eviltrout" } it "requires feed_polling_enabled?" do - SiteSetting.stubs(:feed_polling_enabled?).returns(false) - poller.expects(:poll_feed).never - poller.execute({}) + SiteSetting.stubs(:feed_polling_enabled?).returns(true) + SiteSetting.stubs(:feed_polling_url).returns(nil) + poller.expects(:poll_feed).never + poller.execute({}) end it "requires feed_polling_url" do - SiteSetting.stubs(:feed_polling_url).returns(nil) - poller.expects(:poll_feed).never - poller.execute({}) + SiteSetting.stubs(:feed_polling_enabled?).returns(false) + SiteSetting.stubs(:feed_polling_url).returns(nil) + poller.expects(:poll_feed).never + poller.execute({}) end - it "requires embed_by_username" do - SiteSetting.stubs(:embed_by_username).returns(nil) - poller.expects(:poll_feed).never - poller.execute({}) - end - - it "delegates to poll_feed" do SiteSetting.stubs(:feed_polling_enabled?).returns(true) SiteSetting.stubs(:feed_polling_url).returns(url) - SiteSetting.stubs(:embed_by_username).returns(embed_by_username) poller.expects(:poll_feed).once poller.execute({}) end