From 2b96040a30d37009f97a7a5dab4277001f3bc723 Mon Sep 17 00:00:00 2001 From: r888888888 Date: Thu, 7 Aug 2014 17:43:28 -0700 Subject: [PATCH] fixes #2191 --- app/logical/daily_maintenance.rb | 1 + app/mailers/user_mailer.rb | 7 ++ app/models/forum_post.rb | 22 +++++- app/models/forum_subscription.rb | 22 ++++++ app/models/forum_topic.rb | 19 +++++ .../forum_posts/partials/new/_form.html.erb | 2 + app/views/user_mailer/dmail_notice.html.erb | 3 + app/views/user_mailer/forum_notice.html.erb | 21 ++++++ ...140725003232_create_forum_subscriptions.rb | 13 ++++ db/structure.sql | 70 +++++++++++++++++++ test/factories/forum_subscription.rb | 4 ++ test/unit/forum_post_test.rb | 27 +++++++ 12 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 app/models/forum_subscription.rb create mode 100644 app/views/user_mailer/forum_notice.html.erb create mode 100644 db/migrate/20140725003232_create_forum_subscriptions.rb create mode 100644 test/factories/forum_subscription.rb diff --git a/app/logical/daily_maintenance.rb b/app/logical/daily_maintenance.rb index a62c8d921..6a6aa4dcb 100644 --- a/app/logical/daily_maintenance.rb +++ b/app/logical/daily_maintenance.rb @@ -11,5 +11,6 @@ class DailyMaintenance UserUploadClamper.new.clamp_all! TagSubscription.process_all ApiCacheGenerator.new.generate_tag_cache + ForumSubscription.process_all! end end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 7a9689b70..865725638 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,4 +1,5 @@ class UserMailer < ActionMailer::Base + add_template_helper ApplicationHelper default :from => Danbooru.config.contact_email, :content_type => "text/html" def dmail_notice(dmail) @@ -13,4 +14,10 @@ class UserMailer < ActionMailer::Base def upgrade_fail(email) mail(:to => "#{user.name} <#{email}>", :subject => "#{Danbooru.config.app_name} account upgrade") end + + def forum_notice(user, forum_topic, forum_posts) + @forum_topic = forum_topic + @forum_posts = forum_posts + mail(:to => "#{user.name} <#{user.email}>", :subject => "#{Danbooru.config.app_name} forum topic #{forum_topic.title} updated") + end end diff --git a/app/models/forum_post.rb b/app/models/forum_post.rb index 402d93a19..530544868 100644 --- a/app/models/forum_post.rb +++ b/app/models/forum_post.rb @@ -1,5 +1,5 @@ class ForumPost < ActiveRecord::Base - attr_accessible :body, :topic_id, :as => [:member, :builder, :janitor, :gold, :platinum, :contributor, :admin, :moderator, :default] + attr_accessible :body, :topic_id, :receive_email_notifications, :as => [:member, :builder, :janitor, :gold, :platinum, :contributor, :admin, :moderator, :default] attr_accessible :is_locked, :is_sticky, :is_deleted, :as => [:admin, :moderator, :janitor] attr_readonly :topic_id belongs_to :creator, :class_name => "User" @@ -15,6 +15,8 @@ class ForumPost < ActiveRecord::Base validate :validate_topic_is_unlocked before_destroy :validate_topic_is_unlocked after_save :delete_topic_if_original_post + after_save :update_email_notifications + attr_accessor :receive_email_notifications module SearchMethods def body_matches(body) @@ -188,4 +190,22 @@ class ForumPost < ActiveRecord::Base def hidden_attributes super + [:text_index] end + + def receive_email_notifications + @receive_email_notifications ||= ForumSubscription.where(:forum_topic_id => topic_id, :user_id => CurrentUser.user.id).exists? + end + + def update_email_notifications + subscription = ForumSubscription.where(:forum_topic_id => topic_id, :user_id => CurrentUser.user.id).first + + if receive_email_notifications == "1" + if subscription + subscription.update_attribute(:last_read_at, updated_at) + else + ForumSubscription.create(:forum_topic_id => topic_id, :user_id => CurrentUser.user.id, :last_read_at => updated_at) + end + else + subscription.destroy if subscription + end + end end diff --git a/app/models/forum_subscription.rb b/app/models/forum_subscription.rb new file mode 100644 index 000000000..d71c21753 --- /dev/null +++ b/app/models/forum_subscription.rb @@ -0,0 +1,22 @@ +class ForumSubscription < ActiveRecord::Base + belongs_to :user + belongs_to :forum_topic + attr_accessible :user, :forum_topic, :user_id, :forum_topic_id, :last_read_at, :delete_key + + def self.prune! + where("last_read_at < ?", 3.months.ago).delete_all + end + + def self.process_all! + ForumSubscription.find_each do |subscription| + forum_topic = subscription.forum_topic + if forum_topic.updated_at > subscription.last_read_at + CurrentUser.scoped(subscription.user, "127.0.0.1") do + forum_posts = forum_topic.posts.where("created_at >= ?", subscription.last_read_at).order("id desc") + UserMailer.forum_notice(subscription.user, forum_topic, forum_posts).deliver + subscription.update_attribute(:last_read_at, forum_topic.updated_at) + end + end + end + end +end diff --git a/app/models/forum_topic.rb b/app/models/forum_topic.rb index f7d4c01ff..9761466d0 100644 --- a/app/models/forum_topic.rb +++ b/app/models/forum_topic.rb @@ -11,6 +11,7 @@ class ForumTopic < ActiveRecord::Base belongs_to :updater, :class_name => "User" has_many :posts, lambda {order("forum_posts.id asc")}, :class_name => "ForumPost", :foreign_key => "topic_id", :dependent => :destroy has_one :original_post, lambda {order("forum_posts.id asc")}, :class_name => "ForumPost", :foreign_key => "topic_id" + has_many :subscriptions, :class_name => "ForumSubscription" before_validation :initialize_creator, :on => :create before_validation :initialize_updater before_validation :initialize_is_deleted, :on => :create @@ -99,6 +100,24 @@ class ForumTopic < ActiveRecord::Base end end + module SubscriptionMethods + def update_subscription!(user) + user_subscription = subscriptions.where(:user_id => user.id).first + + if user_subscription + user_subscription.update_attribute(:last_read_at, updated_at) + else + subscriptions.create(:user_id => user.id, :last_read_at => updated_at, :delete_key => SecureRandom.urlsafe_base64(10)) + end + end + + def notify_subscriptions! + ForumSubscription.where(:forum_topic_id => id).where("last_read_at < ?", updated_at).find_each do |subscription| + + end + end + end + extend SearchMethods include CategoryMethods include VisitMethods diff --git a/app/views/forum_posts/partials/new/_form.html.erb b/app/views/forum_posts/partials/new/_form.html.erb index dcede6314..b6a72aff3 100644 --- a/app/views/forum_posts/partials/new/_form.html.erb +++ b/app/views/forum_posts/partials/new/_form.html.erb @@ -4,6 +4,8 @@ <%= f.input :topic_id, :as => :hidden %> <%= dtext_field "forum_post", "body" %> + <%= f.input :receive_email_notifications, :as => :boolean %> + <%= f.button :submit, "Submit" %> <%= dtext_preview_button "forum_post", "body" %> <% end %> diff --git a/app/views/user_mailer/dmail_notice.html.erb b/app/views/user_mailer/dmail_notice.html.erb index a26155ae6..3e47e5fce 100644 --- a/app/views/user_mailer/dmail_notice.html.erb +++ b/app/views/user_mailer/dmail_notice.html.erb @@ -1,5 +1,8 @@ + + <%= stylesheet_link_tag "application", :media => "screen" %> +

<%= h @dmail.from.name %> said:

diff --git a/app/views/user_mailer/forum_notice.html.erb b/app/views/user_mailer/forum_notice.html.erb new file mode 100644 index 000000000..6faff46a9 --- /dev/null +++ b/app/views/user_mailer/forum_notice.html.erb @@ -0,0 +1,21 @@ + + + + <%= stylesheet_link_tag "application", :media => "screen" %> + + +

<%= @forum_topic.title %> was updated:

+ +<% @forum_posts.each do |forum_post| %> +
+

+ <%= forum_post.creator_name %> said: +

+ <%= format_text(forum_post.body) %> +
+<% end %> + +

<%= link_to "View topic", forum_topic_path(@forum_topic, :page => @forum_topic.last_page, :host => Danbooru.config.hostname, :only_path => false) %>

+ + + diff --git a/db/migrate/20140725003232_create_forum_subscriptions.rb b/db/migrate/20140725003232_create_forum_subscriptions.rb new file mode 100644 index 000000000..9264e1694 --- /dev/null +++ b/db/migrate/20140725003232_create_forum_subscriptions.rb @@ -0,0 +1,13 @@ +class CreateForumSubscriptions < ActiveRecord::Migration + def change + create_table :forum_subscriptions do |t| + t.integer :user_id + t.integer :forum_topic_id + t.datetime :last_read_at + t.string :delete_key + end + + add_index :forum_subscriptions, :user_id + add_index :forum_subscriptions, :forum_topic_id + end +end diff --git a/db/structure.sql b/db/structure.sql index c2f6f6fa9..9fa7a23af 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2012,6 +2012,38 @@ CREATE SEQUENCE forum_posts_id_seq ALTER SEQUENCE forum_posts_id_seq OWNED BY forum_posts.id; +-- +-- Name: forum_subscriptions; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE forum_subscriptions ( + id integer NOT NULL, + user_id integer, + forum_topic_id integer, + last_read_at timestamp without time zone, + delete_key character varying(255) +); + + +-- +-- Name: forum_subscriptions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE forum_subscriptions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: forum_subscriptions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE forum_subscriptions_id_seq OWNED BY forum_subscriptions.id; + + -- -- Name: forum_topic_visits; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -3954,6 +3986,13 @@ ALTER TABLE ONLY favorites_99 ALTER COLUMN id SET DEFAULT nextval('favorites_id_ ALTER TABLE ONLY forum_posts ALTER COLUMN id SET DEFAULT nextval('forum_posts_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY forum_subscriptions ALTER COLUMN id SET DEFAULT nextval('forum_subscriptions_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -4300,6 +4339,14 @@ ALTER TABLE ONLY forum_posts ADD CONSTRAINT forum_posts_pkey PRIMARY KEY (id); +-- +-- Name: forum_subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY forum_subscriptions + ADD CONSTRAINT forum_subscriptions_pkey PRIMARY KEY (id); + + -- -- Name: forum_topic_visits_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -6185,6 +6232,20 @@ CREATE INDEX index_forum_posts_on_text_index ON forum_posts USING gin (text_inde CREATE INDEX index_forum_posts_on_topic_id ON forum_posts USING btree (topic_id); +-- +-- Name: index_forum_subscriptions_on_forum_topic_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_forum_subscriptions_on_forum_topic_id ON forum_subscriptions USING btree (forum_topic_id); + + +-- +-- Name: index_forum_subscriptions_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_forum_subscriptions_on_user_id ON forum_subscriptions USING btree (user_id); + + -- -- Name: index_forum_topic_visits_on_forum_topic_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -6192,6 +6253,13 @@ CREATE INDEX index_forum_posts_on_topic_id ON forum_posts USING btree (topic_id) CREATE INDEX index_forum_topic_visits_on_forum_topic_id ON forum_topic_visits USING btree (forum_topic_id); +-- +-- Name: index_forum_topic_visits_on_last_read_at; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_forum_topic_visits_on_last_read_at ON forum_topic_visits USING btree (last_read_at); + + -- -- Name: index_forum_topic_visits_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -6992,3 +7060,5 @@ INSERT INTO schema_migrations (version) VALUES ('20140701224800'); INSERT INTO schema_migrations (version) VALUES ('20140722225753'); +INSERT INTO schema_migrations (version) VALUES ('20140725003232'); + diff --git a/test/factories/forum_subscription.rb b/test/factories/forum_subscription.rb new file mode 100644 index 000000000..fedb14c94 --- /dev/null +++ b/test/factories/forum_subscription.rb @@ -0,0 +1,4 @@ +FactoryGirl.define do + factory(:forum_subscription) do + end +end diff --git a/test/unit/forum_post_test.rb b/test/unit/forum_post_test.rb index f81fe10b8..8ae35543c 100644 --- a/test/unit/forum_post_test.rb +++ b/test/unit/forum_post_test.rb @@ -14,6 +14,33 @@ class ForumPostTest < ActiveSupport::TestCase CurrentUser.ip_addr = nil end + context "#receive_email_notifications" do + should "return true if a matching subscription exists" do + FactoryGirl.create(:forum_subscription, :forum_topic_id => @topic.id, :user_id => CurrentUser.user.id) + assert_equal(true, ForumPost.new(:topic_id => @topic.id).receive_email_notifications) + end + + should "return false if there is no matching subscription" do + assert_equal(false, ForumPost.new(:topic_id => @topic.id).receive_email_notifications) + end + end + + context "#update_email_notifications" do + should "create a forum subscription if one doesn't exist" do + assert_difference("ForumSubscription.count", 1) do + FactoryGirl.create(:forum_post, :topic_id => @topic.id, :receive_email_notifications => true) + end + end + + should "update the forum subscription if one already exists" do + FactoryGirl.create(:forum_subscription, :forum_topic_id => @topic.id, :user_id => CurrentUser.user.id) + assert_difference("ForumSubscription.count", 0) do + FactoryGirl.create(:forum_post, :topic_id => @topic.id, :receive_email_notifications => true) + end + assert_not_nil(ForumSubscription.last.last_read_at) + end + end + context "that belongs to a topic with several pages of posts" do setup do Danbooru.config.stubs(:posts_per_page).returns(3)