add importation mails and views
This commit is contained in:
parent
933a7116c6
commit
331a8f8b09
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,6 +5,9 @@
|
||||||
/.env.*.local
|
/.env.*.local
|
||||||
/config/secrets/prod/prod.decrypt.private.php
|
/config/secrets/prod/prod.decrypt.private.php
|
||||||
/public/bundles/
|
/public/bundles/
|
||||||
|
/public/attachments/
|
||||||
|
/migrations/
|
||||||
|
/data/
|
||||||
/var/
|
/var/
|
||||||
/vendor/
|
/vendor/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
1
.php-version
Normal file
1
.php-version
Normal file
|
@ -0,0 +1 @@
|
||||||
|
7.4
|
|
@ -13,11 +13,16 @@
|
||||||
"doctrine/orm": "^2.7",
|
"doctrine/orm": "^2.7",
|
||||||
"php-mime-mail-parser/php-mime-mail-parser": "^6.0",
|
"php-mime-mail-parser/php-mime-mail-parser": "^6.0",
|
||||||
"ramsey/uuid-doctrine": "^1.6",
|
"ramsey/uuid-doctrine": "^1.6",
|
||||||
|
"sensio/framework-extra-bundle": "^5.6",
|
||||||
|
"symfony/apache-pack": "^1.0",
|
||||||
|
"symfony/asset": "5.2.*",
|
||||||
"symfony/console": "5.2.*",
|
"symfony/console": "5.2.*",
|
||||||
"symfony/dotenv": "5.2.*",
|
"symfony/dotenv": "5.2.*",
|
||||||
|
"symfony/filesystem": "5.2.*",
|
||||||
"symfony/flex": "^1.3.1",
|
"symfony/flex": "^1.3.1",
|
||||||
"symfony/framework-bundle": "5.2.*",
|
"symfony/framework-bundle": "5.2.*",
|
||||||
"symfony/maker-bundle": "^1.23",
|
"symfony/maker-bundle": "^1.23",
|
||||||
|
"symfony/twig-bundle": "5.2.*",
|
||||||
"symfony/yaml": "5.2.*"
|
"symfony/yaml": "5.2.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|
|
@ -5,4 +5,6 @@ return [
|
||||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
|
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
|
||||||
|
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
@ -4,6 +4,7 @@ doctrine:
|
||||||
|
|
||||||
# IMPORTANT: You MUST configure your server version,
|
# IMPORTANT: You MUST configure your server version,
|
||||||
# either here or in the DATABASE_URL env var (see .env file)
|
# either here or in the DATABASE_URL env var (see .env file)
|
||||||
|
charset: UTF8
|
||||||
#server_version: '5.7'
|
#server_version: '5.7'
|
||||||
orm:
|
orm:
|
||||||
auto_generate_proxy_classes: true
|
auto_generate_proxy_classes: true
|
||||||
|
|
3
config/packages/sensio_framework_extra.yaml
Normal file
3
config/packages/sensio_framework_extra.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
sensio_framework_extra:
|
||||||
|
router:
|
||||||
|
annotations: false
|
2
config/packages/test/twig.yaml
Normal file
2
config/packages/test/twig.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
twig:
|
||||||
|
strict_variables: true
|
2
config/packages/twig.yaml
Normal file
2
config/packages/twig.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
twig:
|
||||||
|
default_path: '%kernel.project_dir%/templates'
|
|
@ -1,3 +1,7 @@
|
||||||
|
controllers:
|
||||||
|
resource: '../src/Controller/'
|
||||||
|
type: annotation
|
||||||
|
|
||||||
#index:
|
#index:
|
||||||
# path: /
|
# path: /
|
||||||
# controller: App\Controller\DefaultController::index
|
# controller: App\Controller\DefaultController::index
|
||||||
|
|
66
public/.htaccess
Normal file
66
public/.htaccess
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# Use the front controller as index file. It serves as a fallback solution when
|
||||||
|
# every other rewrite/redirect fails (e.g. in an aliased environment without
|
||||||
|
# mod_rewrite). Additionally, this reduces the matching process for the
|
||||||
|
# start page (path "/") because otherwise Apache will apply the rewriting rules
|
||||||
|
# to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl).
|
||||||
|
DirectoryIndex index.php
|
||||||
|
|
||||||
|
# By default, Apache does not evaluate symbolic links if you did not enable this
|
||||||
|
# feature in your server configuration. Uncomment the following line if you
|
||||||
|
# install assets as symlinks or if you experience problems related to symlinks
|
||||||
|
# when compiling LESS/Sass/CoffeScript assets.
|
||||||
|
# Options +FollowSymlinks
|
||||||
|
|
||||||
|
# Disabling MultiViews prevents unwanted negotiation, e.g. "/index" should not resolve
|
||||||
|
# to the front controller "/index.php" but be rewritten to "/index.php/index".
|
||||||
|
<IfModule mod_negotiation.c>
|
||||||
|
Options -MultiViews
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
# Determine the RewriteBase automatically and set it as environment variable.
|
||||||
|
# If you are using Apache aliases to do mass virtual hosting or installed the
|
||||||
|
# project in a subdirectory, the base path will be prepended to allow proper
|
||||||
|
# resolution of the index.php file and to redirect to the correct URI. It will
|
||||||
|
# work in environments without path prefix as well, providing a safe, one-size
|
||||||
|
# fits all solution. But as you do not need it in this case, you can comment
|
||||||
|
# the following 2 lines to eliminate the overhead.
|
||||||
|
RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
|
||||||
|
RewriteRule .* - [E=BASE:%1]
|
||||||
|
|
||||||
|
# Sets the HTTP_AUTHORIZATION header removed by Apache
|
||||||
|
RewriteCond %{HTTP:Authorization} .+
|
||||||
|
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||||
|
|
||||||
|
# Redirect to URI without front controller to prevent duplicate content
|
||||||
|
# (with and without `/index.php`). Only do this redirect on the initial
|
||||||
|
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
|
||||||
|
# endless redirect loop (request -> rewrite to front controller ->
|
||||||
|
# redirect -> request -> ...).
|
||||||
|
# So in case you get a "too many redirects" error or you always get redirected
|
||||||
|
# to the start page because your Apache does not expose the REDIRECT_STATUS
|
||||||
|
# environment variable, you have 2 choices:
|
||||||
|
# - disable this feature by commenting the following 2 lines or
|
||||||
|
# - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
|
||||||
|
# following RewriteCond (best solution)
|
||||||
|
RewriteCond %{ENV:REDIRECT_STATUS} =""
|
||||||
|
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]
|
||||||
|
|
||||||
|
# If the requested filename exists, simply serve it.
|
||||||
|
# We only want to let Apache serve files and not directories.
|
||||||
|
# Rewrite all other queries to the front controller.
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ %{ENV:BASE}/index.php [L]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<IfModule !mod_rewrite.c>
|
||||||
|
<IfModule mod_alias.c>
|
||||||
|
# When mod_rewrite is not available, we instruct a temporary redirect of
|
||||||
|
# the start page to the front controller explicitly so that the website
|
||||||
|
# and the generated links can still be used.
|
||||||
|
RedirectMatch 307 ^/$ /index.php/
|
||||||
|
# RedirectTemp cannot be used instead
|
||||||
|
</IfModule>
|
||||||
|
</IfModule>
|
117
src/Command/MailImportCommand.php
Normal file
117
src/Command/MailImportCommand.php
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use App\Repository\MailingRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use PhpMimeMailParser\Parser;
|
||||||
|
use App\Entity\Mail;
|
||||||
|
use Symfony\Component\HttpKernel\KernelInterface;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use App\Entity\MailAttachment;
|
||||||
|
|
||||||
|
class MailImportCommand extends Command
|
||||||
|
{
|
||||||
|
protected static $defaultName = 'mail:import';
|
||||||
|
|
||||||
|
protected EntityManagerInterface $em;
|
||||||
|
|
||||||
|
protected MailingRepository $mailingRepo;
|
||||||
|
|
||||||
|
protected KernelInterface $kernel;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $em, MailingRepository $mailingRepo, KernelInterface $kernel)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->em = $em;
|
||||||
|
$this->mailingRepo = $mailingRepo;
|
||||||
|
$this->kernel = $kernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setDescription('Import a mail into a mailing')
|
||||||
|
->addArgument('mailing_id', InputArgument::OPTIONAL, 'ID of the mailing')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$mailingId = $input->getArgument('mailing_id');
|
||||||
|
|
||||||
|
$mailing = $this->mailingRepo->find(['id' => $mailingId]);
|
||||||
|
|
||||||
|
if (null === $mailing) {
|
||||||
|
$io->error(sprintf('Mailing "%s" is not found!', $mailingId));
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stdIn = file_get_contents('php://stdin');
|
||||||
|
|
||||||
|
if (empty($stdIn)) {
|
||||||
|
$io->error('Standard input is empty');
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parser = new Parser();
|
||||||
|
$parser->setText($stdIn);
|
||||||
|
|
||||||
|
$subject = $parser->getHeader('subject');
|
||||||
|
$date = $parser->getHeader('date');
|
||||||
|
$text = $parser->getMessageBody('text');
|
||||||
|
$htmlEmbeddedContent = $parser->getMessageBody('htmlEmbedded');
|
||||||
|
$attachments = $parser->getAttachments();
|
||||||
|
|
||||||
|
if ($subject === false && $date === false) {
|
||||||
|
$io->error('The subject and the date are empty. Is it a valid mail?');
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity = new Mail();
|
||||||
|
$entity
|
||||||
|
->setMailing($mailing)
|
||||||
|
->setSubject($subject)
|
||||||
|
->setDate(new \DateTime($date))
|
||||||
|
->setTextContent($text)
|
||||||
|
->setHtmlContent($htmlEmbeddedContent);
|
||||||
|
|
||||||
|
$this->em->persist($entity);
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
if (!empty($attachments)) {
|
||||||
|
$attachmentsDirectory = $this->kernel->getProjectDir().'/public/attachments/'.$mailing->getId().'/'.$entity->getId();
|
||||||
|
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
$filesystem->mkdir($attachmentsDirectory);
|
||||||
|
|
||||||
|
foreach ($attachments as $attachment) {
|
||||||
|
$filename = basename($attachment->save($attachmentsDirectory, Parser::ATTACHMENT_DUPLICATE_SUFFIX));
|
||||||
|
|
||||||
|
$mailAttachment = new MailAttachment();
|
||||||
|
$mailAttachment
|
||||||
|
->setMail($entity)
|
||||||
|
->setContentType($attachment->getContentType())
|
||||||
|
->setFilename($filename);
|
||||||
|
|
||||||
|
$this->em->persist($mailAttachment);
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->success('Mail imported!');
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
58
src/Controller/MailController.php
Normal file
58
src/Controller/MailController.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Mail;
|
||||||
|
use App\Entity\Mailing;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class MailController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/mail/{mailing}/{id}/show", name="mail_show")
|
||||||
|
*/
|
||||||
|
public function show(string $mailing, Mail $mail): Response
|
||||||
|
{
|
||||||
|
if ($mail->getMailing()->getId() !== $mailing) {
|
||||||
|
throw $this->createNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('mail/show.html.twig', [
|
||||||
|
'mail' => $mail,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/mail/{mailing}/{id}/html", name="mail_html")
|
||||||
|
*/
|
||||||
|
public function html(string $mailing, Mail $mail): Response
|
||||||
|
{
|
||||||
|
if ($mail->getMailing()->getId() !== $mailing) {
|
||||||
|
throw $this->createNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('mail/html.html.twig', [
|
||||||
|
'mail' => $mail,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/mail/{mailing}/{id}/text", name="mail_text")
|
||||||
|
*/
|
||||||
|
public function text(string $mailing, Mail $mail): Response
|
||||||
|
{
|
||||||
|
if ($mail->getMailing()->getId() !== $mailing) {
|
||||||
|
throw $this->createNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->render('mail/text.html.twig', [
|
||||||
|
'mail' => $mail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->headers->set('Content-Type', 'text/plain');
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
38
src/Controller/MailingController.php
Normal file
38
src/Controller/MailingController.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use App\Repository\MailRepository;
|
||||||
|
use App\Entity\Mailing;
|
||||||
|
use App\Entity\Mail;
|
||||||
|
|
||||||
|
class MailingController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/mailing/{id}/rss", name="mailing_rss")
|
||||||
|
*/
|
||||||
|
public function rss(Mailing $mailing, MailRepository $mailRepository): Response
|
||||||
|
{
|
||||||
|
$mails = $mailRepository->findAll(
|
||||||
|
[
|
||||||
|
'mailing' => $mailing->getId(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'date' => 'DESC',
|
||||||
|
],
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->render('mailing/rss.html.twig', [
|
||||||
|
'mailing' => $mailing,
|
||||||
|
'mails' => $mails,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->headers->set('Content-Type', 'application/rss+xml');
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,8 +51,7 @@ trait Timestampable
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUpdatedAt(?\DateTime $updatedAt): self
|
||||||
public function setUpdatedAt(?DateTime $updatedAt): self
|
|
||||||
{
|
{
|
||||||
$this->updatedAt = $updatedAt;
|
$this->updatedAt = $updatedAt;
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,24 @@
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use App\Repository\MailRepository;
|
use App\Repository\MailRepository;
|
||||||
|
use App\Doctrine\Timestampable;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity(repositoryClass=MailRepository::class)
|
* @ORM\Entity(repositoryClass=MailRepository::class)
|
||||||
|
* @ORM\HasLifecycleCallbacks
|
||||||
*/
|
*/
|
||||||
class Mail
|
class Mail
|
||||||
{
|
{
|
||||||
|
use Timestampable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Id
|
* @ORM\Id()
|
||||||
* @ORM\GeneratedValue
|
* @ORM\GeneratedValue(strategy="CUSTOM")
|
||||||
* @ORM\Column(type="integer")
|
* @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
|
||||||
|
* @ORM\Column(type="uuid")
|
||||||
*/
|
*/
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
|
@ -29,7 +36,7 @@ class Mail
|
||||||
private $subject;
|
private $subject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="date")
|
* @ORM\Column(type="datetime")
|
||||||
*/
|
*/
|
||||||
private $date;
|
private $date;
|
||||||
|
|
||||||
|
@ -43,7 +50,17 @@ class Mail
|
||||||
*/
|
*/
|
||||||
private $textContent;
|
private $textContent;
|
||||||
|
|
||||||
public function getId(): ?int
|
/**
|
||||||
|
* @ORM\OneToMany(targetEntity=MailAttachment::class, mappedBy="mail", orphanRemoval=true)
|
||||||
|
*/
|
||||||
|
private $mailAttachments;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->mailAttachments = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?string
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
@ -107,4 +124,34 @@ class Mail
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection|MailAttachment[]
|
||||||
|
*/
|
||||||
|
public function getMailAttachments(): Collection
|
||||||
|
{
|
||||||
|
return $this->mailAttachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMailAttachment(MailAttachment $mailAttachment): self
|
||||||
|
{
|
||||||
|
if (!$this->mailAttachments->contains($mailAttachment)) {
|
||||||
|
$this->mailAttachments[] = $mailAttachment;
|
||||||
|
$mailAttachment->setMail($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeMailAttachment(MailAttachment $mailAttachment): self
|
||||||
|
{
|
||||||
|
if ($this->mailAttachments->removeElement($mailAttachment)) {
|
||||||
|
// set the owning side to null (unless already changed)
|
||||||
|
if ($mailAttachment->getMail() === $this) {
|
||||||
|
$mailAttachment->setMail(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
76
src/Entity/MailAttachment.php
Normal file
76
src/Entity/MailAttachment.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Repository\MailAttachmentRepository;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity(repositoryClass=MailAttachmentRepository::class)
|
||||||
|
*/
|
||||||
|
class MailAttachment
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity=Mail::class, inversedBy="mailAttachments")
|
||||||
|
* @ORM\JoinColumn(nullable=false)
|
||||||
|
*/
|
||||||
|
private $mail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $contentType;
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMail(): ?Mail
|
||||||
|
{
|
||||||
|
return $this->mail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMail(?Mail $mail): self
|
||||||
|
{
|
||||||
|
$this->mail = $mail;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilename(): ?string
|
||||||
|
{
|
||||||
|
return $this->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFilename(string $filename): self
|
||||||
|
{
|
||||||
|
$this->filename = $filename;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContentType(): ?string
|
||||||
|
{
|
||||||
|
return $this->contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContentType(string $contentType): self
|
||||||
|
{
|
||||||
|
$this->contentType = $contentType;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
50
src/Repository/MailAttachmentRepository.php
Normal file
50
src/Repository/MailAttachmentRepository.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\MailAttachment;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method MailAttachment|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method MailAttachment|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method MailAttachment[] findAll()
|
||||||
|
* @method MailAttachment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class MailAttachmentRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, MailAttachment::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @return MailAttachment[] Returns an array of MailAttachment objects
|
||||||
|
// */
|
||||||
|
/*
|
||||||
|
public function findByExampleField($value)
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('m')
|
||||||
|
->andWhere('m.exampleField = :val')
|
||||||
|
->setParameter('val', $value)
|
||||||
|
->orderBy('m.id', 'ASC')
|
||||||
|
->setMaxResults(10)
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function findOneBySomeField($value): ?MailAttachment
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('m')
|
||||||
|
->andWhere('m.exampleField = :val')
|
||||||
|
->setParameter('val', $value)
|
||||||
|
->getQuery()
|
||||||
|
->getOneOrNullResult()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
50
symfony.lock
50
symfony.lock
|
@ -132,6 +132,33 @@
|
||||||
"config/packages/ramsey_uuid_doctrine.yaml"
|
"config/packages/ramsey_uuid_doctrine.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"sensio/framework-extra-bundle": {
|
||||||
|
"version": "5.2",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "5.2",
|
||||||
|
"ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/sensio_framework_extra.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"symfony/apache-pack": {
|
||||||
|
"version": "1.0",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes-contrib",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "1.0",
|
||||||
|
"ref": "71599f5b0fdeeeec0fb90e9b17c85e6f5e1350c1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"public/.htaccess"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"symfony/asset": {
|
||||||
|
"version": "v5.2.0-rc1"
|
||||||
|
},
|
||||||
"symfony/cache": {
|
"symfony/cache": {
|
||||||
"version": "v5.2.0-rc1"
|
"version": "v5.2.0-rc1"
|
||||||
},
|
},
|
||||||
|
@ -271,6 +298,26 @@
|
||||||
"symfony/string": {
|
"symfony/string": {
|
||||||
"version": "v5.2.0-rc1"
|
"version": "v5.2.0-rc1"
|
||||||
},
|
},
|
||||||
|
"symfony/translation-contracts": {
|
||||||
|
"version": "v2.3.0"
|
||||||
|
},
|
||||||
|
"symfony/twig-bridge": {
|
||||||
|
"version": "v5.2.0-rc1"
|
||||||
|
},
|
||||||
|
"symfony/twig-bundle": {
|
||||||
|
"version": "5.0",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "5.0",
|
||||||
|
"ref": "fab9149bbaa4d5eca054ed93f9e1b66cc500895d"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/test/twig.yaml",
|
||||||
|
"config/packages/twig.yaml",
|
||||||
|
"templates/base.html.twig"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/var-dumper": {
|
"symfony/var-dumper": {
|
||||||
"version": "v5.2.0-rc1"
|
"version": "v5.2.0-rc1"
|
||||||
},
|
},
|
||||||
|
@ -280,6 +327,9 @@
|
||||||
"symfony/yaml": {
|
"symfony/yaml": {
|
||||||
"version": "v5.2.0-rc1"
|
"version": "v5.2.0-rc1"
|
||||||
},
|
},
|
||||||
|
"twig/twig": {
|
||||||
|
"version": "v3.1.1"
|
||||||
|
},
|
||||||
"webimpress/safe-writer": {
|
"webimpress/safe-writer": {
|
||||||
"version": "2.1.0"
|
"version": "2.1.0"
|
||||||
}
|
}
|
||||||
|
|
12
templates/base.html.twig
Normal file
12
templates/base.html.twig
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||||
|
{% block stylesheets %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
{% block javascripts %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
1
templates/mail/html.html.twig
Normal file
1
templates/mail/html.html.twig
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ mail.htmlContent|raw }}
|
87
templates/mail/show.html.twig
Normal file
87
templates/mail/show.html.twig
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ mail.subject }}</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-12 {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<ul class="nav nav-tabs" id="tabs">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" id="html-tab" data-toggle="tab" href="#html" role="tab" aria-controls="html" aria-selected="true">
|
||||||
|
HTML
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="text-tab" data-toggle="tab" href="#text" role="tab" aria-controls="text" aria-selected="true">
|
||||||
|
Texte
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="attachments-tab" data-toggle="tab" href="#attachments" role="tab" aria-controls="text" aria-selected="true">
|
||||||
|
Pièces jointes
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane fade show active" id="html" role="tabpanel" aria-labelledby="html-tab">
|
||||||
|
<iframe src="{{ path('mail_html', {mailing: mail.mailing.id, id: mail.id}) }}"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="text" role="tabpanel" aria-labelledby="text-tab">
|
||||||
|
<iframe src="{{ path('mail_text', {mailing: mail.mailing.id, id: mail.id}) }}"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="attachments" role="tabpanel" aria-labelledby="attachments-tab">
|
||||||
|
{% if mail.mailAttachments|length %}
|
||||||
|
<ul>
|
||||||
|
{% for item in mail.mailAttachments %}
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="{{ asset('attachments/' ~ mail.mailing.id ~ '/' ~ mail.id ~ '/' ~ item.filename) }}">
|
||||||
|
{{ item.filename }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span class="badge badge-secondary">
|
||||||
|
{{ item.contentType }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Aucune pièce jointe
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
templates/mail/text.html.twig
Normal file
1
templates/mail/text.html.twig
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ mail.textContent|raw }}
|
24
templates/mailing/rss.html.twig
Normal file
24
templates/mailing/rss.html.twig
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||||||
|
<channel>
|
||||||
|
<title>{{ mailing.label }}</title>
|
||||||
|
|
||||||
|
{% for item in mails %}
|
||||||
|
<item>
|
||||||
|
<title><![CDATA[{{ item.subject|raw }}]]></title>
|
||||||
|
<link>{{ absolute_url(path('mail_show', {mailing: mailing.id, id: item.id})) }}</link>
|
||||||
|
<description>
|
||||||
|
<![CDATA[
|
||||||
|
{% if item.htmlContent %}
|
||||||
|
{{ item.htmlContent|raw }}
|
||||||
|
{% else %}
|
||||||
|
{{ item.textContent|raw }}
|
||||||
|
{% endif %}
|
||||||
|
]]></description>
|
||||||
|
<guid isPermaLink="false">{{ item.id }}</guid>
|
||||||
|
<pubDate>{{ item.date|date('r') }}</pubDate>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
|
|
Loading…
Reference in a new issue