diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6 index 5c5961cbf..315a05f29 100644 --- a/app/assets/javascripts/discourse/lib/transform-post.js.es6 +++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6 @@ -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; diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6 index 07a29b3b5..0cf321bcf 100644 --- a/app/assets/javascripts/discourse/widgets/post.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post.js.es6 @@ -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) { diff --git a/app/assets/javascripts/discourse/widgets/widget.js.es6 b/app/assets/javascripts/discourse/widgets/widget.js.es6 index 63f693048..7bbc3c10e 100644 --- a/app/assets/javascripts/discourse/widgets/widget.js.es6 +++ b/app/assets/javascripts/discourse/widgets/widget.js.es6 @@ -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); diff --git a/app/models/post.rb b/app/models/post.rb index 6c4d99211..83c669fe4 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -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 diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb index 6e0ea158f..7171a3e5f 100644 --- a/app/serializers/post_serializer.rb +++ b/app/serializers/post_serializer.rb @@ -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 diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 45f9a8b97..d58b84c7c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -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: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index bb0d90f4f..4d42ad04d 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -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." diff --git a/config/site_settings.yml b/config/site_settings.yml index 5d2019d3e..183ff1a55 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -574,6 +574,8 @@ email: auto_generated_whitelist: default: '' type: list + block_auto_generated_emails: true + files: max_image_size_kb: diff --git a/db/migrate/20160420172330_add_is_auto_generated_to_incoming_emails.rb b/db/migrate/20160420172330_add_is_auto_generated_to_incoming_emails.rb new file mode 100644 index 000000000..f8b64bfe5 --- /dev/null +++ b/db/migrate/20160420172330_add_is_auto_generated_to_incoming_emails.rb @@ -0,0 +1,5 @@ +class AddIsAutoGeneratedToIncomingEmails < ActiveRecord::Migration + def change + add_column :incoming_emails, :is_auto_generated, :boolean, default: false + end +end diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 9af176b1e..abedda36e 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -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) diff --git a/lib/topic_view.rb b/lib/topic_view.rb index fb9d2b09c..da72aa5bd 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -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? diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index 1903255f5..805e9dfa4 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -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 diff --git a/spec/fixtures/emails/auto_generated_header.eml b/spec/fixtures/emails/auto_generated_header.eml index 74e0e9d4f..aed6702b9 100644 --- a/spec/fixtures/emails/auto_generated_header.eml +++ b/spec/fixtures/emails/auto_generated_header.eml @@ -6,3 +6,5 @@ Auto-Submitted: auto-generated Mime-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: 7bit + +Some thing. diff --git a/spec/fixtures/emails/auto_generated_precedence.eml b/spec/fixtures/emails/auto_generated_precedence.eml index bc82e2b3c..e88edcdfb 100644 --- a/spec/fixtures/emails/auto_generated_precedence.eml +++ b/spec/fixtures/emails/auto_generated_precedence.eml @@ -6,3 +6,5 @@ Precedence: list Mime-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: 7bit + +Some thing. diff --git a/spec/fixtures/emails/auto_generated_unblocked.eml b/spec/fixtures/emails/auto_generated_unblocked.eml new file mode 100644 index 000000000..9829887ee --- /dev/null +++ b/spec/fixtures/emails/auto_generated_unblocked.eml @@ -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. diff --git a/test/javascripts/widgets/post-stream-test.js.es6 b/test/javascripts/widgets/post-stream-test.js.es6 index 396ae206c..20c7ce4f5 100644 --- a/test/javascripts/widgets/post-stream-test.js.es6 +++ b/test/javascripts/widgets/post-stream-test.js.es6 @@ -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'); }