diff --git a/src/FileMessage.php b/src/FileMessage.php index 570cd18..bd39ef8 100644 --- a/src/FileMessage.php +++ b/src/FileMessage.php @@ -6,7 +6,7 @@ class FileMessage implements MessageInterface { - use HasParsedMessage; + use HasFlags, HasParsedMessage; /** * Constructor. @@ -23,6 +23,14 @@ public function uid(): int throw new BadMethodCallException('FileMessage does not support a UID'); } + /** + * {@inheritDoc} + */ + public function flag(mixed $flag, string $operation, bool $expunge = false): void + { + throw new BadMethodCallException('FileMessage does not support flagging'); + } + /** * Get the string representation of the message. */ @@ -40,6 +48,14 @@ public function is(MessageInterface $message): bool && $this->contents === $message->contents; } + /** + * Get the message flags. + */ + public function flags(): array + { + return []; + } + /** * Determine if the message is empty. */ diff --git a/src/FlaggableInterface.php b/src/FlaggableInterface.php new file mode 100644 index 0000000..62a0d3c --- /dev/null +++ b/src/FlaggableInterface.php @@ -0,0 +1,125 @@ +markSeen(); + } + + /** + * {@inheritDoc} + */ + public function markUnread(): void + { + $this->unmarkSeen(); + } + + /** + * {@inheritDoc} + */ + public function markSeen(): void + { + $this->flag(ImapFlag::Seen, '+'); + } + + /** + * {@inheritDoc} + */ + public function unmarkSeen(): void + { + $this->flag(ImapFlag::Seen, '-'); + } + + /** + * {@inheritDoc} + */ + public function markAnswered(): void + { + $this->flag(ImapFlag::Answered, '+'); + } + + /** + * {@inheritDoc} + */ + public function unmarkAnswered(): void + { + $this->flag(ImapFlag::Answered, '-'); + } + + /** + * {@inheritDoc} + */ + public function markFlagged(): void + { + $this->flag(ImapFlag::Flagged, '+'); + } + + /** + * {@inheritDoc} + */ + public function unmarkFlagged(): void + { + $this->flag(ImapFlag::Flagged, '-'); + } + + /** + * {@inheritDoc} + */ + public function markDeleted(bool $expunge = false): void + { + $this->flag(ImapFlag::Deleted, '+', $expunge); + } + + /** + * {@inheritDoc} + */ + public function unmarkDeleted(): void + { + $this->flag(ImapFlag::Deleted, '-'); + } + + /** + * {@inheritDoc} + */ + public function markDraft(): void + { + $this->flag(ImapFlag::Draft, '+'); + } + + /** + * {@inheritDoc} + */ + public function unmarkDraft(): void + { + $this->flag(ImapFlag::Draft, '-'); + } + + /** + * {@inheritDoc} + */ + public function markRecent(): void + { + $this->flag(ImapFlag::Recent, '+'); + } + + /** + * {@inheritDoc} + */ + public function unmarkRecent(): void + { + $this->flag(ImapFlag::Recent, '-'); + } + + /** + * {@inheritDoc} + */ + public function isSeen(): bool + { + return $this->hasFlag(ImapFlag::Seen); + } + + /** + * {@inheritDoc} + */ + public function isAnswered(): bool + { + return $this->hasFlag(ImapFlag::Answered); + } + + /** + * {@inheritDoc} + */ + public function isFlagged(): bool + { + return $this->hasFlag(ImapFlag::Flagged); + } + + /** + * {@inheritDoc} + */ + public function isDeleted(): bool + { + return $this->hasFlag(ImapFlag::Deleted); + } + + /** + * {@inheritDoc} + */ + public function isDraft(): bool + { + return $this->hasFlag(ImapFlag::Draft); + } + + /** + * {@inheritDoc} + */ + public function isRecent(): bool + { + return $this->hasFlag(ImapFlag::Recent); + } + + /** + * {@inheritDoc} + */ + public function hasFlag(BackedEnum|string $flag): bool + { + return in_array(Str::enum($flag), $this->flags()); + } + + /** + * {@inheritDoc} + */ + abstract public function flags(): array; + + /** + * {@inheritDoc} + */ + abstract public function flag(mixed $flag, string $operation, bool $expunge = false): void; +} diff --git a/src/Message.php b/src/Message.php index d40f09f..10f3b62 100644 --- a/src/Message.php +++ b/src/Message.php @@ -2,9 +2,7 @@ namespace DirectoryTree\ImapEngine; -use BackedEnum; use DirectoryTree\ImapEngine\Connection\Responses\MessageResponseParser; -use DirectoryTree\ImapEngine\Enums\ImapFlag; use DirectoryTree\ImapEngine\Exceptions\ImapCapabilityException; use DirectoryTree\ImapEngine\Support\Str; use Illuminate\Contracts\Support\Arrayable; @@ -12,7 +10,7 @@ class Message implements Arrayable, JsonSerializable, MessageInterface { - use HasParsedMessage; + use HasFlags, HasParsedMessage; /** * Constructor. @@ -102,174 +100,6 @@ public function is(MessageInterface $message): bool && $this->folder->is($message->folder); } - /** - * Determine if the message is marked as seen. - */ - public function isSeen(): bool - { - return $this->hasFlag(ImapFlag::Seen); - } - - /** - * Determine if the message is marked as answered. - */ - public function isAnswered(): bool - { - return $this->hasFlag(ImapFlag::Answered); - } - - /** - * Determine if the message is flagged. - */ - public function isFlagged(): bool - { - return $this->hasFlag(ImapFlag::Flagged); - } - - /** - * Determine if the message is marked as deleted. - */ - public function isDeleted(): bool - { - return $this->hasFlag(ImapFlag::Deleted); - } - - /** - * Determine if the message is marked as a draft. - */ - public function isDraft(): bool - { - return $this->hasFlag(ImapFlag::Draft); - } - - /** - * Determine if the message is marked as recent. - */ - public function isRecent(): bool - { - return $this->hasFlag(ImapFlag::Recent); - } - - /** - * Determine if the message has the given flag. - */ - public function hasFlag(BackedEnum|string $flag): bool - { - return in_array(Str::enum($flag), $this->flags); - } - - /** - * Mark the message as read. Alias for markSeen. - */ - public function markRead(): void - { - $this->markSeen(); - } - - /** - * Mark the message as unread. Alias for unmarkSeen. - */ - public function markUnread(): void - { - $this->unmarkSeen(); - } - - /** - * Mark the message as seen. - */ - public function markSeen(): void - { - $this->flag(ImapFlag::Seen, '+'); - } - - /** - * Unmark the seen flag. - */ - public function unmarkSeen(): void - { - $this->flag(ImapFlag::Seen, '-'); - } - - /** - * Mark the message as answered. - */ - public function markAnswered(): void - { - $this->flag(ImapFlag::Answered, '+'); - } - - /** - * Unmark the answered flag. - */ - public function unmarkAnswered(): void - { - $this->flag(ImapFlag::Answered, '-'); - } - - /** - * Mark the message as flagged. - */ - public function markFlagged(): void - { - $this->flag(ImapFlag::Flagged, '+'); - } - - /** - * Unmark the flagged flag. - */ - public function unmarkFlagged(): void - { - $this->flag(ImapFlag::Flagged, '-'); - } - - /** - * Mark the message as deleted. - */ - public function markDeleted(bool $expunge = false): void - { - $this->flag(ImapFlag::Deleted, '+', $expunge); - } - - /** - * Unmark the deleted flag. - */ - public function unmarkDeleted(): void - { - $this->flag(ImapFlag::Deleted, '-'); - } - - /** - * Mark the message as a draft. - */ - public function markDraft(): void - { - $this->flag(ImapFlag::Draft, '+'); - } - - /** - * Unmark the draft flag. - */ - public function unmarkDraft(): void - { - $this->flag(ImapFlag::Draft, '-'); - } - - /** - * Mark the message as recent. - */ - public function markRecent(): void - { - $this->flag(ImapFlag::Recent, '+'); - } - - /** - * Unmark the recent flag. - */ - public function unmarkRecent(): void - { - $this->flag(ImapFlag::Recent, '-'); - } - /** * Add or remove a flag from the message. */ diff --git a/src/MessageInterface.php b/src/MessageInterface.php index 610828f..75b42ba 100644 --- a/src/MessageInterface.php +++ b/src/MessageInterface.php @@ -7,7 +7,7 @@ use ZBateson\MailMimeParser\Header\IHeader; use ZBateson\MailMimeParser\Message as MailMimeMessage; -interface MessageInterface extends Stringable +interface MessageInterface extends FlaggableInterface, Stringable { /** * Get the message's identifier. diff --git a/src/Testing/FakeMessage.php b/src/Testing/FakeMessage.php index 564897b..a08e447 100644 --- a/src/Testing/FakeMessage.php +++ b/src/Testing/FakeMessage.php @@ -2,12 +2,14 @@ namespace DirectoryTree\ImapEngine\Testing; +use DirectoryTree\ImapEngine\HasFlags; use DirectoryTree\ImapEngine\HasParsedMessage; use DirectoryTree\ImapEngine\MessageInterface; +use DirectoryTree\ImapEngine\Support\Str; class FakeMessage implements MessageInterface { - use HasParsedMessage; + use HasFlags, HasParsedMessage; /** * Constructor. @@ -37,6 +39,28 @@ public function is(MessageInterface $message): bool && $this->contents === $message->contents; } + /** + * {@inheritDoc} + */ + public function flag(mixed $flag, string $operation, bool $expunge = false): void + { + $flag = Str::enum($flag); + + if ($operation === '+') { + $this->flags = array_unique([...$this->flags, $flag]); + } else { + $this->flags = array_filter($this->flags, fn (string $value) => $value !== $flag); + } + } + + /** + * {@inheritDoc} + */ + public function flags(): array + { + return $this->flags; + } + /** * {@inheritDoc} */ diff --git a/tests/Unit/FileMessageTest.php b/tests/Unit/FileMessageTest.php index e81db11..c1b1890 100644 --- a/tests/Unit/FileMessageTest.php +++ b/tests/Unit/FileMessageTest.php @@ -1,6 +1,7 @@ parse(); })->throws(RuntimeException::class); +test('it throws exception when uid is called', function () { + $message = new FileMessage('test'); + + $message->uid(); +})->throws(BadMethodCallException::class); + +test('it throws exception when flag is called', function () { + $message = new FileMessage('test'); + + $message->flag(ImapFlag::Seen, '+'); +})->throws(BadMethodCallException::class); + +test('it returns empty flags', function () { + $message = new FileMessage('test'); + + expect($message->flags())->toBe([]); +}); + test('it can parse a standard EML message and read basic headers', function () { $contents = <<<'EOT' From: "John Doe" diff --git a/tests/Unit/Testing/FakeMessageTest.php b/tests/Unit/Testing/FakeMessageTest.php index 33c0e53..84981cc 100644 --- a/tests/Unit/Testing/FakeMessageTest.php +++ b/tests/Unit/Testing/FakeMessageTest.php @@ -1,5 +1,6 @@ is($message5))->toBeFalse(); }); + +test('it can add flags using flag method', function () { + $message = new FakeMessage(1, [], 'Test content'); + + expect($message->flags())->toBe([]); + expect($message->isSeen())->toBeFalse(); + expect($message->isFlagged())->toBeFalse(); + + // Add Seen flag + $message->flag('\\Seen', '+'); + expect($message->flags())->toContain('\\Seen'); + expect($message->hasFlag(ImapFlag::Seen))->toBeTrue(); + expect($message->isSeen())->toBeTrue(); + + // Add Flagged flag + $message->flag('\\Flagged', '+'); + expect($message->flags())->toContain('\\Flagged'); + expect($message->hasFlag(ImapFlag::Flagged))->toBeTrue(); + expect($message->isFlagged())->toBeTrue(); + expect($message->flags())->toHaveCount(2); +}); + +test('it can remove flags using flag method', function () { + $message = new FakeMessage(1, ['\\Seen', '\\Flagged'], 'Test content'); + + expect($message->flags())->toContain('\\Seen'); + expect($message->flags())->toContain('\\Flagged'); + expect($message->hasFlag(ImapFlag::Seen))->toBeTrue(); + expect($message->hasFlag(ImapFlag::Flagged))->toBeTrue(); + expect($message->isSeen())->toBeTrue(); + expect($message->isFlagged())->toBeTrue(); + + // Remove Seen flag + $message->flag('\\Seen', '-'); + expect($message->flags())->not->toContain('\\Seen'); + expect($message->isSeen())->toBeFalse(); + expect($message->isFlagged())->toBeTrue(); + + // Remove Flagged flag + $message->flag('\\Flagged', '-'); + expect($message->flags())->not->toContain('\\Flagged'); + expect($message->isFlagged())->toBeFalse(); + expect($message->flags())->toBeEmpty(); +});