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 81 additions and 23 deletions

View File

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

View File

@ -115,14 +115,19 @@ createWidget('wiki-edit-button', {
createWidget('post-email-indicator', {
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) {
return attrs.canViewRawEmail ? 'raw-email' : null;
},
html() {
return iconNode('envelope-o');
html(attrs) {
return attrs.isAutoGenerated ? iconNode('envelope') : iconNode('envelope-o');
},
click() {
@ -318,7 +323,10 @@ createWidget('post-article', {
},
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) {

View File

@ -75,8 +75,13 @@ function drawWidget(builder, attrs, state) {
const attributes = properties['attributes'] || {};
properties.attributes = attributes;
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);

View File

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

View File

@ -63,6 +63,7 @@ class PostSerializer < BasicPostSerializer
:user_custom_fields,
:static_doc,
:via_email,
:is_auto_generated,
:action_code,
:action_code_who
@ -311,6 +312,14 @@ class PostSerializer < BasicPostSerializer
object.via_email?
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
scope.is_staff? ? object.version : object.public_version
end

View File

@ -1534,6 +1534,7 @@ en:
yes_value: "Yes, abandon"
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"
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"
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."
block_auto_generated_emails: "Block incoming emails identified as being auto generated."
manual_polling_enabled: "Push emails using the API for email replies."
pop3_polling_enabled: "Poll via POP3 for email replies."

View File

@ -574,6 +574,8 @@ email:
auto_generated_whitelist:
default: ''
type: list
block_auto_generated_emails: true
files:
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
def process_internal
raise BouncedEmailError if @mail.bounced? && !@mail.retryable?
raise ScreenedEmailError if ScreenedEmail.should_block?(@from_email)
user = find_or_create_user(@from_email, @from_display_name)
@ -64,15 +65,19 @@ module Email
@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 ||= ""
raise BouncedEmailError if (@mail.bounced? && !@mail.retryable?)
raise AutoGeneratedEmailReplyError if check_reply_to_auto_generated_header
raise AutoGeneratedEmailError if is_auto_generated?
raise NoBodyDetectedError if body.blank? && !@mail.has_attachments?
raise InactiveUserError if !user.active && !user.staged
raise BlockedUserError if user.blocked
raise NoBodyDetectedError if body.blank? && !@mail.has_attachments?
if is_auto_generated?
@incoming_email.update_columns(is_auto_generated: true)
raise AutoGeneratedEmailReplyError if check_reply_to_auto_generated_header
raise AutoGeneratedEmailError if SiteSetting.block_auto_generated_emails?
end
if action = subscription_action_for(body, subject)
message = SubscriptionMailer.send(action, user)

View File

@ -348,7 +348,7 @@ class TopicView
visible_types = Topic.visible_post_types(@user)
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
posts.where(post_type: visible_types)
end
@ -357,7 +357,7 @@ class TopicView
def filter_posts_by_ids(post_ids)
# TODO: Sort might be off
@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')
@posts = filter_post_types(@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)
end
it "raises an AutoGeneratedEmailReplyError when email contains a reply marked
as reply to an auto generated email".squish do
expect { process(:bounced_email_2) }
.to raise_error(Email::Receiver::AutoGeneratedEmailReplyError)
it "raises an AutoGeneratedEmailReplyError when email contains a marked reply" do
expect { process(:bounced_email_2) }.to raise_error(Email::Receiver::AutoGeneratedEmailReplyError)
end
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
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
describe 'Unsubscribing via email' do

View File

@ -6,3 +6,5 @@ Auto-Submitted: auto-generated
Mime-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Some thing.

View File

@ -6,3 +6,5 @@ Precedence: list
Mime-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Some thing.

View File

@ -0,0 +1,11 @@
Return-Path: <discourse@bar.com>
From: Un Blocked <discourse@bar.com>
To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com
Date: Fri, 15 Jan 2016 00:12:43 +0100
Message-ID: <52@foo.bar.mail>
Auto-Submitted: auto-generated
Mime-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Lorem ipsum dolor sit amet, consectetur adipiscing elit.

View File

@ -19,18 +19,18 @@ postStreamTest('basics', {
const site = this.container.lookup('site:main');
const topic = Topic.create({ details: { created_by: { id: 123 } } });
return [
Post.create({ topic, id: 1, post_number: 1, user_id: 123, primary_group_name: 'trout',
avatar_template: '/images/avatar.png' }),
Post.create({ topic, id: 1, post_number: 1, user_id: 123, primary_group_name: 'trout', 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: 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: 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) {
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
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-post-id=3]').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');
}