diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index a42435af3a9e71..0e9654e0ddb0d8 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -22,6 +22,7 @@ //= require ./discourse/app/lib/offset-calculator //= require ./discourse/app/lib/lock-on //= require ./discourse/app/lib/url +//= require ./discourse/app/lib/email-provider-default-settings //= require ./discourse/app/lib/debounce //= require ./discourse/app/lib/quote //= require ./discourse/app/lib/key-value-store diff --git a/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js b/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js new file mode 100644 index 00000000000000..012c1178691992 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js @@ -0,0 +1,91 @@ +import Component from "@ember/component"; +import emailProviderDefaultSettings from "discourse/lib/email-provider-default-settings"; +import { isEmpty } from "@ember/utils"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import discourseComputed, { on } from "discourse-common/utils/decorators"; +import EmberObject, { action } from "@ember/object"; +import { ajax } from "discourse/lib/ajax"; + +export default Component.extend({ + tagName: "", + form: null, + + @discourseComputed( + "group.email_username", + "group.email_password", + "form.imap_server", + "form.imap_port" + ) + missingSettings(email_username, email_password, imap_server, imap_port) { + return [ + email_username, + email_password, + imap_server, + imap_port, + ].some((value) => isEmpty(value)); + }, + + @discourseComputed("group.imap_mailboxes") + mailboxes(imapMailboxes) { + if (!imapMailboxes) { + return []; + } + return imapMailboxes.map((mailbox) => ({ name: mailbox, value: mailbox })); + }, + + @discourseComputed("group.imap_mailbox_name", "mailboxes.length") + mailboxSelected(mailboxName, mailboxesSize) { + return mailboxesSize === 0 || !isEmpty(mailboxName); + }, + + @action + resetSettingsValid() { + this.set("imapSettingsValid", false); + }, + + @on("init") + _fillForm() { + this.set( + "form", + EmberObject.create({ + imap_server: this.group.imap_server, + imap_port: (this.group.imap_port || "").toString(), + imap_ssl: this.group.imap_ssl, + }) + ); + }, + + @action + prefillSettings(provider) { + this.form.setProperties(emailProviderDefaultSettings(provider, "imap")); + }, + + @action + testImapSettings() { + const settings = { + host: this.form.imap_server, + port: this.form.imap_port, + ssl: this.form.imap_ssl, + username: this.group.email_username, + password: this.group.email_password, + }; + + this.set("testingSettings", true); + this.set("imapSettingsValid", false); + + return ajax(`/groups/${this.group.id}/test_email_settings`, { + type: "POST", + data: Object.assign(settings, { protocol: "imap" }), + }) + .then(() => { + this.set("imapSettingsValid", true); + this.group.setProperties({ + imap_server: this.form.imap_server, + imap_port: this.form.imap_port, + imap_ssl: this.form.imap_ssl, + }); + }) + .catch(popupAjaxError) + .finally(() => this.set("testingSettings", false)); + }, +}); diff --git a/app/assets/javascripts/discourse/app/components/group-manage-email-settings.js b/app/assets/javascripts/discourse/app/components/group-manage-email-settings.js new file mode 100644 index 00000000000000..ffa586b32a31f8 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/group-manage-email-settings.js @@ -0,0 +1,116 @@ +import Component from "@ember/component"; +import { isEmpty } from "@ember/utils"; +import discourseComputed, { on } from "discourse-common/utils/decorators"; +import I18n from "I18n"; +import bootbox from "bootbox"; +import { action } from "@ember/object"; + +export default Component.extend({ + tagName: "", + + imapSettingsValid: false, + smtpSettingsValid: false, + + @on("init") + _determineSettingsValid() { + this.set( + "imapSettingsValid", + this.group.imap_enabled && this.group.imap_server + ); + this.set( + "smtpSettingsValid", + this.group.smtp_enabled && this.group.smtp_server + ); + }, + + @discourseComputed( + "emailSettingsValid", + "group.smtp_enabled", + "group.imap_enabled" + ) + enableImapSettings(emailSettingsValid, smtpEnabled, imapEnabled) { + return smtpEnabled && (emailSettingsValid || imapEnabled); + }, + + @discourseComputed( + "smtpSettingsValid", + "imapSettingsValid", + "group.smtp_enabled", + "group.imap_enabled" + ) + emailSettingsValid( + smtpSettingsValid, + imapSettingsValid, + smtpEnabled, + imapEnabled + ) { + return ( + (!smtpEnabled || smtpSettingsValid) && (!imapEnabled || imapSettingsValid) + ); + }, + + _anySmtpFieldsFilled() { + return [ + this.group.smtp_server, + this.group.smtp_port, + this.group.email_username, + this.group.email_password, + ].some((value) => !isEmpty(value)); + }, + + _anyImapFieldsFilled() { + return [this.group.imap_server, this.group.imap_port].some( + (value) => !isEmpty(value) + ); + }, + + @action + smtpEnabledChange(event) { + if ( + !event.target.checked && + this.group.smtp_enabled && + this._anySmtpFieldsFilled() + ) { + bootbox.confirm( + I18n.t("groups.manage.email.smtp_disable_confirm"), + (result) => { + if (!result) { + this.group.set("smtp_enabled", true); + } else { + this.group.set("imap_enabled", false); + } + } + ); + } + + this.group.set("smtp_enabled", event.target.checked); + }, + + @action + imapEnabledChange(event) { + if ( + !event.target.checked && + this.group.imap_enabled && + this._anyImapFieldsFilled() + ) { + bootbox.confirm( + I18n.t("groups.manage.email.imap_disable_confirm"), + (result) => { + if (!result) { + this.group.set("imap_enabled", true); + } + } + ); + } + + this.group.set("imap_enabled", event.target.checked); + }, + + @action + afterSave() { + // reload the group to get the updated imap_mailboxes + this.store.find("group", this.group.name).then(() => { + this._determineSettingsValid(); + }); + }, +}); diff --git a/app/assets/javascripts/discourse/app/components/group-manage-save-button.js b/app/assets/javascripts/discourse/app/components/group-manage-save-button.js index 0a92d4bd843b0d..82cc182b7d7879 100644 --- a/app/assets/javascripts/discourse/app/components/group-manage-save-button.js +++ b/app/assets/javascripts/discourse/app/components/group-manage-save-button.js @@ -7,6 +7,7 @@ import { popupAutomaticMembershipAlert } from "discourse/controllers/groups-new" export default Component.extend({ saving: null, + disabled: false, @discourseComputed("saving") savingText(saving) { @@ -15,6 +16,10 @@ export default Component.extend({ actions: { save() { + if (this.beforeSave) { + this.beforeSave(); + } + this.set("saving", true); const group = this.model; @@ -31,6 +36,10 @@ export default Component.extend({ } this.set("saved", true); + + if (this.afterSave) { + this.afterSave(); + } }) .catch(popupAjaxError) .finally(() => this.set("saving", false)); diff --git a/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js b/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js new file mode 100644 index 00000000000000..d9758d2c4631a6 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js @@ -0,0 +1,82 @@ +import Component from "@ember/component"; +import emailProviderDefaultSettings from "discourse/lib/email-provider-default-settings"; +import { isEmpty } from "@ember/utils"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import discourseComputed, { on } from "discourse-common/utils/decorators"; +import EmberObject, { action } from "@ember/object"; +import { ajax } from "discourse/lib/ajax"; + +export default Component.extend({ + tagName: "", + form: null, + + @discourseComputed( + "form.email_username", + "form.email_password", + "form.smtp_server", + "form.smtp_port" + ) + missingSettings(email_username, email_password, smtp_server, smtp_port) { + return [ + email_username, + email_password, + smtp_server, + smtp_port, + ].some((value) => isEmpty(value)); + }, + + @action + resetSettingsValid() { + this.set("smtpSettingsValid", false); + }, + + @on("init") + _fillForm() { + this.set( + "form", + EmberObject.create({ + email_username: this.group.email_username, + email_password: this.group.email_password, + smtp_server: this.group.smtp_server, + smtp_port: (this.group.smtp_port || "").toString(), + smtp_ssl: this.group.smtp_ssl, + }) + ); + }, + + @action + prefillSettings(provider) { + this.form.setProperties(emailProviderDefaultSettings(provider, "smtp")); + }, + + @action + testSmtpSettings() { + const settings = { + host: this.form.smtp_server, + port: this.form.smtp_port, + ssl: this.form.smtp_ssl, + username: this.form.email_username, + password: this.form.email_password, + }; + + this.set("testingSettings", true); + this.set("smtpSettingsValid", false); + + return ajax(`/groups/${this.group.id}/test_email_settings`, { + type: "POST", + data: Object.assign(settings, { protocol: "smtp" }), + }) + .then(() => { + this.set("smtpSettingsValid", true); + this.group.setProperties({ + smtp_server: this.form.smtp_server, + smtp_port: this.form.smtp_port, + smtp_ssl: this.form.smtp_ssl, + email_username: this.form.email_username, + email_password: this.form.email_password, + }); + }) + .catch(popupAjaxError) + .finally(() => this.set("testingSettings", false)); + }, +}); diff --git a/app/assets/javascripts/discourse/app/lib/ajax-error.js b/app/assets/javascripts/discourse/app/lib/ajax-error.js index 0f3feba41caaee..34a2b3604b4e63 100644 --- a/app/assets/javascripts/discourse/app/lib/ajax-error.js +++ b/app/assets/javascripts/discourse/app/lib/ajax-error.js @@ -1,4 +1,5 @@ import I18n from "I18n"; +import { isTesting } from "discourse-common/config/environment"; import bootbox from "bootbox"; export function extractError(error, defaultMessage) { @@ -69,6 +70,11 @@ export function popupAjaxError(error) { } bootbox.alert(extractError(error)); + // in testing mode we want to be able to test these ajax popup messages + if (isTesting()) { + return; + } + error._discourse_displayed = true; // We re-throw in a catch to not swallow the exception diff --git a/app/assets/javascripts/discourse/app/lib/email-provider-default-settings.js b/app/assets/javascripts/discourse/app/lib/email-provider-default-settings.js new file mode 100644 index 00000000000000..fa2939f6ace814 --- /dev/null +++ b/app/assets/javascripts/discourse/app/lib/email-provider-default-settings.js @@ -0,0 +1,22 @@ +const GMAIL = { + imap: { + imap_server: "imap.gmail.com", + imap_port: "993", + imap_ssl: true, + }, + smtp: { + smtp_server: "smtp.gmail.com", + smtp_port: "587", + smtp_ssl: true, + }, +}; + +export default function emailProviderDefaultSettings(provider, protocol) { + provider = provider.toLowerCase(); + protocol = protocol.toLowerCase(); + + switch (provider) { + case "gmail": + return GMAIL[protocol]; + } +} diff --git a/app/assets/javascripts/discourse/app/models/group.js b/app/assets/javascripts/discourse/app/models/group.js index 429dcd05953e71..23a8e54f26acc9 100644 --- a/app/assets/javascripts/discourse/app/models/group.js +++ b/app/assets/javascripts/discourse/app/models/group.js @@ -217,10 +217,12 @@ const Group = RestModel.extend({ smtp_server: this.smtp_server, smtp_port: this.smtp_port, smtp_ssl: this.smtp_ssl, + smtp_enabled: this.smtp_enabled, imap_server: this.imap_server, imap_port: this.imap_port, imap_ssl: this.imap_ssl, imap_mailbox_name: this.imap_mailbox_name, + imap_enabled: this.imap_enabled, email_username: this.email_username, email_password: this.email_password, flair_icon: null, diff --git a/app/assets/javascripts/discourse/app/routes/group-manage-email.js b/app/assets/javascripts/discourse/app/routes/group-manage-email.js index 1ba2f5d5b5142a..5f8a26e43b73e5 100644 --- a/app/assets/javascripts/discourse/app/routes/group-manage-email.js +++ b/app/assets/javascripts/discourse/app/routes/group-manage-email.js @@ -5,7 +5,8 @@ export default DiscourseRoute.extend({ showFooter: true, beforeModel() { - if (!this.siteSettings.enable_imap && !this.siteSettings.enable_smtp) { + // cannot configure IMAP without SMTP being enabled + if (!this.siteSettings.enable_smtp) { return this.transitionTo("group.manage.profile"); } }, diff --git a/app/assets/javascripts/discourse/app/templates/components/group-imap-email-settings.hbs b/app/assets/javascripts/discourse/app/templates/components/group-imap-email-settings.hbs new file mode 100644 index 00000000000000..2c6025d4ac4cd2 --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/components/group-imap-email-settings.hbs @@ -0,0 +1,80 @@ +
{{i18n "groups.manage.email.settings.allow_unknown_sender_topic_replies_hint"}}
+{{i18n "groups.manage.email.smtp_instructions"}}
+ + + + {{#if group.smtp_enabled}} + {{group-smtp-email-settings group=group smtpSettingsValid=smtpSettingsValid}} + {{/if}} + + {{#if siteSettings.enable_imap}} ++ {{html-safe (i18n "groups.manage.email.imap_instructions")}} +
+ +