forked from wallabag/wallabag
Move to Symfony Flex
The structure changed completely. Bundles are gone. Everything is flatten in the folder `src`. I separated import & api controllers to avoid _pollution_ in the main controller folder.
This commit is contained in:
26
src/Event/EntryDeletedEvent.php
Normal file
26
src/Event/EntryDeletedEvent.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\Entry;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* This event is fired as soon as an entry is deleted.
|
||||
*/
|
||||
class EntryDeletedEvent extends Event
|
||||
{
|
||||
public const NAME = 'entry.deleted';
|
||||
|
||||
protected $entry;
|
||||
|
||||
public function __construct(Entry $entry)
|
||||
{
|
||||
$this->entry = $entry;
|
||||
}
|
||||
|
||||
public function getEntry(): Entry
|
||||
{
|
||||
return $this->entry;
|
||||
}
|
||||
}
|
||||
26
src/Event/EntrySavedEvent.php
Normal file
26
src/Event/EntrySavedEvent.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\Entry;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* This event is fired as soon as an entry was saved.
|
||||
*/
|
||||
class EntrySavedEvent extends Event
|
||||
{
|
||||
public const NAME = 'entry.saved';
|
||||
|
||||
protected $entry;
|
||||
|
||||
public function __construct(Entry $entry)
|
||||
{
|
||||
$this->entry = $entry;
|
||||
}
|
||||
|
||||
public function getEntry(): Entry
|
||||
{
|
||||
return $this->entry;
|
||||
}
|
||||
}
|
||||
44
src/Event/Listener/LocaleListener.php
Normal file
44
src/Event/Listener/LocaleListener.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event\Listener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* @see http://symfony.com/doc/current/cookbook/session/locale_sticky_session.html
|
||||
*/
|
||||
class LocaleListener implements EventSubscriberInterface
|
||||
{
|
||||
private $defaultLocale;
|
||||
|
||||
public function __construct($defaultLocale = 'en')
|
||||
{
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
if (!$request->hasPreviousSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// try to see if the locale has been set as a _locale routing parameter
|
||||
if ($locale = $request->attributes->get('_locale')) {
|
||||
$request->getSession()->set('_locale', $locale);
|
||||
} else {
|
||||
// if no explicit locale has been set on this request, use one from the session
|
||||
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
// must be registered before the default Locale listener
|
||||
KernelEvents::REQUEST => [['onKernelRequest', 17]],
|
||||
];
|
||||
}
|
||||
}
|
||||
35
src/Event/Listener/UserLocaleListener.php
Normal file
35
src/Event/Listener/UserLocaleListener.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event\Listener;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||
|
||||
/**
|
||||
* Stores the locale of the user in the session after the login.
|
||||
* If no locale are defined (if user doesn't change it from the login screen), override it with the user's config one.
|
||||
*
|
||||
* This can be used by the LocaleListener afterwards.
|
||||
*
|
||||
* @see http://symfony.com/doc/master/cookbook/session/locale_sticky_session.html
|
||||
*/
|
||||
class UserLocaleListener
|
||||
{
|
||||
private SessionInterface $session;
|
||||
|
||||
public function __construct(SessionInterface $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function onInteractiveLogin(InteractiveLoginEvent $event)
|
||||
{
|
||||
$user = $event->getAuthenticationToken()->getUser();
|
||||
\assert($user instanceof User);
|
||||
|
||||
if (null !== $user->getConfig()->getLanguage() && null === $this->session->get('_locale')) {
|
||||
$this->session->set('_locale', $user->getConfig()->getLanguage());
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/Event/Subscriber/CustomDoctrineORMSubscriber.php
Normal file
35
src/Event/Subscriber/CustomDoctrineORMSubscriber.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event\Subscriber;
|
||||
|
||||
use Lexik\Bundle\FormFilterBundle\Event\GetFilterConditionEvent;
|
||||
use Lexik\Bundle\FormFilterBundle\Event\Subscriber\DoctrineORMSubscriber;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* This custom class override the default behavior of LexikFilterBundle on `filter_date_range`
|
||||
* It converts a date_range to date_time_range to add hour to be able to grab a whole day (from 00:00:00 to 23:59:59).
|
||||
*/
|
||||
class CustomDoctrineORMSubscriber extends DoctrineORMSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function filterDateRange(GetFilterConditionEvent $event)
|
||||
{
|
||||
$expr = $event->getFilterQuery()->getExpressionBuilder();
|
||||
$values = $event->getValues();
|
||||
$value = $values['value'];
|
||||
|
||||
// left date should start at midnight
|
||||
if (isset($value['left_date'][0]) && $value['left_date'][0] instanceof \DateTime) {
|
||||
$value['left_date'][0]->setTime(0, 0, 0);
|
||||
}
|
||||
|
||||
// right adte should end one second before midnight
|
||||
if (isset($value['right_date'][0]) && $value['right_date'][0] instanceof \DateTime) {
|
||||
$value['right_date'][0]->setTime(23, 59, 59);
|
||||
}
|
||||
|
||||
if (isset($value['left_date'][0]) || isset($value['right_date'][0])) {
|
||||
$event->setCondition($expr->dateTimeInRange($event->getField(), $value['left_date'][0], $value['right_date'][0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
113
src/Event/Subscriber/DownloadImagesSubscriber.php
Normal file
113
src/Event/Subscriber/DownloadImagesSubscriber.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event\Subscriber;
|
||||
|
||||
use App\Entity\Entry;
|
||||
use App\Event\EntryDeletedEvent;
|
||||
use App\Event\EntrySavedEvent;
|
||||
use App\Helper\DownloadImages;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class DownloadImagesSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private $em;
|
||||
private $downloadImages;
|
||||
private $enabled;
|
||||
private $logger;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, DownloadImages $downloadImages, $enabled, LoggerInterface $logger)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->downloadImages = $downloadImages;
|
||||
$this->enabled = $enabled;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
EntrySavedEvent::NAME => 'onEntrySaved',
|
||||
EntryDeletedEvent::NAME => 'onEntryDeleted',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Download images and updated the data into the entry.
|
||||
*/
|
||||
public function onEntrySaved(EntrySavedEvent $event)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
$this->logger->debug('DownloadImagesSubscriber: disabled.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$entry = $event->getEntry();
|
||||
|
||||
$html = $this->downloadImages($entry);
|
||||
if (false !== $html) {
|
||||
$this->logger->debug('DownloadImagesSubscriber: updated html.');
|
||||
|
||||
$entry->setContent($html);
|
||||
}
|
||||
|
||||
// update preview picture
|
||||
$previewPicture = $this->downloadPreviewImage($entry);
|
||||
if (false !== $previewPicture) {
|
||||
$this->logger->debug('DownloadImagesSubscriber: update preview picture.');
|
||||
|
||||
$entry->setPreviewPicture($previewPicture);
|
||||
}
|
||||
|
||||
$this->em->persist($entry);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove images related to the entry.
|
||||
*/
|
||||
public function onEntryDeleted(EntryDeletedEvent $event)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
$this->logger->debug('DownloadImagesSubscriber: disabled.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->downloadImages->removeImages($event->getEntry()->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Download all images from the html.
|
||||
*
|
||||
* @todo If we want to add async download, it should be done in that method
|
||||
*
|
||||
* @return string|false False in case of async
|
||||
*/
|
||||
private function downloadImages(Entry $entry)
|
||||
{
|
||||
return $this->downloadImages->processHtml(
|
||||
$entry->getId(),
|
||||
$entry->getContent(),
|
||||
$entry->getUrl()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the preview picture.
|
||||
*
|
||||
* @todo If we want to add async download, it should be done in that method
|
||||
*
|
||||
* @return string|false False in case of async
|
||||
*/
|
||||
private function downloadPreviewImage(Entry $entry)
|
||||
{
|
||||
return $this->downloadImages->processSingleImage(
|
||||
$entry->getId(),
|
||||
$entry->getPreviewPicture(),
|
||||
$entry->getUrl()
|
||||
);
|
||||
}
|
||||
}
|
||||
65
src/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
Normal file
65
src/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event\Subscriber;
|
||||
|
||||
use App\Entity\Entry;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry.
|
||||
* Foreign Key Support can be enabled by running `PRAGMA foreign_keys = ON;` at runtime (AT RUNTIME !).
|
||||
* But it needs a compilation flag that not all SQLite instance has ...
|
||||
*
|
||||
* @see https://www.sqlite.org/foreignkeys.html#fk_enable
|
||||
*/
|
||||
class SQLiteCascadeDeleteSubscriber implements EventSubscriber
|
||||
{
|
||||
private $doctrine;
|
||||
|
||||
public function __construct(ManagerRegistry $doctrine)
|
||||
{
|
||||
$this->doctrine = $doctrine;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
'preRemove',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* We removed everything related to the upcoming removed entry because SQLite can't handle it on it own.
|
||||
* We do it in the preRemove, because we can't retrieve tags in the postRemove (because the entry id is gone).
|
||||
*/
|
||||
public function preRemove(LifecycleEventArgs $args)
|
||||
{
|
||||
$entity = $args->getEntity();
|
||||
if (!$this->doctrine->getConnection()->getDatabasePlatform() instanceof SqlitePlatform
|
||||
|| !$entity instanceof Entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
$em = $this->doctrine->getManager();
|
||||
|
||||
if (null !== $entity->getTags()) {
|
||||
foreach ($entity->getTags() as $tag) {
|
||||
$entity->removeTag($tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $entity->getAnnotations()) {
|
||||
foreach ($entity->getAnnotations() as $annotation) {
|
||||
$em->remove($annotation);
|
||||
}
|
||||
}
|
||||
|
||||
$em->flush();
|
||||
}
|
||||
}
|
||||
51
src/Event/Subscriber/TablePrefixSubscriber.php
Normal file
51
src/Event/Subscriber/TablePrefixSubscriber.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event\Subscriber;
|
||||
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
|
||||
/**
|
||||
* Puts a prefix to each table.
|
||||
* This way were used instead of using the built-in strategy from Doctrine, using `naming_strategy`
|
||||
* Because it conflicts with the DefaultQuoteStrategy (that espace table name, like user for Postgres)
|
||||
* see #1498 for more detail.
|
||||
*
|
||||
* Solution from :
|
||||
* - http://stackoverflow.com/a/23860613/569101
|
||||
* - http://doctrine-orm.readthedocs.org/en/latest/reference/namingstrategy.html
|
||||
*/
|
||||
class TablePrefixSubscriber implements EventSubscriber
|
||||
{
|
||||
protected $tablePrefix = '';
|
||||
|
||||
public function __construct($tablePrefix)
|
||||
{
|
||||
$this->tablePrefix = (string) $tablePrefix;
|
||||
}
|
||||
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return ['loadClassMetadata'];
|
||||
}
|
||||
|
||||
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
|
||||
{
|
||||
$classMetadata = $args->getClassMetadata();
|
||||
|
||||
// if we are in an inheritance hierarchy, only apply this once
|
||||
if ($classMetadata->isInheritanceTypeSingleTable() && !$classMetadata->isRootEntity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classMetadata->setPrimaryTable(['name' => $this->tablePrefix . $classMetadata->getTableName()]);
|
||||
|
||||
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
|
||||
if (ClassMetadataInfo::MANY_TO_MANY === $mapping['type'] && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) {
|
||||
$mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name'];
|
||||
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->tablePrefix . $mappedTableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user