mirror of
https://github.com/discourse/discourse.git
synced 2024-11-21 16:38:15 -06:00
DEV: Convert admin-incoming-email
modal to component-based API (#22701)
- Convert `admin-incoming-email` modal to component-based API - Testing that the modal was working in local development was extremely challenging due to the need for `rejected` and `bounced` emails. Something that is not easy to stub in a local dev environment. To make this process more smooth for future developers I have added a new rake task: ``` desc "Creates sample email logs" task "email_logs:populate" => ["db:load_config"] do |_, args| DiscourseDev::EmailLog.populate! end ``` That will generate fully functional email logs in development to be toyed with. <img width="787" alt="Screenshot 2023-07-20 at 3 27 04 PM" src="https://github.com/discourse/discourse/assets/50783505/47b3fe34-cd7e-49a5-8fe6-768c0fbd1aa2">
This commit is contained in:
parent
9ff56ef474
commit
37942cb8bb
@ -0,0 +1,54 @@
|
|||||||
|
<DModal
|
||||||
|
class="admin-incoming-email-modal"
|
||||||
|
@title={{i18n "admin.email.incoming_emails.modal.title"}}
|
||||||
|
@closeModal={{@closeModal}}
|
||||||
|
@bodyClass="incoming-emails"
|
||||||
|
>
|
||||||
|
<:body>
|
||||||
|
<div class="control-group">
|
||||||
|
<label>{{i18n "admin.email.incoming_emails.modal.error"}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
<p>{{@model.error}}</p>
|
||||||
|
{{#if @model.error_description}}
|
||||||
|
<p class="error-description">{{@model.error_description}}</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label>{{i18n "admin.email.incoming_emails.modal.headers"}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
<Textarea @value={{@model.headers}} wrap="off" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label>{{i18n "admin.email.incoming_emails.modal.subject"}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
{{@model.subject}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label>{{i18n "admin.email.incoming_emails.modal.body"}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
<Textarea @value={{@model.body}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if @model.rejection_message}}
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label>{{i18n
|
||||||
|
"admin.email.incoming_emails.modal.rejection_message"
|
||||||
|
}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
<Textarea @value={{@model.rejection_message}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</:body>
|
||||||
|
</DModal>
|
@ -1,28 +0,0 @@
|
|||||||
import Controller from "@ember/controller";
|
|
||||||
import IncomingEmail from "admin/models/incoming-email";
|
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { longDate } from "discourse/lib/formatter";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
|
|
||||||
export default class AdminIncomingEmailController extends Controller.extend(
|
|
||||||
ModalFunctionality
|
|
||||||
) {
|
|
||||||
@discourseComputed("model.date")
|
|
||||||
date(d) {
|
|
||||||
return longDate(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
load(id) {
|
|
||||||
return IncomingEmail.find(id).then((result) => this.set("model", result));
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFromBounced(id) {
|
|
||||||
return IncomingEmail.findByBounced(id)
|
|
||||||
.then((result) => this.set("model", result))
|
|
||||||
.catch((error) => {
|
|
||||||
this.send("closeModal");
|
|
||||||
popupAjaxError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,26 @@
|
|||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import AdminEmailLogs from "admin/routes/admin-email-logs";
|
import AdminEmailLogs from "admin/routes/admin-email-logs";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import IncomingEmail from "admin/models/incoming-email";
|
||||||
|
import IncomingEmailModal from "../components/modal/incoming-email";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class AdminEmailBouncedRoute extends AdminEmailLogs {
|
export default class AdminEmailBouncedRoute extends AdminEmailLogs {
|
||||||
|
@service modal;
|
||||||
status = "bounced";
|
status = "bounced";
|
||||||
|
|
||||||
@action
|
@action
|
||||||
showIncomingEmail(id) {
|
async showIncomingEmail(id) {
|
||||||
showModal("admin-incoming-email", { admin: true });
|
const model = await this.loadFromBounced(id);
|
||||||
this.controllerFor("modals/admin-incoming-email").loadFromBounced(id);
|
this.modal.show(IncomingEmailModal, { model });
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async loadFromBounced(id) {
|
||||||
|
try {
|
||||||
|
return await IncomingEmail.findByBounced(id);
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
|
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import IncomingEmailModal from "../components/modal/incoming-email";
|
||||||
|
import IncomingEmail from "admin/models/incoming-email";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class AdminEmailRejectedRoute extends AdminEmailIncomings {
|
export default class AdminEmailRejectedRoute extends AdminEmailIncomings {
|
||||||
|
@service modal;
|
||||||
status = "rejected";
|
status = "rejected";
|
||||||
|
|
||||||
@action
|
@action
|
||||||
showIncomingEmail(id) {
|
async showIncomingEmail(id) {
|
||||||
showModal("admin-incoming-email", { admin: true });
|
const model = await IncomingEmail.find(id);
|
||||||
this.controllerFor("modals/admin-incoming-email").load(id);
|
this.modal.show(IncomingEmailModal, { model });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
<DModalBody
|
|
||||||
@class="incoming-emails"
|
|
||||||
@title="admin.email.incoming_emails.modal.title"
|
|
||||||
>
|
|
||||||
<div class="control-group">
|
|
||||||
<label>{{i18n "admin.email.incoming_emails.modal.error"}}</label>
|
|
||||||
<div class="controls">
|
|
||||||
<p>{{this.model.error}}</p>
|
|
||||||
{{#if this.model.error_description}}
|
|
||||||
<p class="error-description">{{this.model.error_description}}</p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label>{{i18n "admin.email.incoming_emails.modal.headers"}}</label>
|
|
||||||
<div class="controls">
|
|
||||||
<Textarea @value={{this.model.headers}} wrap="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label>{{i18n "admin.email.incoming_emails.modal.subject"}}</label>
|
|
||||||
<div class="controls">
|
|
||||||
{{this.model.subject}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label>{{i18n "admin.email.incoming_emails.modal.body"}}</label>
|
|
||||||
<div class="controls">
|
|
||||||
<Textarea @value={{this.model.body}} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.model.rejection_message}}
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label>{{i18n
|
|
||||||
"admin.email.incoming_emails.modal.rejection_message"
|
|
||||||
}}</label>
|
|
||||||
<div class="controls">
|
|
||||||
<Textarea @value={{this.model.rejection_message}} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</DModalBody>
|
|
@ -58,7 +58,6 @@ const KNOWN_LEGACY_MODALS = [
|
|||||||
"user-status",
|
"user-status",
|
||||||
"admin-add-upload",
|
"admin-add-upload",
|
||||||
"admin-delete-posts-confirmation",
|
"admin-delete-posts-confirmation",
|
||||||
"admin-incoming-email",
|
|
||||||
"admin-merge-users-prompt",
|
"admin-merge-users-prompt",
|
||||||
"admin-start-backup",
|
"admin-start-backup",
|
||||||
"admin-watched-word-test",
|
"admin-watched-word-test",
|
||||||
|
@ -35,6 +35,8 @@ topic:
|
|||||||
max: 3
|
max: 3
|
||||||
user:
|
user:
|
||||||
count: 30
|
count: 30
|
||||||
|
email_logs:
|
||||||
|
count: 2
|
||||||
|
|
||||||
new_user:
|
new_user:
|
||||||
username: new_user
|
username: new_user
|
||||||
|
77
lib/discourse_dev/email_log.rb
Normal file
77
lib/discourse_dev/email_log.rb
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "discourse_dev/record"
|
||||||
|
require "faker"
|
||||||
|
|
||||||
|
module DiscourseDev
|
||||||
|
class EmailLog < Record
|
||||||
|
def initialize
|
||||||
|
super(::EmailLog, DiscourseDev.config.email_logs[:count])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_sent!
|
||||||
|
::EmailLog.create!(email_log_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_bounced!
|
||||||
|
bounce_key = SecureRandom.hex
|
||||||
|
email_local_part, email_domain = SiteSetting.notification_email.split("@")
|
||||||
|
bounced_to_address = "#{email_local_part}+verp-#{bounce_key}@#{email_domain}"
|
||||||
|
bounce_data =
|
||||||
|
email_log_data.merge(
|
||||||
|
to_address: bounced_to_address,
|
||||||
|
bounced: true,
|
||||||
|
bounce_key: bounce_key,
|
||||||
|
bounce_error_code: "5.0.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bounced email logs require a matching incoming email record
|
||||||
|
::IncomingEmail.create!(
|
||||||
|
incoming_email_data.merge(to_addresses: bounced_to_address, is_bounce: true),
|
||||||
|
)
|
||||||
|
::EmailLog.create!(bounce_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_rejected!
|
||||||
|
::IncomingEmail.create!(incoming_email_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_log_data
|
||||||
|
{
|
||||||
|
to_address: User.random.email,
|
||||||
|
email_type: :digest,
|
||||||
|
user_id: User.random.id,
|
||||||
|
raw: Faker::Lorem.paragraph,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def incoming_email_data
|
||||||
|
user = User.random
|
||||||
|
subject = Faker::Lorem.sentence
|
||||||
|
email_content = <<-EMAIL
|
||||||
|
Return-Path: #{user.email}
|
||||||
|
From: #{user.email}
|
||||||
|
Date: #{Date.today}
|
||||||
|
Mime-Version: "1.0"
|
||||||
|
Content-Type: "text/plain"
|
||||||
|
Content-Transfer-Encoding: "7bit"
|
||||||
|
|
||||||
|
#{Faker::Lorem.paragraph}
|
||||||
|
EMAIL
|
||||||
|
|
||||||
|
{
|
||||||
|
user_id: user.id,
|
||||||
|
from_address: user.email,
|
||||||
|
raw: email_content,
|
||||||
|
error: Faker::Lorem.sentence,
|
||||||
|
rejection_message: I18n.t("emails.incoming.errors.bounced_email_error"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def populate!
|
||||||
|
@count.times { create_sent! }
|
||||||
|
@count.times { create_bounced! }
|
||||||
|
@count.times { create_rejected! }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -54,3 +54,8 @@ desc "Add replies to a topic"
|
|||||||
task "replies:populate", %i[topic_id count] => ["db:load_config"] do |_, args|
|
task "replies:populate", %i[topic_id count] => ["db:load_config"] do |_, args|
|
||||||
DiscourseDev::Post.add_replies!(args)
|
DiscourseDev::Post.add_replies!(args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Creates sample email logs"
|
||||||
|
task "email_logs:populate" => ["db:load_config"] do |_, args|
|
||||||
|
DiscourseDev::EmailLog.populate!
|
||||||
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user