diff --git a/app/jobs/scheduled/poll_mailbox.rb b/app/jobs/scheduled/poll_mailbox.rb index 3dd1445e9..edd0852e0 100644 --- a/app/jobs/scheduled/poll_mailbox.rb +++ b/app/jobs/scheduled/poll_mailbox.rb @@ -3,11 +3,14 @@ # require 'net/pop' require_dependency 'email/receiver' +require_dependency 'email/sender' +require_dependency 'email/message_builder' module Jobs class PollMailbox < Jobs::Scheduled every 5.minutes sidekiq_options retry: false + include Email::BuildEmailHelper def execute(args) if SiteSetting.pop3s_polling_enabled? @@ -25,6 +28,12 @@ module Jobs pop.each do |mail| if Email::Receiver.new(mail.pop).process == Email::Receiver.results[:processed] mail.delete + else + @message = Mail::Message.new(mail.pop) + # One for you (mod), and one for me (sender) + GroupMessage.create(Group[:moderators].name, :email_reject_notification, {limit_once_per: false, message_params: {from: @message.from, body: @message.body}}) + clientMessage = RejectionMailer.send_rejection(@message.from, @message.body) + Email::Sender.new(clientMessage, :email_reject_notification).send end end end diff --git a/app/mailers/rejection_mailer.rb b/app/mailers/rejection_mailer.rb new file mode 100644 index 000000000..fa9957361 --- /dev/null +++ b/app/mailers/rejection_mailer.rb @@ -0,0 +1,9 @@ +require_dependency 'email/message_builder' + +class RejectionMailer < ActionMailer::Base + include Email::BuildEmailHelper + + def send_rejection(from, body) + build_email(from, template: 'email_reject_notification', from: from, body: body) + end +end diff --git a/config/discourse_defaults.conf b/config/discourse_defaults.conf index f4ede17d4..5fa0b3c9f 100644 --- a/config/discourse_defaults.conf +++ b/config/discourse_defaults.conf @@ -90,3 +90,11 @@ cors_origin = '*' # enable if you really need to serve assets in prd serve_static_assets = false + +# Enable new topic creation via email by setting this value to "true" +allow_new_topics_from_email = + +# Set the default category for new threads by entering the category number here +default_categories_id = + + diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index fe50842f5..a5b70719e 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1107,6 +1107,18 @@ en: subject_template: "Import completed successfully" text_body_template: "The import was successful." + email_reject_notification: + subject_template: "Message posting failed" + text_body_template: | + This is an automated message to inform you that the user a message failed to meet topic criteria. + + Please review the following message. + + From - %{from} + + Contents - %{body}. + + too_many_spam_flags: subject_template: "New account blocked" text_body_template: | diff --git a/docs/MAILING-LIST-SETUP.MD b/docs/MAILING-LIST-SETUP.MD new file mode 100644 index 000000000..f400f2453 --- /dev/null +++ b/docs/MAILING-LIST-SETUP.MD @@ -0,0 +1,48 @@ +## App Setup +> TODO build an admin UI for this, get it out of discourse_defaults.conf + +Enable new topic creation via email by setting this value to "true" +`allow_new_topics_from_email = ` + +Set the default category for new threads by entering the category number here +`default_categories_id = ` + + +## Admin UI Setup + +Let's tell discourse to check for emails +![enable-reply-by-email](https://f.cloud.github.com/assets/2879972/2242953/97d5dd52-9d17-11e3-915e-037758cc68a7.png) +Be sure to setup email as you would for POP3 based replies. + +If users will be using discourse as a mailing list, allow them to opt-in +![enable-mailing-list-mode](https://f.cloud.github.com/assets/2879972/2242954/994ac2a6-9d17-11e3-8f1f-31e570905394.png) +#TODO Document how to set this true by default + +No way to enforce subject lines, so lower minimum topic length +![lower-min-topic-length](https://f.cloud.github.com/assets/2879972/2242956/9b20df84-9d17-11e3-917b-d91c17fd88c3.png) + +Emails may have the same subject, allow duplicate titles +![allow-duplicate-titles](https://f.cloud.github.com/assets/2879972/2242957/9ce3ed70-9d17-11e3-88ae-b7f9b63145bf.png) + +## Suggested User Preferences +![suggested-user-prefs](https://f.cloud.github.com/assets/2879972/2242958/9e866356-9d17-11e3-815d-164c78794b01.png) + + +## FAQ + +Q: Why is this needed? + +A: No matter how good a forum is, sometimes members need to ask a question and all they have is their mail client. + + +Q: What if a message is received from an email address which doesn't belong to an approved, registered user? + +A: It will be rejected, and a notification email sent to the moderator. Check your POP mailbox to see the rejected email content. + + + + +Q: Who did this? + +A: @therealx and @yesthatallen + diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 819b1508f..4c161f046 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -39,12 +39,19 @@ module Email @reply_key.gsub!(t, "") if t.present? end - # Look up the email log for the reply key + # Look up the email log for the reply key, or create a new post if there is none + # Enabled when config/discourse.conf contains "allow_new_topics_from_email = true" @email_log = EmailLog.for(reply_key) - return Email::Receiver.results[:missing] if @email_log.blank? - - create_reply - + if @email_log.blank? + return Email::Receiver.results[:unprocessable] if GlobalSetting.allow_new_topics_from_email == false + @subject = @message.subject + @user_info = User.find_by_email(@message.from.first) + return Email::Receiver.results[:unprocessable] if @user_info.blank? + Rails.logger.debug "Creating post from #{@message.from.first} with subject #{@subject}" + create_new + else + create_reply + end Email::Receiver.results[:processed] rescue Email::Receiver.results[:error] @@ -130,6 +137,21 @@ module Email creator.create end + def create_new + # Try to create a new topic with the body and subject + # looking to config/discourse.conf to set category + if defined? GlobalSetting.default_categories_id + @categoryID = 1 + else + @categoryID = GlobalSetting.default_categories_id + end + creator = PostCreator.new(@user_info, + title: @subject, + raw: @body, + category: @categoryID, + cooking_options: {traditional_markdown_linebreaks: true}) + creator.create + end end end