FEATURE: new 'block_auto_generated_emails' site setting

This commit is contained in:
Régis Hanol 2016-04-20 21:29:27 +02:00
parent f62ffce03b
commit 4960b62110
16 changed files with 66 additions and 23 deletions

View File

@ -34,6 +34,7 @@ export function transformBasicPost(post) {
post_number: post.post_number, post_number: post.post_number,
cooked: post.cooked, cooked: post.cooked,
via_email: post.via_email, via_email: post.via_email,
isAutoGenerated: post.is_auto_generated,
user_id: post.user_id, user_id: post.user_id,
usernameUrl: Discourse.getURL(`/users/${post.username}`), usernameUrl: Discourse.getURL(`/users/${post.username}`),
username: post.username, username: post.username,
@ -92,6 +93,7 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos
postAtts.topicCreatedById = createdBy.id; postAtts.topicCreatedById = createdBy.id;
postAtts.post_type = postType; postAtts.post_type = postType;
postAtts.via_email = post.via_email; postAtts.via_email = post.via_email;
postAtts.isAutoGenerated = post.is_auto_generated;
postAtts.isModeratorAction = postType === postTypes.moderator_action; postAtts.isModeratorAction = postType === postTypes.moderator_action;
postAtts.isWhisper = postType === postTypes.whisper; postAtts.isWhisper = postType === postTypes.whisper;
postAtts.isSmallAction = postType === postTypes.small_action; postAtts.isSmallAction = postType === postTypes.small_action;

View File

@ -115,14 +115,19 @@ createWidget('wiki-edit-button', {
createWidget('post-email-indicator', { createWidget('post-email-indicator', {
tagName: 'div.post-info.via-email', tagName: 'div.post-info.via-email',
title: 'post.via_email',
title(attrs) {
return attrs.isAutoGenerated ?
I18n.t('post.via_auto_generated_email') :
I18n.t('post.via_email');
},
buildClasses(attrs) { buildClasses(attrs) {
return attrs.canViewRawEmail ? 'raw-email' : null; return attrs.canViewRawEmail ? 'raw-email' : null;
}, },
html() { html(attrs) {
return iconNode('envelope-o'); return attrs.isAutoGenerated ? iconNode('envelope') : iconNode('envelope-o');
}, },
click() { click() {
@ -318,7 +323,10 @@ createWidget('post-article', {
}, },
buildClasses(attrs) { buildClasses(attrs) {
if (attrs.via_email) { return 'via-email'; } let classNames = [];
if (attrs.via_email) { classNames.push('via-email'); }
if (attrs.isAutoGenerated) { classNames.push('is-auto-generated'); }
return classNames;
}, },
buildAttributes(attrs) { buildAttributes(attrs) {

View File

@ -75,8 +75,13 @@ function drawWidget(builder, attrs, state) {
const attributes = properties['attributes'] || {}; const attributes = properties['attributes'] || {};
properties.attributes = attributes; properties.attributes = attributes;
if (this.title) { if (this.title) {
attributes.title = I18n.t(this.title); if (typeof this.title === 'function') {
attributes.title = this.title(attrs, state);
} else {
attributes.title = I18n.t(this.title);
}
} }
let contents = this.html(attrs, state); let contents = this.html(attrs, state);

View File

@ -40,6 +40,8 @@ class Post < ActiveRecord::Base
has_one :post_search_data has_one :post_search_data
has_one :post_stat has_one :post_stat
has_one :incoming_email
has_many :post_details has_many :post_details
has_many :post_revisions has_many :post_revisions

View File

@ -63,6 +63,7 @@ class PostSerializer < BasicPostSerializer
:user_custom_fields, :user_custom_fields,
:static_doc, :static_doc,
:via_email, :via_email,
:is_auto_generated,
:action_code, :action_code,
:action_code_who :action_code_who
@ -311,6 +312,14 @@ class PostSerializer < BasicPostSerializer
object.via_email? object.via_email?
end end
def is_auto_generated
object.incoming_email.try(:is_auto_generated)
end
def include_is_auto_generated?
object.via_email? && is_auto_generated
end
def version def version
scope.is_staff? ? object.version : object.public_version scope.is_staff? ? object.version : object.public_version
end end

View File

@ -1534,6 +1534,7 @@ en:
yes_value: "Yes, abandon" yes_value: "Yes, abandon"
via_email: "this post arrived via email" via_email: "this post arrived via email"
via_auto_generated_email: "this post arrived via an auto generated email"
whisper: "this post is a private whisper for moderators" whisper: "this post is a private whisper for moderators"
wiki: wiki:

View File

@ -1174,6 +1174,7 @@ en:
max_emails_per_day_per_user: "Maximum number of emails to send users per day. 0 to disable the limit" max_emails_per_day_per_user: "Maximum number of emails to send users per day. 0 to disable the limit"
enable_staged_users: "Automatically create staged users when processing incoming emails." enable_staged_users: "Automatically create staged users when processing incoming emails."
auto_generated_whitelist: "List of email addresses that won't be checked for auto-generated content." auto_generated_whitelist: "List of email addresses that won't be checked for auto-generated content."
block_auto_generated_emails: "Block incoming emails identified as being auto generated."
manual_polling_enabled: "Push emails using the API for email replies." manual_polling_enabled: "Push emails using the API for email replies."
pop3_polling_enabled: "Poll via POP3 for email replies." pop3_polling_enabled: "Poll via POP3 for email replies."

View File

@ -574,6 +574,8 @@ email:
auto_generated_whitelist: auto_generated_whitelist:
default: '' default: ''
type: list type: list
block_auto_generated_emails: true
files: files:
max_image_size_kb: max_image_size_kb:

View File

@ -0,0 +1,5 @@
class AddIsAutoGeneratedToIncomingEmails < ActiveRecord::Migration
def change
add_column :incoming_emails, :is_auto_generated, :boolean, default: false
end
end

View File

@ -56,6 +56,7 @@ module Email
end end
def process_internal def process_internal
raise BouncedEmailError if @mail.bounced? && !@mail.retryable?
raise ScreenedEmailError if ScreenedEmail.should_block?(@from_email) raise ScreenedEmailError if ScreenedEmail.should_block?(@from_email)
user = find_or_create_user(@from_email, @from_display_name) user = find_or_create_user(@from_email, @from_display_name)
@ -64,15 +65,19 @@ module Email
@incoming_email.update_columns(user_id: user.id) @incoming_email.update_columns(user_id: user.id)
raise InactiveUserError if !user.active && !user.staged
raise BlockedUserError if user.blocked
body, @elided = select_body body, @elided = select_body
body ||= "" body ||= ""
raise BouncedEmailError if (@mail.bounced? && !@mail.retryable?) raise NoBodyDetectedError if body.blank? && !@mail.has_attachments?
raise AutoGeneratedEmailReplyError if check_reply_to_auto_generated_header
raise AutoGeneratedEmailError if is_auto_generated? if is_auto_generated?
raise NoBodyDetectedError if body.blank? && !@mail.has_attachments? @incoming_email.update_columns(is_auto_generated: true)
raise InactiveUserError if !user.active && !user.staged raise AutoGeneratedEmailReplyError if check_reply_to_auto_generated_header
raise BlockedUserError if user.blocked raise AutoGeneratedEmailError if SiteSetting.block_auto_generated_emails?
end
if action = subscription_action_for(body, subject) if action = subscription_action_for(body, subject)
message = SubscriptionMailer.send(action, user) message = SubscriptionMailer.send(action, user)

View File

@ -348,7 +348,7 @@ class TopicView
visible_types = Topic.visible_post_types(@user) visible_types = Topic.visible_post_types(@user)
if @user.present? if @user.present?
posts.where("user_id = ? OR post_type IN (?)", @user.id, visible_types) posts.where("posts.user_id = ? OR post_type IN (?)", @user.id, visible_types)
else else
posts.where(post_type: visible_types) posts.where(post_type: visible_types)
end end
@ -357,7 +357,7 @@ class TopicView
def filter_posts_by_ids(post_ids) def filter_posts_by_ids(post_ids)
# TODO: Sort might be off # TODO: Sort might be off
@posts = Post.where(id: post_ids, topic_id: @topic.id) @posts = Post.where(id: post_ids, topic_id: @topic.id)
.includes(:user, :reply_to_user) .includes(:user, :reply_to_user, :incoming_email)
.order('sort_order') .order('sort_order')
@posts = filter_post_types(@posts) @posts = filter_post_types(@posts)
@posts = @posts.with_deleted if @guardian.can_see_deleted_posts? @posts = @posts.with_deleted if @guardian.can_see_deleted_posts?

View File

@ -59,11 +59,8 @@ describe Email::Receiver do
expect { process(:bounced_email) }.to raise_error(Email::Receiver::BouncedEmailError) expect { process(:bounced_email) }.to raise_error(Email::Receiver::BouncedEmailError)
end end
it "raises an AutoGeneratedEmailReplyError when email contains a reply marked it "raises an AutoGeneratedEmailReplyError when email contains a marked reply" do
as reply to an auto generated email".squish do expect { process(:bounced_email_2) }.to raise_error(Email::Receiver::AutoGeneratedEmailReplyError)
expect { process(:bounced_email_2) }
.to raise_error(Email::Receiver::AutoGeneratedEmailReplyError)
end end
context "reply" do context "reply" do
@ -156,7 +153,12 @@ describe Email::Receiver do
it "doesn't raise an AutoGeneratedEmailError when the mail is auto generated but is whitelisted" do it "doesn't raise an AutoGeneratedEmailError when the mail is auto generated but is whitelisted" do
SiteSetting.auto_generated_whitelist = "foo@bar.com|discourse@bar.com" SiteSetting.auto_generated_whitelist = "foo@bar.com|discourse@bar.com"
expect { process(:auto_generated_whitelisted) }.not_to raise_error expect { process(:auto_generated_whitelisted) }.to change { topic.posts.count }
end
it "doesn't raise an AutoGeneratedEmailError when block_auto_generated_emails is disabled" do
SiteSetting.block_auto_generated_emails = false
expect { process(:auto_generated_unblocked) }.to change { topic.posts.count }
end end
describe 'Unsubscribing via email' do describe 'Unsubscribing via email' do

Binary file not shown.

Binary file not shown.

View File

@ -19,18 +19,18 @@ postStreamTest('basics', {
const site = this.container.lookup('site:main'); const site = this.container.lookup('site:main');
const topic = Topic.create({ details: { created_by: { id: 123 } } }); const topic = Topic.create({ details: { created_by: { id: 123 } } });
return [ return [
Post.create({ topic, id: 1, post_number: 1, user_id: 123, primary_group_name: 'trout', Post.create({ topic, id: 1, post_number: 1, user_id: 123, primary_group_name: 'trout', avatar_template: '/images/avatar.png' }),
avatar_template: '/images/avatar.png' }),
Post.create({ topic, id: 2, post_number: 2, post_type: site.get('post_types.moderator_action') }), Post.create({ topic, id: 2, post_number: 2, post_type: site.get('post_types.moderator_action') }),
Post.create({ topic, id: 3, post_number: 3, hidden: true }), Post.create({ topic, id: 3, post_number: 3, hidden: true }),
Post.create({ topic, id: 4, post_number: 4, post_type: site.get('post_types.whisper') }), Post.create({ topic, id: 4, post_number: 4, post_type: site.get('post_types.whisper') }),
Post.create({ topic, id: 5, post_number: 5, wiki: true, via_email: true }) Post.create({ topic, id: 5, post_number: 5, wiki: true, via_email: true }),
Post.create({ topic, id: 6, post_number: 6, via_email: true, is_auto_generated: true }),
]; ];
}, },
test(assert) { test(assert) {
assert.equal(this.$('.post-stream').length, 1); assert.equal(this.$('.post-stream').length, 1);
assert.equal(this.$('.topic-post').length, 5, 'renders all posts'); assert.equal(this.$('.topic-post').length, 6, 'renders all posts');
// look for special class bindings // look for special class bindings
assert.equal(this.$('.topic-post:eq(0).topic-owner').length, 1, 'it applies the topic owner class'); assert.equal(this.$('.topic-post:eq(0).topic-owner').length, 1, 'it applies the topic owner class');
@ -46,6 +46,7 @@ postStreamTest('basics', {
assert.equal(this.$('article[data-user-id=123]').length, 1); assert.equal(this.$('article[data-user-id=123]').length, 1);
assert.equal(this.$('article[data-post-id=3]').length, 1); assert.equal(this.$('article[data-post-id=3]').length, 1);
assert.equal(this.$('article#post_5.via-email').length, 1); assert.equal(this.$('article#post_5.via-email').length, 1);
assert.equal(this.$('article#post_6.is-auto-generated').length, 1);
assert.equal(this.$('article:eq(0) .main-avatar').length, 1, 'renders the main avatar'); assert.equal(this.$('article:eq(0) .main-avatar').length, 1, 'renders the main avatar');
} }