Skip to content

Commit

Permalink
Modernize Email\Body class
Browse files Browse the repository at this point in the history
  • Loading branch information
distantnative committed Aug 18, 2024
1 parent f508833 commit 2b73a54
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 49 deletions.
84 changes: 53 additions & 31 deletions src/Email/Body.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Kirby\Email;

use Kirby\Toolkit\Properties;
use Kirby\Cms\App;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;

/**
* Representation of a an Email body
Expand All @@ -17,29 +19,51 @@
*/
class Body
{
protected string|null $html;
protected string|null $text;

/**
* Email body constructor
*/
public function __construct(array $props = [])
{
$this->html = $props['html'] ?? null;
$this->text = $props['text'] ?? null;
public function __construct(
protected string|null $html = null,
protected string|null $text = null
) {
}

/**
* Creates a new instance while
* merging initial and new properties
* @deprecated 4.0.0
*/
public function clone(array $props = []): static
{
return new static(array_merge_recursive([
'html' => $this->html,
'text' => $this->text
], $props));
public static function factory(
string|array|null $body = null,
string|null $template = null,
array $data = []
): static {
if ($body !== null) {
return match (true) {
is_string($body) => new static(text: $body),
is_array($body) => new static(...$body),
default => $body
};
}

if ($template !== null) {
$kirby = App::instance();

// check if html/text templates exist
$html = $kirby->template('emails/' . $template, 'html', 'text');
$text = $kirby->template('emails/' . $template, 'text', 'text');

if ($html->exists() === false && $text->exists() === false) {
throw new NotFoundException('The email template "' . $template . '" cannot be found');
}

if ($html->exists() === true) {
if ($text->exists() === true) {
return new static(
html: $html->render($data),
text: $text->render($data)
);
}

return new static(html: $html->render($data));
}

return new static(text: $text->render($data));
}

throw new InvalidArgumentException('Email requires either body or template');
}

/**
Expand All @@ -51,21 +75,19 @@ public function html(): string
}

/**
* Returns the plain text content of the email body
* Checks if body is HTML
* @since 5.0.0
*/
public function text(): string
public function isHtml(): bool
{
return $this->text ?? '';
return empty($this->html()) === false;
}

/**
* @since 4.0.0
* Returns the plain text content of the email body
*/
public function toArray(): array
public function text(): string
{
return [
'html' => $this->html(),
'text' => $this->text()
];
return $this->text ?? '';
}
}
7 changes: 5 additions & 2 deletions src/Email/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function __construct(array $props = [], bool $debug = false)
$this->attachments = Attachment::factory($props['attachments'] ?? []);
$this->bcc = Address::factory($props['bcc'] ?? [], multiple: true);
$this->beforeSend = $props['beforeSend'] ?? null;
$this->body = new Body($props['body']);
$this->body = Body::factory($props['body']);
$this->cc = Address::factory($props['cc'] ?? [], multiple: true);
$this->from = Address::factory([$props['from'] => $props['fromName'] ?? null]);
$this->subject = $props['subject'];
Expand Down Expand Up @@ -224,7 +224,10 @@ public function toArray(): array
return [
'attachments' => $this->attachments(),
'bcc' => $this->bcc(),
'body' => $this->body()->toArray(),
'body' => [
'html' => $this->body()->html(),
'text' => $this->body()->text()
],
'cc' => $this->cc(),
'from' => $this->from(),
'fromName' => $this->fromName(),
Expand Down
153 changes: 137 additions & 16 deletions tests/Email/BodyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,161 @@

namespace Kirby\Email;

use Kirby\Cms\App;
use Kirby\Cms\User;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
use Kirby\TestCase;

/**
* @coversDefaultClass \Kirby\Email\Body
* @covers ::__construct
*/
class BodyTest extends TestCase
{
public function testConstruct()
{
$body = new Body();
public const FIXTURES = __DIR__ . '/fixtures';

/**
* @covers ::factory
*/
public function testFactory(): void
{
$body = Body::factory($text = 'test');
$this->assertSame($text, $body->text());
$this->assertSame('', $body->html());
$this->assertSame('', $body->text());

$body = Body::factory([
'text' => $text,
'html' => $html = '<b>test</b>'
]);
$this->assertSame($text, $body->text());
$this->assertSame($html, $body->html());
}

public function testConstructParams()
/**
* @covers ::factory
*/
public function testFactoryTemplate(): void
{
$data = [
'html' => '<strong>We will never reply</strong>',
'text' => 'We will never reply'
];
new App([
'templates' => [
'emails/contact' => static::FIXTURES . '/contact.php'
]
]);

$body = new Body($data);
$body = Body::factory(null, 'contact', ['name' => 'Alex']);
$this->assertSame('Cheers, Alex!', $body->text());
}

$this->assertSame($data['html'], $body->html());
$this->assertSame($data['text'], $body->text());
/**
* @covers ::factory
*/
public function testFactoryTemplateHtml(): void
{
new App([
'templates' => [
'emails/media.html' => static::FIXTURES . '/media.html.php'
]
]);

$body = Body::factory(null, 'media');
$this->assertSame('<b>Image:</b> <img src=""/>', $body->html());
$this->assertSame('', $body->text());
}

/**
* @covers ::factory
*/
public function testFactoryTemplateHtmlText(): void
{
new App([
'templates' => [
'emails/media.html' => static::FIXTURES . '/media.html.php',
'emails/media.text' => static::FIXTURES . '/media.text.php',
]
]);

$body = Body::factory(null, 'media');
$this->assertSame('<b>Image:</b> <img src=""/>', $body->html());
$this->assertSame('Image: Description', $body->text());
}

public function testConstructNullParams()
/**
* @covers ::factory
*/
public function testFactoryTemplateData(): void
{
$body = new Body([
'html' => null,
'text' => null
new App([
'roots' => [
'index' => '/dev/null'
],
'templates' => [
'emails/user-info' => static::FIXTURES . '/user-info.php'
]
]);

$user = new User([
'email' => '[email protected]',
'name' => 'Mario'
]);

$body = Body::factory(null, 'user-info', ['user' => $user]);
$this->assertSame('Welcome, Mario!', trim($body->text()));
}

/**
* @covers ::factory
*/
public function testFactoryTemplateInvalid(): void
{
$this->expectException(NotFoundException::class);
$this->expectExceptionMessage('The email template "subscription" cannot be found');
Body::factory(null, 'subscription');
}

/**
* @covers ::factory
*/
public function testFactoryInvalid(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Email requires either body or template');
Body::factory();
}

/**
* @covers ::html
*/
public function testHtml(): void
{
$body = new Body();
$this->assertSame('', $body->html());

$body = new Body(html: $html = '<strong>Foo</strong>');
$this->assertSame($html, $body->html());
}

/**
* @covers ::isHtml
*/
public function testIsHtml(): void
{
$body = new Body();
$this->assertFalse($body->isHtml());

$body = new Body(html: '<strong>Foo</strong>');
$this->assertTrue($body->isHtml());
}

/**
* @covers ::text
*/
public function testText(): void
{
$body = new Body();
$this->assertSame('', $body->text());

$body = new Body(text: $text = 'Foo');
$this->assertSame($text, $body->text());
}
}
31 changes: 31 additions & 0 deletions tests/Email/EmailTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,37 @@ public function testProperties()
$this->assertSame(['type' => 'mail'], $email->transport());
}

public function testToArray()
{
$email = $this->_email([
'from' => $from = '[email protected]',
'to' => $to = '[email protected]',
'subject' => $subject = 'Thank you for your contact request',
'body' => $body = 'We will never reply',
]);

$expected = [
'attachments' => [],
'bcc' => [],
'body' => [
'html' => '',
'text' => $body
],
'cc' => [],
'from' => $from,
'fromName' => null,
'replyTo' => '',
'replyToName' => null,
'subject' => $subject,
'to' => [$to => null],
'transport' => [
'type' => 'mail'
]
];

$this->assertSame($expected, $email->toArray());
}

public function testRequiredProperty()
{
$this->expectException(Exception::class);
Expand Down
1 change: 1 addition & 0 deletions tests/Email/fixtures/contact.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cheers, <?= $name ?>!
1 change: 1 addition & 0 deletions tests/Email/fixtures/media.html.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<b>Image:</b> <img src=""/>
1 change: 1 addition & 0 deletions tests/Email/fixtures/media.text.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Image: Description
1 change: 1 addition & 0 deletions tests/Email/fixtures/user-info.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Welcome, <?= $user->name() ?>!

0 comments on commit 2b73a54

Please sign in to comment.