Skip to content

Commit

Permalink
add history of resent mails with state of mail delivery (#54)
Browse files Browse the repository at this point in the history

---------

Co-authored-by: Felix von WIRDUZEN <felix@wirduzen.de>
Co-authored-by: tinect <s.koenig@tinect.de>
  • Loading branch information
3 people committed Oct 14, 2023
1 parent 209e5b2 commit 1a8fd31
Show file tree
Hide file tree
Showing 23 changed files with 519 additions and 56 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -3,3 +3,6 @@
/.project
frosh-platform-mail-archive.css
frosh-platform-mail-archive.js
/vendor/
/src/Resources/public/
/composer.lock
4 changes: 4 additions & 0 deletions src/Content/MailArchive/MailArchiveDefinition.php
Expand Up @@ -46,6 +46,7 @@ protected function defineFields(): FieldCollection
(new LongTextField('htmlText', 'htmlText'))->addFlags(new AllowHtml(), new SearchRanking(SearchRanking::LOW_SEARCH_RANKING)),
(new LongTextField('eml', 'eml'))->addFlags(new AllowHtml()),
(new StringField('eml_path', 'emlPath', 2048)),
(new StringField('transport_state', 'transportState'))->addFlags(new Required()),

(new OneToManyAssociationField('attachments', MailArchiveAttachmentDefinition::class, 'mail_archive_id', 'id'))->addFlags(new CascadeDelete()),

Expand All @@ -54,6 +55,9 @@ protected function defineFields(): FieldCollection

new FkField('customerId', 'customerId', CustomerDefinition::class),
new ManyToOneAssociationField('customer', 'customerId', CustomerDefinition::class, 'id', true),

new FkField('source_mail_id', 'sourceMailId', self::class),
new ManyToOneAssociationField('sourceMail', 'source_mail_id', self::class, 'id', false),
]);
}
}
38 changes: 38 additions & 0 deletions src/Content/MailArchive/MailArchiveEntity.php
Expand Up @@ -24,6 +24,8 @@ class MailArchiveEntity extends Entity

protected ?string $htmlText;

protected ?string $transportState;

/**
* @deprecated will not be filled anyone. Use emlPath instead
*/
Expand All @@ -42,6 +44,11 @@ class MailArchiveEntity extends Entity
/** @var EntityCollection<MailArchiveAttachmentEntity>|null $attachments */
protected ?EntityCollection $attachments = null;

protected ?string $sourceMailId;

protected ?MailArchiveEntity $sourceMail;


/**
* @return array<string, string>
*/
Expand Down Expand Up @@ -185,4 +192,35 @@ public function setAttachments(EntityCollection $attachments): void
{
$this->attachments = $attachments;
}

public function getSourceMailId(): ?string
{
return $this->sourceMailId;
}

public function setSourceMailId(?string $sourceMailId): void
{
$this->sourceMailId = $sourceMailId;
}

public function getSourceMail(): ?MailArchiveEntity
{
return $this->sourceMail;
}

public function setSourceMail(?MailArchiveEntity $sourceMail): void
{
$this->sourceMail = $sourceMail;
}

public function getTransportState(): ?string
{
return $this->transportState;
}

public function setTransportState(string $transportState): void
{
$this->transportState = $transportState;
}

}
7 changes: 5 additions & 2 deletions src/Controller/Api/MailArchiveController.php
Expand Up @@ -7,11 +7,13 @@
use Frosh\MailArchive\Content\MailArchive\MailArchiveException;
use Frosh\MailArchive\Services\EmlFileManager;
use Frosh\MailArchive\Services\MailSender;
use Shopware\Core\Content\Mail\Service\AbstractMailSender;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\PlatformRequest;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
Expand All @@ -29,13 +31,14 @@ class MailArchiveController extends AbstractController
public function __construct(
private readonly EntityRepository $froshMailArchiveRepository,
private readonly EntityRepository $froshMailArchiveAttachmentRepository,
private readonly MailSender $mailSender,
#[Autowire(service: MailSender::class)]
private readonly AbstractMailSender $mailSender,
private readonly RequestStack $requestStack,
private readonly EmlFileManager $emlFileManager
) {
}

#[Route(path: '/api/_action/frosh-mail-archive/resend-mail')]
#[Route(path: '/api/_action/frosh-mail-archive/resend-mail', name: 'api.action.frosh-mail-archive.resend-mail')]
public function resend(Request $request): JsonResponse
{
$mailId = $request->request->get('mailId');
Expand Down
4 changes: 2 additions & 2 deletions src/FroshPlatformMailArchive.php
Expand Up @@ -21,11 +21,11 @@ public function uninstall(UninstallContext $uninstallContext): void

$connection = $container->get(Connection::class);
if (!$connection instanceof Connection) {
return;
return;
}

$connection->executeStatement('DROP TABLE IF EXISTS frosh_mail_archive');
$connection->executeStatement('DROP TABLE IF EXISTS frosh_mail_archive_attachment');
$connection->executeStatement('DROP TABLE IF EXISTS frosh_mail_archive');
}

public function executeComposerCommands(): bool
Expand Down
25 changes: 25 additions & 0 deletions src/Migration/Migration1694604822AddSourceMailId.php
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Frosh\MailArchive\Migration;

use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;

class Migration1694604822AddSourceMailId extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1694604822;
}

public function update(Connection $connection): void
{
$connection->executeStatement("ALTER TABLE `frosh_mail_archive` ADD `source_mail_id` BINARY(16) NULL;");
}

public function updateDestructive(Connection $connection): void
{
}
}
25 changes: 25 additions & 0 deletions src/Migration/Migration1694714751TransportInfo.php
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Frosh\MailArchive\Migration;

use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;

class Migration1694714751TransportInfo extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1694714751;
}

public function update(Connection $connection): void
{
$connection->executeStatement("ALTER TABLE `frosh_mail_archive` ADD `transport_state` VARCHAR(255) NULL;");
}

public function updateDestructive(Connection $connection): void
{
}
}
1 change: 1 addition & 0 deletions src/Resources/app/administration/src/main.js
@@ -1,3 +1,4 @@
import './init/api.init';
import './module/frosh-mail-archive';
import './component/notification-sw-admin';

@@ -0,0 +1,59 @@
<sw-card :title="$tc('frosh-mail-archive.detail.resend-grid.title')">
<template #grid>
<sw-data-grid
:isLoading="isLoading"
:data-source="resentMails"
:columns="columns"
:allowInlineEdit="false"
:allowColumnEdit="false"
:showSettings="false"
:showSelection="false"
>
<template #column-createdAt="{item}">
<template v-if="currentMailId === item.id">
{{ item.createdAt|date }} ({{ $tc('frosh-mail-archive.detail.resend-grid.currently-selected') }})
</template>
<router-link v-else :to="{ name: 'frosh.mail.archive.detail', params: { id: item.id } }">
{{ item.createdAt|date }}
</router-link>
</template>
<template #column-success="{item}">
<div v-if="item.transportState === 'sent'">
<sw-color-badge
color="#37d046"
rounded
/>
{{ $tc('frosh-mail-archive.detail.resend-grid.success-label') }}
</div>
<div v-else-if="item.transportState === 'failed'">
<sw-color-badge
color="#de294c"
rounded
/>
{{ $tc('frosh-mail-archive.detail.resend-grid.failed-label') }}
</div>
<div v-else-if="item.transportState === 'pending'">
<sw-color-badge
color="#ffab22"
rounded
/>
{{ $tc('frosh-mail-archive.detail.resend-grid.pending-label') }}
</div>
<div v-else>
<sw-color-badge
color="#94a6b8"
rounded
/>
{{ $tc('frosh-mail-archive.detail.resend-grid.unknown-label') }}
</div>
</template>
<template #actions="{ item }">
<sw-context-menu-item :disabled="currentMailId === item.id"
@click="navigateToDetailPage(item.id)"
>
{{ $tc('frosh-mail-archive.detail.resend-grid.navigate') }}
</sw-context-menu-item>
</template>
</sw-data-grid>
</template>
</sw-card>
@@ -0,0 +1,57 @@
const {Criteria} = Shopware.Data;
import template from './frosh-mail-resend-history.html.twig';

Shopware.Component.register('frosh-mail-resend-history', {
props: {
sourceMailId: {
required: true,
type: String
},
currentMailId: {
required: true,
type: String
}
},
template,
data() {
return {
resentMails: [],
isLoading: false,
columns: [{
property: 'createdAt',
label: this.$tc('frosh-mail-archive.detail.resend-grid.column-created-at'),
primary: true,
}, {
property: 'success',
label: this.$tc('frosh-mail-archive.detail.resend-grid.column-state'),
sortable: false,
}]
}
},
inject: ['repositoryFactory'],
computed: {
mailArchiveRepository() {
return this.repositoryFactory.create('frosh_mail_archive');
}
},
async created() {
this.isLoading = true;
await this.loadMails();
this.isLoading = false;
},
methods: {
async loadMails() {
const criteria = new Criteria();
criteria.addFilter(Criteria.multi('OR', [
Criteria.equals('id', this.sourceMailId),
Criteria.equals('sourceMailId', this.sourceMailId)
]));
criteria.addSorting(Criteria.sort('createdAt', 'DESC'));

this.resentMails = await this.mailArchiveRepository.search(criteria, Shopware.Context.api);
},
navigateToDetailPage(id) {
this.$router.push({name: 'frosh.mail.archive.detail', params: {id}})
}
}
});
@@ -1,5 +1,6 @@
import './page/frosh-mail-archive-index/index';
import './page/frosh-mail-archive-detail/index';
import './component/frosh-mail-resend-history'

Shopware.Module.register('frosh-mail-archive', {
type: 'plugin',
Expand All @@ -20,7 +21,12 @@ Shopware.Module.register('frosh-mail-archive', {
path: 'detail/:id',
meta: {
parentPath: 'frosh.mail.archive.list'
}
},
props: {
default: ($route) => {
return { archiveId: $route.params.id };
},
},
}
},

Expand Down
Expand Up @@ -9,3 +9,7 @@
white-space: pre-line;
}
}

.frosh-mail-archive__detail-alert {
max-width: 960px;
}
Expand Up @@ -8,30 +8,47 @@
{{ $tc('frosh-mail-archive.detail.toolbar.customer') }}
</sw-button>

<sw-button-process :isLoading="downloadIsLoading" :processSuccess="downloadIsSuccessful" @click="downloadMail">
<sw-button-process :isLoading="downloadIsLoading" :processSuccess="downloadIsSuccessful" @click="downloadMail"
@process-finish="downloadFinish">
{{ $tc('frosh-mail-archive.detail.toolbar.downloadEml') }}
</sw-button-process>

<sw-button-process :isLoading="resendIsLoading" :processSuccess="resendIsSuccessful" @click="resendMail">
<sw-button-process :isLoading="resendIsLoading" :processSuccess="resendIsSuccessful" @click="resendMail"
@process-finish="resendFinish">
{{ $tc('frosh-mail-archive.detail.toolbar.resend') }}
</sw-button-process>
</template>

<template slot="content">
<sw-card-view v-if="archive">
<sw-alert
v-if="archive.transportState === 'failed'"
variant="warning"
class="frosh-mail-archive__detail-alert"
>
{{ $tc('frosh-mail-archive.detail.alert.transportFailed') }}
</sw-alert>
<sw-card
:title="$tc('frosh-mail-archive.detail.metadata.title')"
position-identifier="frosh-mail-archive-metadata"
:title="$tc('frosh-mail-archive.detail.metadata.title')"
position-identifier="frosh-mail-archive-metadata"
>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.sentDate')" :disabled="true" v-model="createdAtDate"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.sender')" :disabled="true" v-model="senderText"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.receiver')" :disabled="true" v-model="receiverText"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.subject')" :disabled="true" v-model="archive.subject"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.salesChannel')" v-if="archive.salesChannel" :disabled="true" v-model="archive.salesChannel.name"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.sentDate')" :disabled="true"
v-model="createdAtDate"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.sender')" :disabled="true"
v-model="senderText"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.receiver')" :disabled="true"
v-model="receiverText"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.subject')" :disabled="true"
v-model="archive.subject"></sw-text-field>
<sw-text-field :label="$tc('frosh-mail-archive.detail.metadata.salesChannel')"
v-if="archive.salesChannel" :disabled="true"
v-model="archive.salesChannel.name"></sw-text-field>
</sw-card>
<frosh-mail-resend-history :key="resendKey" :currentMailId="archive.id"
:sourceMailId="archive.sourceMailId ?? archive.id"/>
<sw-card
:title="$tc('frosh-mail-archive.detail.content.title')"
position-identifier="frosh-mail-archive-content"
:title="$tc('frosh-mail-archive.detail.content.title')"
position-identifier="frosh-mail-archive-content"
>
<h4>HTML</h4>
<iframe :src="htmlText" sandbox frameborder="0"></iframe>
Expand All @@ -57,7 +74,8 @@
</template>

<template slot="actions" slot-scope="{ item }">
<sw-context-menu-item class="sw-entity-listing__context-menu-show-action" @click="downloadAttachment(item.id)">
<sw-context-menu-item class="sw-entity-listing__context-menu-show-action"
@click="downloadAttachment(item.id)">
Download
</sw-context-menu-item>
</template>
Expand Down

0 comments on commit 1a8fd31

Please sign in to comment.