]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
Save changes
authorThomas Citharel <tcit@tcit.fr>
Fri, 24 Feb 2017 10:34:36 +0000 (11:34 +0100)
committerJeremy Benoist <jeremy.benoist@gmail.com>
Tue, 30 May 2017 09:47:39 +0000 (11:47 +0200)
PHP CS

Fixed Events on changes

Renamed field

First draft for migration (create table Change)

Added setter for tag in EntryTaggedEvent

Fixed migration for Change table

Added API route for entry history

Removed deletion history

app/DoctrineMigrations/Version20170328185535.php [new file with mode: 0644]
src/Wallabag/ApiBundle/Controller/EntryRestController.php
src/Wallabag/CoreBundle/Controller/EntryController.php
src/Wallabag/CoreBundle/Controller/TagController.php
src/Wallabag/CoreBundle/Entity/Change.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Entity/Entry.php
src/Wallabag/CoreBundle/Event/EntryTaggedEvent.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Event/EntryUpdatedEvent.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Event/Subscriber/ChangesSubscriber.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Repository/ChangeRepository.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/config/services.yml

diff --git a/app/DoctrineMigrations/Version20170328185535.php b/app/DoctrineMigrations/Version20170328185535.php
new file mode 100644 (file)
index 0000000..f0afb7b
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+namespace Application\Migrations;
+
+use Doctrine\DBAL\Migrations\AbstractMigration;
+use Doctrine\DBAL\Schema\Schema;
+use Doctrine\DBAL\Schema\SchemaException;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Doctrine\DBAL\Migrations\SkipMigrationException;
+
+/**
+ * Creates the Change table.
+ */
+class Version20170328185535 extends AbstractMigration implements ContainerAwareInterface
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    public function setContainer(ContainerInterface $container = null)
+    {
+        $this->container = $container;
+    }
+
+    private function getTable($tableName)
+    {
+        return $this->container->getParameter('database_table_prefix').$tableName;
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        try {
+            $schema->getTable($this->getTable('change'));
+        } catch (SchemaException $e) {
+            // The Change table doesn't exist, we need to create it
+            if (10 == $e->getCode()) {
+                if ($this->connection->getDatabasePlatform()->getName() == 'sqlite') {
+                    $this->addSql('CREATE TABLE '.$this->getTable('change').' (id INTEGER NOT NULL, entry_id INTEGER DEFAULT NULL, type INTEGER NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_133B9D0FBA364942 FOREIGN KEY (entry_id) REFERENCES '.$this->getTable('entry').' (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
+
+                    return true;
+                }
+
+                $changeTable = $schema->createTable($this->getTable('change'));
+                $changeTable->addColumn(
+                    'id',
+                    'integer',
+                    ['autoincrement' => true]
+                );
+                $changeTable->addColumn(
+                    'type',
+                    'integer',
+                    ['notnull' => false]
+                );
+                $changeTable->addColumn(
+                    'created_at',
+                    'datetime',
+                    ['notnull' => false]
+                );
+                $changeTable->addColumn(
+                    'entry_id',
+                    'integer',
+                    ['notnull' => false]
+                );
+
+                $changeTable->setPrimaryKey(['id']);
+
+                $changeTable->addForeignKeyConstraint(
+                    $this->getTable('entry'),
+                    ['entry_id'],
+                    ['id'],
+                    ['onDelete' => 'CASCADE'],
+                    'IDX_change_entry'
+                );
+
+                return true;
+            }
+        }
+
+        throw new SkipMigrationException('It seems that you already played this migration.');
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        try {
+            $changeTable = $schema->getTable($this->getTable('change'));
+            $schema->dropTable($changeTable->getName());
+        } catch (SchemaException $e) {
+            throw new SkipMigrationException('It seems that you already played this migration.');
+        }
+    }
+}
index 31bb67fd7d834ed391f08b2c942ca78d6c1056f9..2cf562bc5fceaf3e2333d2d743b08e92dfc028de 100644 (file)
@@ -12,7 +12,8 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 use Wallabag\CoreBundle\Entity\Entry;
 use Wallabag\CoreBundle\Entity\Tag;
 use Wallabag\CoreBundle\Event\EntrySavedEvent;
-use Wallabag\CoreBundle\Event\EntryDeletedEvent;
+use Wallabag\CoreBundle\Event\EntryTaggedEvent;
+use Wallabag\CoreBundle\Event\EntryUpdatedEvent;
 
 class EntryRestController extends WallabagRestController
 {
@@ -385,6 +386,8 @@ class EntryRestController extends WallabagRestController
         $em = $this->getDoctrine()->getManager();
         $em->flush();
 
+        $this->get('event_dispatcher')->dispatch(EntryUpdatedEvent::NAME, new EntryUpdatedEvent($entry));
+
         return $this->sendResponse($entry);
     }
 
@@ -426,6 +429,7 @@ class EntryRestController extends WallabagRestController
         $em->flush();
 
         // entry saved, dispatch event about it!
+        $this->get('event_dispatcher')->dispatch(EntryUpdatedEvent::NAME, new EntryUpdatedEvent($entry));
         $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
 
         return $this->sendResponse($entry);
@@ -496,14 +500,17 @@ class EntryRestController extends WallabagRestController
         $this->validateUserAccess($entry->getUser()->getId());
 
         $tags = $request->request->get('tags', '');
+        $tagsEntries = [];
         if (!empty($tags)) {
-            $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags);
+            $tagsEntries = $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags);
         }
 
         $em = $this->getDoctrine()->getManager();
         $em->persist($entry);
         $em->flush();
 
+        $this->get('event_dispatcher')->dispatch(EntryTaggedEvent::NAME, new EntryTaggedEvent($entry, $tagsEntries));
+
         return $this->sendResponse($entry);
     }
 
@@ -650,4 +657,28 @@ class EntryRestController extends WallabagRestController
 
         return (new JsonResponse())->setJson($json);
     }
+
+    /**
+     * Gets history since a date.
+     *
+     * @ApiDoc(
+     *       parameters={
+     *          {"name"="since", "dataType"="integer", "required"=true, "format"="A timestamp", "description"="Timestamp of the history's start"},
+     *       }
+     * )
+     *
+     * @return JsonResponse
+     */
+    public function getEntriesHistoryAction(Request $request)
+    {
+        $this->validateAuthentication();
+
+        $res = $this->getDoctrine()
+            ->getRepository('WallabagCoreBundle:Change')
+            ->findChangesSinceDate($request->query->get('since'));
+
+        $json = $this->get('serializer')->serialize($res, 'json');
+
+        return (new JsonResponse())->setJson($json);
+    }
 }
index 8d2ac6d4cf144fd515bf347ec1d0dfa12beb725d..2a287825747dd17085760f58088ba756d2e2fe82 100644 (file)
@@ -9,12 +9,12 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 use Wallabag\CoreBundle\Entity\Entry;
+use Wallabag\CoreBundle\Event\EntryUpdatedEvent;
 use Wallabag\CoreBundle\Form\Type\EntryFilterType;
 use Wallabag\CoreBundle\Form\Type\EditEntryType;
 use Wallabag\CoreBundle\Form\Type\NewEntryType;
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
 use Wallabag\CoreBundle\Event\EntrySavedEvent;
-use Wallabag\CoreBundle\Event\EntryDeletedEvent;
 use Wallabag\CoreBundle\Form\Type\SearchEntryType;
 
 class EntryController extends Controller
@@ -391,6 +391,7 @@ class EntryController extends Controller
         $em->flush();
 
         // entry saved, dispatch event about it!
+        $this->get('event_dispatcher')->dispatch(EntryUpdatedEvent::NAME, new EntryUpdatedEvent($entry));
         $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
 
         return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
@@ -413,6 +414,8 @@ class EntryController extends Controller
         $entry->toggleArchive();
         $this->getDoctrine()->getManager()->flush();
 
+        $this->get('event_dispatcher')->dispatch(EntryUpdatedEvent::NAME, new EntryUpdatedEvent($entry));
+
         $message = 'flashes.entry.notice.entry_unarchived';
         if ($entry->isArchived()) {
             $message = 'flashes.entry.notice.entry_archived';
@@ -445,6 +448,8 @@ class EntryController extends Controller
         $entry->toggleStar();
         $this->getDoctrine()->getManager()->flush();
 
+        $this->get('event_dispatcher')->dispatch(EntryUpdatedEvent::NAME, new EntryUpdatedEvent($entry));
+
         $message = 'flashes.entry.notice.entry_unstarred';
         if ($entry->isStarred()) {
             $message = 'flashes.entry.notice.entry_starred';
@@ -481,9 +486,6 @@ class EntryController extends Controller
             UrlGeneratorInterface::ABSOLUTE_PATH
         );
 
-        // entry deleted, dispatch event about it!
-        $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
-
         $em = $this->getDoctrine()->getManager();
         $em->remove($entry);
         $em->flush();
index fb6a720b6e9d73d5b2608a505cf36a3a5836ed91..e6d55931ccbf52a1ea4937eae574a2b66c657d28 100644 (file)
@@ -9,6 +9,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\Request;
 use Wallabag\CoreBundle\Entity\Entry;
 use Wallabag\CoreBundle\Entity\Tag;
+use Wallabag\CoreBundle\Event\EntryTaggedEvent;
 use Wallabag\CoreBundle\Form\Type\NewTagType;
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
 
@@ -28,7 +29,7 @@ class TagController extends Controller
         $form->handleRequest($request);
 
         if ($form->isSubmitted() && $form->isValid()) {
-            $this->get('wallabag_core.tags_assigner')->assignTagsToEntry(
+            $tags = $this->get('wallabag_core.tags_assigner')->assignTagsToEntry(
                 $entry,
                 $form->get('label')->getData()
             );
@@ -37,6 +38,8 @@ class TagController extends Controller
             $em->persist($entry);
             $em->flush();
 
+            $this->get('event_dispatcher')->dispatch(EntryTaggedEvent::NAME, new EntryTaggedEvent($entry, $tags));
+
             $this->get('session')->getFlashBag()->add(
                 'notice',
                 'flashes.tag.notice.tag_added'
@@ -64,6 +67,8 @@ class TagController extends Controller
         $em = $this->getDoctrine()->getManager();
         $em->flush();
 
+        $this->get('event_dispatcher')->dispatch(EntryTaggedEvent::NAME, new EntryTaggedEvent($entry, $tag));
+
         // remove orphan tag in case no entries are associated to it
         if (count($tag->getEntries()) === 0) {
             $em->remove($tag);
diff --git a/src/Wallabag/CoreBundle/Entity/Change.php b/src/Wallabag/CoreBundle/Entity/Change.php
new file mode 100644 (file)
index 0000000..203a2d3
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+namespace Wallabag\CoreBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * Change.
+ *
+ * This entity stores a datetime for each event (updated or tagged) done on an entry.
+ *
+ * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\ChangeRepository")
+ * @ORM\Table(name="`change`")
+ */
+class Change
+{
+    const MODIFIED_TYPE = 1;
+    const CHANGED_TAG_TYPE = 2;
+
+    /**
+     * @var int
+     *
+     * @ORM\Column(type="integer")
+     * @ORM\Id
+     * @ORM\GeneratedValue(strategy="AUTO")
+     */
+    private $id;
+
+    /**
+     * @var int
+     *
+     * @ORM\Column(type="integer")
+     */
+    private $type;
+
+    /**
+     * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="changes")
+     */
+    private $entry;
+
+    /**
+     * @var \DateTime
+     *
+     * @ORM\Column(name="created_at", type="datetime")
+     */
+    private $createdAt;
+
+    public function __construct($type, Entry $entry)
+    {
+        $this->type = $type;
+        $this->entry = $entry;
+        $this->createdAt = new \DateTime();
+    }
+
+    /**
+     * @return int
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * @return int
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * @return DateTime
+     */
+    public function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+
+    /**
+     * @return Entry
+     */
+    public function getEntry()
+    {
+        return $this->entry;
+    }
+}
index 08a67c34b1f00275b1f61b3cf5bed3b734800560..a24409b9cbda3510f48344dfad71139b87da0e70 100644 (file)
@@ -233,6 +233,11 @@ class Entry
      */
     private $tags;
 
+    /**
+     * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\Change", mappedBy="entry", cascade={"remove"})
+     */
+    private $changes;
+
     /*
      * @param User     $user
      */
@@ -240,6 +245,7 @@ class Entry
     {
         $this->user = $user;
         $this->tags = new ArrayCollection();
+        $this->changes = new ArrayCollection();
     }
 
     /**
diff --git a/src/Wallabag/CoreBundle/Event/EntryTaggedEvent.php b/src/Wallabag/CoreBundle/Event/EntryTaggedEvent.php
new file mode 100644 (file)
index 0000000..88856fb
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+namespace Wallabag\CoreBundle\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+use Wallabag\CoreBundle\Entity\Entry;
+use Wallabag\CoreBundle\Entity\Tag;
+
+/**
+ * This event is fired as soon as a tag is added on an entry.
+ */
+class EntryTaggedEvent extends Event
+{
+    const NAME = 'entry.tagged';
+
+    /** @var Entry */
+    protected $entry;
+
+    /** @var Tag[] */
+    protected $tags;
+
+    public function __construct(Entry $entry, $tags)
+    {
+        $this->entry = $entry;
+
+        if (false === is_array($tags)) {
+            $tags = [$tags];
+        }
+
+        $this->tags = $tags;
+    }
+
+    public function getEntry()
+    {
+        return $this->entry;
+    }
+
+    public function getTags()
+    {
+        return $this->tags;
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Event/EntryUpdatedEvent.php b/src/Wallabag/CoreBundle/Event/EntryUpdatedEvent.php
new file mode 100644 (file)
index 0000000..7282835
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+namespace Wallabag\CoreBundle\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+use Wallabag\CoreBundle\Entity\Entry;
+
+/**
+ * This event is fired as soon as an entry was updated.
+ */
+class EntryUpdatedEvent extends Event
+{
+    const NAME = 'entry.updated';
+
+    protected $entry;
+
+    public function __construct(Entry $entry)
+    {
+        $this->entry = $entry;
+    }
+
+    public function getEntry()
+    {
+        return $this->entry;
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/ChangesSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/ChangesSubscriber.php
new file mode 100644 (file)
index 0000000..1ba18a4
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+namespace Wallabag\CoreBundle\Event\Subscriber;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Doctrine\ORM\EntityManager;
+use Psr\Log\LoggerInterface;
+use Wallabag\CoreBundle\Entity\Change;
+use Wallabag\CoreBundle\Event\EntryTaggedEvent;
+use Wallabag\CoreBundle\Event\EntryUpdatedEvent;
+
+class ChangesSubscriber implements EventSubscriberInterface
+{
+    /** @var LoggerInterface $logger */
+    private $logger;
+
+    /** @var EntityManager $em */
+    private $em;
+
+    public function __construct(EntityManager $em, LoggerInterface $logger)
+    {
+        $this->logger = $logger;
+        $this->em = $em;
+    }
+
+    public static function getSubscribedEvents()
+    {
+        return [
+            EntryUpdatedEvent::NAME => 'onEntryUpdated',
+            EntryTaggedEvent::NAME => 'onEntryTagged',
+        ];
+    }
+
+    /**
+     * @param EntryUpdatedEvent $event
+     */
+    public function onEntryUpdated(EntryUpdatedEvent $event)
+    {
+        $change = new Change(Change::MODIFIED_TYPE, $event->getEntry());
+
+        $this->em->persist($change);
+        $this->em->flush();
+
+        $this->logger->debug('saved updated entry '.$event->getEntry()->getId().' event ');
+    }
+
+    /**
+     * @param EntryTaggedEvent $event
+     */
+    public function onEntryTagged(EntryTaggedEvent $event)
+    {
+        $change = new Change(Change::CHANGED_TAG_TYPE, $event->getEntry());
+
+        $this->em->persist($change);
+        $this->em->flush();
+
+        $this->logger->debug('saved (un)tagged entry '.$event->getEntry()->getId().' event ');
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Repository/ChangeRepository.php b/src/Wallabag/CoreBundle/Repository/ChangeRepository.php
new file mode 100644 (file)
index 0000000..18d015a
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+namespace Wallabag\CoreBundle\Repository;
+
+use Doctrine\ORM\EntityRepository;
+
+class ChangeRepository extends EntityRepository
+{
+    /**
+     * Used only in test case to get a tag for our entry.
+     *
+     * @param int $timestamp
+     *
+     * @return Tag
+     */
+    public function findChangesSinceDate($timestamp)
+    {
+        $date = new \DateTime();
+        $date->setTimestamp($timestamp);
+
+        return $this->createQueryBuilder('c')
+            ->where('c.createdAt >= :timestamp')->setParameter('timestamp', $date)
+            ->getQuery()
+            ->getResult();
+    }
+}
index a68b2fdcdb875e27f0a05cfae1fd2bf306d8720b..0def87f301c1353a274b23a6c915307f47d224fb 100644 (file)
@@ -199,3 +199,11 @@ services:
 
     wallabag_core.entry.download_images.client:
         class: GuzzleHttp\Client
+
+    wallabag_core.subscriber.changes:
+        class: Wallabag\CoreBundle\Event\Subscriber\ChangesSubscriber
+        arguments:
+            - "@doctrine.orm.default_entity_manager"
+            - "@logger"
+        tags:
+            - { name: kernel.event_subscriber }