Skip to content

Commit bc76271

Browse files
authored
feat(emails): implement send email functionality (EC-14)
* feat(emails): implement send email functionality * chore(emails): implement email sent listener * chore(emails): add reply-to, refactor send email action, use WYSIWYG editor * chore(emails): fix sending emails through table action * feat(emails): implement email outbox * feat(workflows): fix ref * chore(emails): add MailLogResource test * chore(emails): add LogEmailToDatabase test * chore(emails): format test * chore(emails): improve MailLogResource test * chore(emails): increase test coverage, ui fixes * chore(emails): fix permissions
1 parent 2ae5449 commit bc76271

27 files changed

Lines changed: 1824 additions & 2 deletions

.github/workflows/linter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Checkout code
2323
uses: actions/checkout@v4
2424
with:
25-
ref: ${{ github.head_ref }}
25+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
2626

2727
- name: Run Laravel Pint
2828
uses: aglipanci/laravel-pint-action@latest

.github/workflows/test-runner.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
- name: Checkout code
4242
uses: actions/checkout@v4
4343
with:
44-
ref: ${{ github.head_ref }}
44+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
4545

4646
- name: Validate composer.json and composer.lock
4747
run: composer validate --strict
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace Eclipse\Core\Database\Factories;
4+
5+
use Eclipse\Core\Models\MailLog;
6+
use Eclipse\Core\Models\Site;
7+
use Eclipse\Core\Models\User;
8+
use Illuminate\Database\Eloquent\Factories\Factory;
9+
10+
/**
11+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<MailLog>
12+
*/
13+
class MailLogFactory extends Factory
14+
{
15+
/**
16+
* The name of the factory's corresponding model.
17+
*
18+
* @var class-string<\Illuminate\Database\Eloquent\Model>
19+
*/
20+
protected $model = MailLog::class;
21+
22+
/**
23+
* Define the model's default state.
24+
*
25+
* @return array<string, mixed>
26+
*/
27+
public function definition(): array
28+
{
29+
return [
30+
'site_id' => Site::inRandomOrder()->first()?->id ?? Site::factory()->create()->id,
31+
'message_id' => fake()->uuid(),
32+
'from' => fake()->email(),
33+
'to' => fake()->email(),
34+
'cc' => null,
35+
'bcc' => null,
36+
'subject' => fake()->sentence(),
37+
'body' => '<p>'.fake()->paragraph().'</p>',
38+
'headers' => [
39+
'Content-Type' => 'text/html; charset=UTF-8',
40+
'X-Mailer' => 'Laravel',
41+
],
42+
'attachments' => [],
43+
'sender_id' => User::inRandomOrder()->first()?->id ?? User::factory()->create()->id,
44+
'recipient_id' => null,
45+
'status' => fake()->randomElement(['sent', 'sending', 'failed']),
46+
'sent_at' => fake()->dateTimeBetween('-1 month', 'now'),
47+
'data' => [],
48+
'opened' => null,
49+
'delivered' => null,
50+
'complaint' => null,
51+
'bounced' => null,
52+
];
53+
}
54+
55+
/**
56+
* Indicate that the mail log is sent.
57+
*/
58+
public function sent(): static
59+
{
60+
return $this->state(fn (array $attributes) => [
61+
'status' => 'sent',
62+
'sent_at' => now(),
63+
]);
64+
}
65+
66+
/**
67+
* Indicate that the mail log is sending.
68+
*/
69+
public function sending(): static
70+
{
71+
return $this->state(fn (array $attributes) => [
72+
'status' => 'sending',
73+
'sent_at' => null,
74+
]);
75+
}
76+
77+
/**
78+
* Indicate that the mail log failed.
79+
*/
80+
public function failed(): static
81+
{
82+
return $this->state(fn (array $attributes) => [
83+
'status' => 'failed',
84+
'sent_at' => null,
85+
]);
86+
}
87+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::create('mail_logs', function (Blueprint $table) {
15+
$table->increments('id');
16+
17+
$table->foreignId('site_id')->nullable()->constrained('sites')->onDelete('cascade');
18+
19+
$table->string('from')->nullable();
20+
$table->string('to')->nullable();
21+
$table->string('cc')->nullable();
22+
$table->string('bcc')->nullable();
23+
$table->string('subject');
24+
$table->text('body');
25+
$table->text('headers')->nullable();
26+
$table->longText('attachments')->nullable();
27+
$table->uuid('message_id')->nullable();
28+
$table->string('status')->nullable();
29+
$table->longText('data')->nullable();
30+
$table->timestamp('opened')->nullable();
31+
$table->timestamp('delivered')->nullable();
32+
$table->timestamp('complaint')->nullable();
33+
$table->timestamp('bounced')->nullable();
34+
35+
$table->foreignId('sender_id')->nullable()->constrained('users')->onDelete('set null');
36+
$table->foreignId('recipient_id')->nullable()->constrained('users')->onDelete('set null');
37+
38+
$table->timestamps();
39+
40+
$table->index('message_id');
41+
$table->index('status');
42+
$table->index(['site_id', 'created_at']);
43+
});
44+
}
45+
46+
/**
47+
* Reverse the migrations.
48+
*/
49+
public function down(): void
50+
{
51+
Schema::dropIfExists('mail_logs');
52+
}
53+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::table('mail_logs', function (Blueprint $table) {
15+
$table->timestamp('sent_at')->nullable()->after('status');
16+
});
17+
}
18+
19+
/**
20+
* Reverse the migrations.
21+
*/
22+
public function down(): void
23+
{
24+
Schema::table('mail_logs', function (Blueprint $table) {
25+
$table->dropColumn('sent_at');
26+
});
27+
}
28+
};

resources/lang/en/email.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
return [
4+
'send_email' => 'Send Email',
5+
'send_email_to' => 'Send Email to :name',
6+
'sender' => 'Sender',
7+
'recipient' => 'Recipient',
8+
'cc' => 'CC',
9+
'cc_help' => 'Additional recipients',
10+
'bcc' => 'BCC',
11+
'bcc_help' => 'Hidden recipients',
12+
'subject' => 'Subject',
13+
'subject_placeholder' => 'Enter email subject',
14+
'message' => 'Message',
15+
'message_placeholder' => 'Enter message content...',
16+
'send' => 'Send',
17+
'cancel' => 'Cancel',
18+
'email_queued' => 'Email queued',
19+
'email_queued_to' => 'Email to :email has been queued for sending.',
20+
'email_sent' => 'Email sent',
21+
'email_sent_to' => 'Email to :email has been successfully sent.',
22+
'permission_denied' => 'Permission denied',
23+
'permission_denied_message' => 'You do not have permission to send emails.',
24+
'error' => 'Error',
25+
'send_error_message' => 'An error occurred while sending the email: :error',
26+
'email_form_title' => 'Send Email',
27+
'email_form_description' => 'Send an email to the selected user',
28+
'your_email' => 'Your email address',
29+
'recipient_email' => 'Recipient email address',
30+
'invalid_cc_email' => 'One or more CC email addresses are invalid.',
31+
'invalid_bcc_email' => 'One or more BCC email addresses are invalid.',
32+
33+
// Mail Log / Outbox
34+
'outbox' => 'Email Outbox',
35+
'outbox_description' => 'View all outgoing emails',
36+
'email_details' => 'Email Details',
37+
'email_body' => 'Email Body',
38+
'metadata' => 'Metadata',
39+
'user_information' => 'User Information',
40+
'headers' => 'Headers',
41+
'from' => 'From',
42+
'to' => 'To',
43+
'sent_at' => 'Sent At',
44+
'message_id' => 'Message ID',
45+
'status' => 'Status',
46+
'sent' => 'Sent',
47+
'sending' => 'Sending',
48+
'failed' => 'Failed',
49+
'sent_today' => 'Sent Today',
50+
'sent_this_week' => 'Sent This Week',
51+
'back_to_outbox' => 'Back to Outbox',
52+
'view_email' => 'View Email',
53+
'no_emails' => 'No emails found',
54+
'email_log' => 'Email Log',
55+
'show_html' => 'Show HTML',
56+
'show_preview' => 'Show Preview',
57+
'email_preview' => 'Email Preview',
58+
'html_source' => 'HTML Source',
59+
];

resources/lang/hr/email.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
return [
4+
'send_email' => 'Pošaljite e-mail',
5+
'send_email_to' => 'Pošaljite e-mail korisniku :name',
6+
'sender' => 'Pošaljitelj',
7+
'recipient' => 'Primatelj',
8+
'cc' => 'CC',
9+
'cc_help' => 'Dodatni primatelji',
10+
'bcc' => 'BCC',
11+
'bcc_help' => 'Skriveni primatelji',
12+
'subject' => 'Predmet',
13+
'subject_placeholder' => 'Unesite predmet e-maila',
14+
'message' => 'Poruka',
15+
'message_placeholder' => 'Unesite sadržaj poruke...',
16+
'send' => 'Pošaljite',
17+
'cancel' => 'Otkaži',
18+
'email_queued' => 'E-mail je u redu za slanje',
19+
'email_queued_to' => 'E-mail za :email je dodan u red za slanje.',
20+
'email_sent' => 'E-mail poslan',
21+
'email_sent_to' => 'E-mail poslan na :email je uspješno poslan.',
22+
'permission_denied' => 'Dozvola odbijena',
23+
'permission_denied_message' => 'Nemate dozvolu za slanje e-mailova.',
24+
'error' => 'Greška',
25+
'send_error_message' => 'Došlo je do greške prilikom slanja e-maila: :error',
26+
'email_form_title' => 'Pošaljite e-mail',
27+
'email_form_description' => 'Pošaljite e-mail odabranom korisniku',
28+
'your_email' => 'Vaša e-mail adresa',
29+
'recipient_email' => 'E-mail adresa primatelja',
30+
'invalid_cc_email' => 'Jedna ili više CC e-mail adresa nije valjana.',
31+
'invalid_bcc_email' => 'Jedna ili više BCC e-mail adresa nije valjana.',
32+
33+
// Mail Log / Outbox
34+
'outbox' => 'Odlazni e-mailovi',
35+
'outbox_description' => 'Pregled svih odlaznih e-mailova',
36+
'email_details' => 'Detalji e-maila',
37+
'email_body' => 'Sadržaj e-maila',
38+
'metadata' => 'Metapodaci',
39+
'user_information' => 'Informacije o korisnicima',
40+
'headers' => 'Zaglavlja',
41+
'from' => 'Od',
42+
'to' => 'Za',
43+
'sent_at' => 'Poslano u',
44+
'message_id' => 'ID poruke',
45+
'status' => 'Status',
46+
'sent' => 'Poslano',
47+
'sending' => 'Šalje se',
48+
'failed' => 'Neuspješno',
49+
'sent_today' => 'Poslano danas',
50+
'sent_this_week' => 'Poslano ovaj tjedan',
51+
'back_to_outbox' => 'Natrag na odlazne poruke',
52+
'view_email' => 'Prikaži e-mail',
53+
'no_emails' => 'Nema pronađenih e-mailova',
54+
'email_log' => 'Dnevnik e-mailova',
55+
'show_html' => 'Prikaži HTML',
56+
'show_preview' => 'Prikaži pregled',
57+
'email_preview' => 'Pregled e-maila',
58+
'html_source' => 'HTML kod',
59+
];

resources/lang/sl/email.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
return [
4+
'send_email' => 'Pošlji e-pošto',
5+
'send_email_to' => 'Pošlji e-pošto uporabniku :name',
6+
'sender' => 'Pošiljatelj',
7+
'recipient' => 'Prejemnik',
8+
'cc' => 'Kp (CC)',
9+
'cc_help' => 'Dodatni prejemniki',
10+
'bcc' => 'Skp (BCC)',
11+
'bcc_help' => 'Skriti prejemniki',
12+
'subject' => 'Zadeva',
13+
'subject_placeholder' => 'Vnesite zadevo sporočila',
14+
'message' => 'Sporočilo',
15+
'message_placeholder' => 'Vnesite vsebino sporočila...',
16+
'send' => 'Pošlji',
17+
'cancel' => 'Prekliči',
18+
'email_queued' => 'E-pošta v čakalni vrsti',
19+
'email_queued_to' => 'E-pošta za :email je dodana v čakalno vrsto za pošiljanje.',
20+
'email_sent' => 'E-pošta poslana',
21+
'email_sent_to' => 'E-pošta je bila uspešno poslana na :email.',
22+
'permission_denied' => 'Dostop zavrnjen',
23+
'permission_denied_message' => 'Nimate dovoljenja za pošiljanje e-pošte.',
24+
'error' => 'Napaka',
25+
'send_error_message' => 'Prišlo je do napake pri pošiljanju e-pošte: :error',
26+
'email_form_title' => 'Pošiljanje e-pošte',
27+
'email_form_description' => 'Pošljite e-pošto izbranemu uporabniku',
28+
'your_email' => 'Vaš e-poštni naslov',
29+
'recipient_email' => 'Prejemnik e-pošte',
30+
'invalid_cc_email' => 'En ali več CC e-poštnih naslovov ni veljavnih.',
31+
'invalid_bcc_email' => 'En ali več BCC e-poštnih naslovov ni veljavnih.',
32+
33+
// Mail Log / Outbox
34+
'outbox' => 'Odhodna e-pošta',
35+
'outbox_description' => 'Pregled vse odhodne e-pošte',
36+
'email_details' => 'Podrobnosti e-pošte',
37+
'email_body' => 'Vsebina e-pošte',
38+
'metadata' => 'Metapodatki',
39+
'user_information' => 'Podatki o uporabnikih',
40+
'headers' => 'Glave',
41+
'from' => 'Od',
42+
'to' => 'Za',
43+
'sent_at' => 'Poslano ob',
44+
'message_id' => 'ID sporočila',
45+
'status' => 'Status',
46+
'sent' => 'Poslano',
47+
'sending' => 'Pošiljanje',
48+
'failed' => 'Neuspešno',
49+
'sent_today' => 'Poslano danes',
50+
'sent_this_week' => 'Poslano ta teden',
51+
'back_to_outbox' => 'Nazaj na odhodna sporočila',
52+
'view_email' => 'Prikaži e-pošto',
53+
'no_emails' => 'Ni najdenih e-poštnih sporočil',
54+
'email_log' => 'Dnevnik e-pošte',
55+
'show_html' => 'Prikaži HTML',
56+
'show_preview' => 'Prikaži predogled',
57+
'email_preview' => 'Predogled e-pošte',
58+
'html_source' => 'HTML koda',
59+
];

0 commit comments

Comments
 (0)