--- /dev/null
+<?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.');
+ }
+ }
+}
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
{
$em = $this->getDoctrine()->getManager();
$em->flush();
+ $this->get('event_dispatcher')->dispatch(EntryUpdatedEvent::NAME, new EntryUpdatedEvent($entry));
+
return $this->sendResponse($entry);
}
$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);
$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);
}
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);
+ }
}
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
$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()]));
$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';
$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';
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();
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;
$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()
);
$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'
$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);
--- /dev/null
+<?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;
+ }
+}
*/
private $tags;
+ /**
+ * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\Change", mappedBy="entry", cascade={"remove"})
+ */
+ private $changes;
+
/*
* @param User $user
*/
{
$this->user = $user;
$this->tags = new ArrayCollection();
+ $this->changes = new ArrayCollection();
}
/**
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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 ');
+ }
+}
--- /dev/null
+<?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();
+ }
+}
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 }