mirror of
https://github.com/discourse/discourse.git
synced 2025-02-09 21:04:48 +00:00
FEATURE: proper mailing list mode
once enable_mailing_list_mode is enabled any user can elect to get every post via email unless they opt out of category or topic
This commit is contained in:
parent
3a1c5ed39d
commit
227873df78
@ -178,7 +178,7 @@ Discourse.User = Discourse.Model.extend({
|
||||
'digest_after_days',
|
||||
'new_topic_duration_minutes',
|
||||
'external_links_in_new_tab',
|
||||
'watch_new_topics',
|
||||
'mailing_list_mode',
|
||||
'enable_quoting');
|
||||
|
||||
_.each(['muted','watched','tracked'], function(s){
|
||||
|
@ -83,6 +83,13 @@
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{i18n user.email_settings}}</label>
|
||||
<div class="controls">
|
||||
{{#if Discourse.SiteSettings.enable_mailing_list_mode}}
|
||||
<label>
|
||||
{{view Ember.Checkbox checkedBinding="mailing_list_mode"}}
|
||||
{{i18n user.mailing_list_mode}}
|
||||
</label>
|
||||
{{/if}}
|
||||
|
||||
<label>{{view Ember.Checkbox checkedBinding="email_digests"}}
|
||||
{{i18n user.email_digests.title}}</label>
|
||||
|
||||
@ -116,15 +123,6 @@
|
||||
{{combobox valueAttribute="value" content=considerNewTopicOptions value=new_topic_duration_minutes}}
|
||||
</div>
|
||||
|
||||
{{#if Discourse.SiteSettings.enable_watch_new_topics}}
|
||||
<div class="controls">
|
||||
<label>
|
||||
{{view Ember.Checkbox checkedBinding="watch_new_topics"}}
|
||||
{{i18n user.watch_new_topics}}
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="controls">
|
||||
<label>{{view Ember.Checkbox checkedBinding="external_links_in_new_tab"}}
|
||||
{{i18n user.external_links_in_new_tab}}</label>
|
||||
|
37
app/jobs/regular/notify_mailing_list_subscribers.rb
Normal file
37
app/jobs/regular/notify_mailing_list_subscribers.rb
Normal file
@ -0,0 +1,37 @@
|
||||
module Jobs
|
||||
|
||||
class NotifyMailingListSubscribers < Jobs::Base
|
||||
|
||||
def execute(args)
|
||||
post_id = args[:post_id]
|
||||
post = Post.find(post_id) if post_id
|
||||
|
||||
raise Discourse::InvalidParameters.new(:post_id) unless post
|
||||
|
||||
User.not_suspended
|
||||
.not_blocked
|
||||
.real
|
||||
.where(mailing_list_mode: true)
|
||||
.where('NOT EXISTS(
|
||||
SELECT 1
|
||||
FROM topic_users tu
|
||||
WHERE
|
||||
tu.topic_id = ? AND
|
||||
tu.user_id = users.id AND
|
||||
tu.notification_level = ?
|
||||
)', post.topic_id, TopicUser.notification_levels[:muted])
|
||||
.where('NOT EXISTS(
|
||||
SELECT 1
|
||||
FROM category_users cu
|
||||
WHERE
|
||||
cu.category_id = ? AND
|
||||
cu.user_id = users.id AND
|
||||
cu.notification_level = ?
|
||||
)', post.topic.category_id, CategoryUser.notification_levels[:muted])
|
||||
.each do |user|
|
||||
UserNotifications.mailing_list_notify(user, post).deliver
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -93,6 +93,17 @@ class UserNotifications < ActionMailer::Base
|
||||
notification_email(user, opts)
|
||||
end
|
||||
|
||||
def mailing_list_notify(user, post)
|
||||
send_notification_email(
|
||||
title: post.topic.title,
|
||||
post: post,
|
||||
from_alias: post.user.username,
|
||||
allow_reply_by_email: true,
|
||||
notification_type: "posted",
|
||||
user: user
|
||||
)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def email_post_markdown(post)
|
||||
@ -130,9 +141,34 @@ class UserNotifications < ActionMailer::Base
|
||||
username = @notification.data_hash[:original_username]
|
||||
notification_type = opts[:notification_type] || Notification.types[@notification.notification_type].to_s
|
||||
|
||||
return if SiteSetting.enable_mailing_list_mode &&
|
||||
["replied", "mentioned", "quoted", "posted"].include?(notification_type)
|
||||
|
||||
title = @notification.data_hash[:topic_title]
|
||||
allow_reply_by_email = opts[:allow_reply_by_email]
|
||||
|
||||
send_notification_email(
|
||||
title: title,
|
||||
post: @post,
|
||||
from_alias: username,
|
||||
allow_reply_by_email: allow_reply_by_email,
|
||||
notification_type: notification_type,
|
||||
user: user
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def send_notification_email(opts)
|
||||
post = opts[:post]
|
||||
title = opts[:title]
|
||||
allow_reply_by_email = opts[:allow_reply_by_email]
|
||||
from_alias = opts[:from_alias]
|
||||
notification_type = opts[:notification_type]
|
||||
user = opts[:user]
|
||||
|
||||
context = ""
|
||||
tu = TopicUser.get(@post.topic_id, user)
|
||||
context_posts = self.class.get_context_posts(@post, tu)
|
||||
tu = TopicUser.get(post.topic_id, user)
|
||||
context_posts = self.class.get_context_posts(post, tu)
|
||||
|
||||
# make .present? cheaper
|
||||
context_posts = context_posts.to_a
|
||||
@ -147,37 +183,36 @@ class UserNotifications < ActionMailer::Base
|
||||
html = UserNotificationRenderer.new(Rails.configuration.paths["app/views"]).render(
|
||||
template: 'email/notification',
|
||||
format: :html,
|
||||
locals: { context_posts: context_posts, post: @post }
|
||||
locals: { context_posts: context_posts, post: post }
|
||||
)
|
||||
|
||||
template = "user_notifications.user_#{notification_type}"
|
||||
if @post.topic.private_message?
|
||||
if post.topic.private_message?
|
||||
template << "_pm"
|
||||
end
|
||||
|
||||
email_opts = {
|
||||
topic_title: @notification.data_hash[:topic_title],
|
||||
message: email_post_markdown(@post),
|
||||
url: @post.url,
|
||||
post_id: @post.id,
|
||||
topic_id: @post.topic_id,
|
||||
topic_title: title,
|
||||
message: email_post_markdown(post),
|
||||
url: post.url,
|
||||
post_id: post.id,
|
||||
topic_id: post.topic_id,
|
||||
context: context,
|
||||
username: username,
|
||||
username: from_alias,
|
||||
add_unsubscribe_link: true,
|
||||
allow_reply_by_email: opts[:allow_reply_by_email],
|
||||
allow_reply_by_email: allow_reply_by_email,
|
||||
template: template,
|
||||
html_override: html,
|
||||
style: :notification
|
||||
}
|
||||
|
||||
# If we have a display name, change the from address
|
||||
if username.present?
|
||||
email_opts[:from_alias] = username
|
||||
if from_alias.present?
|
||||
email_opts[:from_alias] = from_alias
|
||||
end
|
||||
|
||||
TopicUser.change(user.id, @post.topic_id, last_emailed_post_number: @post.post_number)
|
||||
TopicUser.change(user.id, post.topic_id, last_emailed_post_number: post.post_number)
|
||||
|
||||
build_email(user.email, email_opts)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -72,23 +72,6 @@ class TopicUser < ActiveRecord::Base
|
||||
TopicUser.where('topic_id = ? and user_id = ?', topic, user).first
|
||||
end
|
||||
|
||||
def auto_watch_new_topic(topic_id)
|
||||
# Can not afford to slow down creation of topics when a pile of users are watching new topics, reverting to SQL for max perf here
|
||||
sql = <<SQL
|
||||
INSERT INTO topic_users(user_id, topic_id, notification_level, notifications_reason_id)
|
||||
SELECT id, :topic_id, :level, :reason
|
||||
FROM users
|
||||
WHERE watch_new_topics AND
|
||||
NOT EXISTS(SELECT 1 FROM topic_users WHERE topic_id = :topic_id AND user_id = users.id)
|
||||
SQL
|
||||
|
||||
exec_sql(
|
||||
sql,
|
||||
topic_id: topic_id,
|
||||
level: notification_levels[:watching],
|
||||
reason: notification_reasons[:auto_watch]
|
||||
)
|
||||
end
|
||||
|
||||
# Change attributes for a user (creates a record when none is present). First it tries an update
|
||||
# since there's more likely to be an existing record than not. If the update returns 0 rows affected
|
||||
|
@ -83,6 +83,7 @@ class User < ActiveRecord::Base
|
||||
attr_accessor :notification_channel_position
|
||||
|
||||
scope :blocked, -> { where(blocked: true) } # no index
|
||||
scope :not_blocked, -> { where(blocked: false) } # no index
|
||||
scope :suspended, -> { where('suspended_till IS NOT NULL AND suspended_till > ?', Time.zone.now) } # no index
|
||||
scope :not_suspended, -> { where('suspended_till IS NULL') }
|
||||
# excluding fake users like the community user
|
||||
|
@ -51,7 +51,7 @@ class UserSerializer < BasicUserSerializer
|
||||
:email_direct,
|
||||
:email_always,
|
||||
:digest_after_days,
|
||||
:watch_new_topics,
|
||||
:mailing_list_mode,
|
||||
:auto_track_topics_after_msecs,
|
||||
:new_topic_duration_minutes,
|
||||
:external_links_in_new_tab,
|
||||
|
@ -14,7 +14,7 @@ class UserUpdater
|
||||
:external_links_in_new_tab,
|
||||
:enable_quoting,
|
||||
:dynamic_favicon,
|
||||
:watch_new_topics
|
||||
:mailing_list_mode
|
||||
]
|
||||
|
||||
def initialize(actor, user)
|
||||
|
@ -229,7 +229,6 @@ da:
|
||||
deleted: "(slettet)"
|
||||
suspended_notice: "Denne bruger er suspenderet indtil {{date}}."
|
||||
suspended_reason: "Begrundelse: "
|
||||
watch_new_topics: "Overvåg automatisk alle nye emner i forummet"
|
||||
watched_categories: "Overvåget"
|
||||
watched_categories_instructions: "Du overvåger automatisk alle emner i disse kategorier"
|
||||
tracked_categories: "Fulgt"
|
||||
|
@ -230,7 +230,6 @@ de:
|
||||
deleted: "(gelöscht)"
|
||||
suspended_notice: "Diser Benutzer ist bis zum {{date}} susperndiert."
|
||||
suspended_reason: "Grund: "
|
||||
watch_new_topics: "Automatische alle neuen Themen dieses Forums beobachten"
|
||||
watched_categories: "Beobachten"
|
||||
watched_categories_instructions: "Das selbe wie 'verfolgen', zusätzlich wirst du über alle neuen Beiträge und Themen informiert."
|
||||
tracked_categories: "Verfolgen"
|
||||
|
@ -231,7 +231,7 @@ en:
|
||||
deleted: "(deleted)"
|
||||
suspended_notice: "This user is suspended until {{date}}."
|
||||
suspended_reason: "Reason: "
|
||||
watch_new_topics: "Automatically watch all new topics posted on the forum"
|
||||
mailing_list_mode: "Automatically recieve an email every time a post is made on the forum (unless you have the topic or category muted)"
|
||||
watched_categories: "Watched"
|
||||
watched_categories_instructions: "Same as Tracking, plus you will be notified of all new posts and topics."
|
||||
tracked_categories: "Tracked"
|
||||
|
@ -229,7 +229,6 @@ fr:
|
||||
deleted: "(supprimé)"
|
||||
suspended_notice: "L'utilisateur est suspendu jusqu'au {{date}}."
|
||||
suspended_reason: "Raison :"
|
||||
watch_new_topics: "Surveiller automatiquement toutes les nouvelles discussions postées sur le forum"
|
||||
watched_categories: "Surveillées"
|
||||
watched_categories_instructions: "Vous allez surveiller automatiquement toutes les discussions de ces catégories"
|
||||
tracked_categories: "Suivies"
|
||||
|
@ -233,7 +233,6 @@ nl:
|
||||
deleted: (verwijderd)
|
||||
suspended_notice: "Deze gebruiker is geschorst tot {{date}}."
|
||||
suspended_reason: "Reden: "
|
||||
watch_new_topics: "Houd alle nieuwe topics in het forum automatisch in de gaten"
|
||||
watched_categories: "In de gaten gehouden"
|
||||
watched_categories_instructions: "Zelfde als volgen, daarnaast zou je een notificatie ontvangen voor alle nieuwe berichten en topics"
|
||||
tracked_categories: "Gevolgd"
|
||||
|
@ -229,7 +229,6 @@ zh_CN:
|
||||
deleted: "(已删除)"
|
||||
suspended_notice: "该用户将被禁止登录,直至{{date}}."
|
||||
suspended_reason: "原因:"
|
||||
watch_new_topics: "自动关注论坛中所有新发表的主题"
|
||||
watched_categories: "已关注"
|
||||
watched_categories_instructions: "和跟踪相同,此外你还将收到新的帖子和主题的通知。"
|
||||
tracked_categories: "已跟踪"
|
||||
|
@ -749,7 +749,6 @@ da:
|
||||
pop3s_polling_host: "Host til polling af POP3S-konto."
|
||||
pop3s_polling_username: "Brugernavn til polling af POP3S-konto."
|
||||
pop3s_polling_password: "Adgangskode til polling af POP3S-konto."
|
||||
enable_watch_new_topics: "Lad brugerne overvåge alle nye emner (valgfrit) via en brugerindstilling"
|
||||
|
||||
minimum_topics_similar: "Hvor emner der skal eksistere i databasen før der vises tilsvarende emner."
|
||||
|
||||
|
@ -761,7 +761,7 @@ en:
|
||||
pop3s_polling_host: "The host to poll for email via POP3S"
|
||||
pop3s_polling_username: "The username for the POP3S account to poll for email"
|
||||
pop3s_polling_password: "The password for the POP3S account to poll for email"
|
||||
enable_watch_new_topics: "Allow users to watch all new topics (optionally) via a user preference"
|
||||
enable_mailing_list_mode: "Allow users to (optionally) opt-in to mailing list mode via a user preference"
|
||||
|
||||
minimum_topics_similar: "How many topics need to exist in the database before similar topics are presented."
|
||||
|
||||
|
@ -663,7 +663,6 @@ fr:
|
||||
pop3s_polling_host: "L'hôte utilisé pour le polling pour l'email via POP3S"
|
||||
pop3s_polling_username: "Le nom d'utilisateur pour le polling POPS3 par email"
|
||||
pop3s_polling_password: "Le mot de passe pour le polling POPS3 par email"
|
||||
enable_watch_new_topics: "Autoriser les utilisateurs à suivre toutes les nouvelles discussions (optionnellement) via une préférence de l'utilisateur"
|
||||
minimum_topics_similar: "Combien de topics ont besoin d'exister dans la base de données avant que des topics similaires soit présentés."
|
||||
relative_date_duration: "Nombre de jours après la création d'un message à partir desquels les dates seront affichées en absolu plutôt qu'en relatif. Exemple: relatif : 7j, absolut : 20 Fév"
|
||||
delete_user_max_age: "L'age maximum d'un utilisateur, en jours, qui permet à un administrateur de le supprimer."
|
||||
|
@ -753,7 +753,6 @@ nl:
|
||||
pop3s_polling_host: "De host voor POP3S"
|
||||
pop3s_polling_username: "De username voor het POP3S e-mailaccount"
|
||||
pop3s_polling_password: "Het wachtwoord voor het POP3S e-mailaccount"
|
||||
enable_watch_new_topics: "Sta toe dat gebruikers alle nieuwe topics in de gaten houden (optioneel) via hun gebruikersinstellingen"
|
||||
|
||||
minimum_topics_similar: "Hoeveel topics moeten er in de database staan voordat er vergelijkbare topics getoond worden?"
|
||||
|
||||
|
@ -232,7 +232,7 @@ email:
|
||||
pop3s_polling_port: 995
|
||||
pop3s_polling_username: ''
|
||||
pop3s_polling_password: ''
|
||||
enable_watch_new_topics:
|
||||
enable_mailing_list_mode:
|
||||
default: false
|
||||
client: true
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
class AddMailingListModeToUsers < ActiveRecord::Migration
|
||||
def change
|
||||
rename_column :users, :watch_new_topics, :mailing_list_mode
|
||||
end
|
||||
end
|
@ -135,8 +135,17 @@ class PostCreator
|
||||
end
|
||||
|
||||
def after_post_create
|
||||
if !@topic.private_message? && @post.post_number > 1 && @post.post_type != Post.types[:moderator_action]
|
||||
TopicTrackingState.publish_unread(@post)
|
||||
if !@topic.private_message? && @post.post_type != Post.types[:moderator_action]
|
||||
if @post.post_number > 1
|
||||
TopicTrackingState.publish_unread(@post)
|
||||
end
|
||||
if SiteSetting.enable_mailing_list_mode
|
||||
Jobs.enqueue_in(
|
||||
SiteSetting.email_time_window_mins.minutes,
|
||||
:notify_mailing_list_subscribers,
|
||||
post_id: @post.id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -39,7 +39,6 @@ class TopicCreator
|
||||
@topic.notifier.watch_topic!(id, nil)
|
||||
end
|
||||
|
||||
TopicUser.auto_watch_new_topic(@topic.id)
|
||||
CategoryUser.auto_watch_new_topic(@topic)
|
||||
end
|
||||
|
||||
|
@ -268,21 +268,28 @@ describe TopicUser do
|
||||
tu.seen_post_count.should == 2
|
||||
end
|
||||
|
||||
describe "auto_watch_new_topic" do
|
||||
describe "mailing_list_mode" do
|
||||
|
||||
it "will receive email notification for every topic" do
|
||||
SiteSetting.stubs(:enable_mailing_list_mode).returns(true)
|
||||
|
||||
it "auto watches topics when called" do
|
||||
user1 = Fabricate(:user)
|
||||
user2 = Fabricate(:user, watch_new_topics: true)
|
||||
user3 = Fabricate(:user, watch_new_topics: true)
|
||||
TopicUser.auto_watch_new_topic(topic.id)
|
||||
user2 = Fabricate(:user, mailing_list_mode: true)
|
||||
post = create_post
|
||||
user3 = Fabricate(:user, mailing_list_mode: true)
|
||||
create_post(topic_id: post.topic_id)
|
||||
|
||||
TopicUser.get(topic, user1).should == nil
|
||||
# mails posts from earlier topics
|
||||
tu = TopicUser.where(user_id: user3.id, topic_id: post.topic_id).first
|
||||
tu.last_emailed_post_number.should == 2
|
||||
|
||||
tu = TopicUser.get(topic, user2)
|
||||
tu.notification_level.should == TopicUser.notification_levels[:watching]
|
||||
tu.notifications_reason_id.should == TopicUser.notification_reasons[:auto_watch]
|
||||
# mails nothing to random users
|
||||
tu = TopicUser.where(user_id: user1.id, topic_id: post.topic_id).first
|
||||
tu.should be_nil
|
||||
|
||||
TopicUser.get(topic, user3).notification_level.should == TopicUser.notification_levels[:watching]
|
||||
# mails other user
|
||||
tu = TopicUser.where(user_id: user2.id, topic_id: post.topic_id).first
|
||||
tu.last_emailed_post_number.should == 2
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user