FEATURE: Add hooks for email poller plugins (#21384)
While we are unable to support OAUTH2 with pop3 (due to upstream dependency ruby/net-pop#16), we are adding the support for mail pollers plugin. Doing so, it would be possible to write a plugin which then uses other ways (microsoft graph sdk for example) to poll emails from a mailbox. The idea is that a plugin would define a class which inherits from Email::Poller and defines a poll_mailbox static method which returns an array of strings. Then the plugin could call register_mail_poller(<class_name>) to have it registered. All the configuration (oauth2 tokens, email, etc) could be managed by sitesettings defined in the plugin.
This commit is contained in:
parent
52894b9d7c
commit
56718504ac
|
@ -12,6 +12,12 @@ module Jobs
|
||||||
def execute(args)
|
def execute(args)
|
||||||
@args = args
|
@args = args
|
||||||
poll_pop3 if should_poll?
|
poll_pop3 if should_poll?
|
||||||
|
|
||||||
|
DiscoursePluginRegistry.mail_pollers.each do |poller|
|
||||||
|
return if !poller.enabled?
|
||||||
|
|
||||||
|
poller.poll_mailbox(method(:process_popmail))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_poll?
|
def should_poll?
|
||||||
|
|
|
@ -115,7 +115,8 @@ class SiteSetting < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.email_polling_enabled?
|
def self.email_polling_enabled?
|
||||||
SiteSetting.manual_polling_enabled? || SiteSetting.pop3_polling_enabled?
|
SiteSetting.manual_polling_enabled? || SiteSetting.pop3_polling_enabled? ||
|
||||||
|
DiscoursePluginRegistry.mail_pollers.any?(&:enabled?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.blocked_attachment_content_types_regex
|
def self.blocked_attachment_content_types_regex
|
||||||
|
|
|
@ -2457,7 +2457,7 @@ en:
|
||||||
pop3_polling_password_is_empty: "You must set a 'pop3 polling password' before enabling POP3 polling."
|
pop3_polling_password_is_empty: "You must set a 'pop3 polling password' before enabling POP3 polling."
|
||||||
pop3_polling_authentication_failed: "POP3 authentication failed. Please verify your pop3 credentials."
|
pop3_polling_authentication_failed: "POP3 authentication failed. Please verify your pop3 credentials."
|
||||||
reply_by_email_address_is_empty: "You must set a 'reply by email address' before enabling reply by email."
|
reply_by_email_address_is_empty: "You must set a 'reply by email address' before enabling reply by email."
|
||||||
email_polling_disabled: "You must enable either manual or POP3 polling before enabling reply by email."
|
email_polling_disabled: "You must enable either manual, POP3 polling or have a custom mail poller enabled before enabling reply by email."
|
||||||
user_locale_not_enabled: "You must first enable 'allow user locale' before enabling this setting."
|
user_locale_not_enabled: "You must first enable 'allow user locale' before enabling this setting."
|
||||||
personal_message_enabled_groups_invalid: "You must specify at least one group for this setting. If you do not want anyone except staff to send PMs, choose the staff group."
|
personal_message_enabled_groups_invalid: "You must specify at least one group for this setting. If you do not want anyone except staff to send PMs, choose the staff group."
|
||||||
invalid_regex: "Regex is invalid or not allowed."
|
invalid_regex: "Regex is invalid or not allowed."
|
||||||
|
|
|
@ -72,6 +72,7 @@ class DiscoursePluginRegistry
|
||||||
define_register :seedfu_filter, Set
|
define_register :seedfu_filter, Set
|
||||||
define_register :demon_processes, Set
|
define_register :demon_processes, Set
|
||||||
define_register :groups_callback_for_users_search_controller_action, Hash
|
define_register :groups_callback_for_users_search_controller_action, Hash
|
||||||
|
define_register :mail_pollers, Set
|
||||||
|
|
||||||
define_filtered_register :staff_user_custom_fields
|
define_filtered_register :staff_user_custom_fields
|
||||||
define_filtered_register :public_user_custom_fields
|
define_filtered_register :public_user_custom_fields
|
||||||
|
@ -119,6 +120,10 @@ class DiscoursePluginRegistry
|
||||||
self.auth_providers << auth_provider
|
self.auth_providers << auth_provider
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.register_mail_poller(mail_poller)
|
||||||
|
self.mail_pollers << mail_poller
|
||||||
|
end
|
||||||
|
|
||||||
def register_js(filename, options = {})
|
def register_js(filename, options = {})
|
||||||
# If we have a server side option, add that too.
|
# If we have a server side option, add that too.
|
||||||
self.class.javascripts << filename
|
self.class.javascripts << filename
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Email
|
||||||
|
class Poller
|
||||||
|
# To be implemented by concrete classes.
|
||||||
|
# This function takes as input a function that processes the incoming email.
|
||||||
|
# The function passed as argument should take as an argument the MIME string of the email.
|
||||||
|
# An example of function to pass is `process_popmail` in `app/jobs/scheduled/poll_mailbox.rb`
|
||||||
|
def poll_mailbox(process_cb)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Child class can override this
|
||||||
|
def enabled?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -647,6 +647,11 @@ class Plugin::Instance
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def register_email_poller(poller)
|
||||||
|
plugin = self
|
||||||
|
DiscoursePluginRegistry.register_mail_poller(poller) if plugin.enabled?
|
||||||
|
end
|
||||||
|
|
||||||
def register_asset(file, opts = nil)
|
def register_asset(file, opts = nil)
|
||||||
raise <<~ERROR if file.end_with?(".hbs", ".handlebars")
|
raise <<~ERROR if file.end_with?(".hbs", ".handlebars")
|
||||||
[#{name}] Handlebars templates can no longer be included via `register_asset`.
|
[#{name}] Handlebars templates can no longer be included via `register_asset`.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
require "email/poller"
|
||||||
|
|
||||||
RSpec.describe Jobs::PollMailbox do
|
RSpec.describe Jobs::PollMailbox do
|
||||||
let(:poller) { Jobs::PollMailbox.new }
|
let(:poller) { Jobs::PollMailbox.new }
|
||||||
|
@ -175,4 +176,45 @@ RSpec.describe Jobs::PollMailbox do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "poller plugin" do
|
||||||
|
let(:poller_plugin) do
|
||||||
|
Class
|
||||||
|
.new(described_class) do
|
||||||
|
def set_enabled(e)
|
||||||
|
@enabled = e
|
||||||
|
end
|
||||||
|
|
||||||
|
def enabled?
|
||||||
|
@enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
def poll_mailbox(process_cb)
|
||||||
|
process_cb.call(file_from_fixtures("original_message.eml", "emails"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
.new
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:plugin) { Plugin::Instance.new }
|
||||||
|
|
||||||
|
before(:each) { plugin.register_email_poller(poller_plugin) }
|
||||||
|
|
||||||
|
after(:each) do
|
||||||
|
Discourse.plugins.delete plugin
|
||||||
|
DiscoursePluginRegistry.reset!
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't call process method when plugin is not active" do
|
||||||
|
poller_plugin.set_enabled(false)
|
||||||
|
poller.expects(:process_popmail).never
|
||||||
|
poller.execute({})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls process method when plugin is active" do
|
||||||
|
poller_plugin.set_enabled(true)
|
||||||
|
poller.expects(:process_popmail).once
|
||||||
|
poller.execute({})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -98,6 +98,13 @@ RSpec.describe DiscoursePluginRegistry do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#mail_pollers" do
|
||||||
|
it "defaults to an empty Set" do
|
||||||
|
registry.reset!
|
||||||
|
expect(registry.mail_pollers).to eq(Set.new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe ".register_html_builder" do
|
describe ".register_html_builder" do
|
||||||
it "can register and build html" do
|
it "can register and build html" do
|
||||||
DiscoursePluginRegistry.register_html_builder(:my_html) { "<b>my html</b>" }
|
DiscoursePluginRegistry.register_html_builder(:my_html) { "<b>my html</b>" }
|
||||||
|
|
Loading…
Reference in New Issue