return $this->renderJsonResponse($json);
}
+ /**
+ * Permanently remove one tag from **every** entry.
+ *
+ * @ApiDoc(
+ * requirements={
+ * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
+ * }
+ * )
+ *
+ * @return Response
+ */
+ public function deleteTagLabelAction(Request $request)
+ {
+ $this->validateAuthentication();
+ $label = $request->request->get('tag', '');
+
+ $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
+
+ if (empty($tag)) {
+ throw $this->createNotFoundException('Tag not found');
+ }
+
+ $this->getDoctrine()
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->removeTag($this->getUser()->getId(), $tag);
+
+ $json = $this->get('serializer')->serialize($tag, 'json');
+
+ return $this->renderJsonResponse($json);
+ }
+
+ /**
+ * Permanently remove some tags from **every** entry.
+ *
+ * @ApiDoc(
+ * requirements={
+ * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
+ * }
+ * )
+ *
+ * @return Response
+ */
+ public function deleteTagsLabelAction(Request $request)
+ {
+ $this->validateAuthentication();
+
+ $tagsLabels = $request->request->get('tags', '');
+
+ $tags = [];
+
+ foreach (explode(',', $tagsLabels) as $tagLabel) {
+ $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
+
+ if (!empty($tagEntity)) {
+ $tags[] = $tagEntity;
+ }
+ }
+
+ if (empty($tags)) {
+ throw $this->createNotFoundException('Tags not found');
+ }
+
+ $this->getDoctrine()
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->removeTags($this->getUser()->getId(), $tags);
+
+ $json = $this->get('serializer')->serialize($tags, 'json');
+
+ return $this->renderJsonResponse($json);
+ }
+
/**
* Permanently remove one tag from **every** entry.
*
return $this->renderJsonResponse($json);
}
+
/**
* Retrieve version number.
*
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Exception\OutOfRangeCurrentPageException;
-use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
}
$pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
- $entries = new Pagerfanta($pagerAdapter);
- $entries->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
+ $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')
+ ->prepare($pagerAdapter, $page);
+
try {
$entries->setCurrentPage($page);
} catch (OutOfRangeCurrentPageException $e) {
*
* @Route("/export/{category}.{format}", name="export_entries", requirements={
* "format": "epub|mobi|pdf|json|xml|txt|csv",
- * "category": "all|unread|starred|archive"
+ * "category": "all|unread|starred|archive|tag_entries"
* })
*
* @return \Symfony\Component\HttpFoundation\Response
namespace Wallabag\CoreBundle\Controller;
+use Pagerfanta\Adapter\ArrayAdapter;
+use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
use Wallabag\CoreBundle\Form\Type\NewTagType;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class TagController extends Controller
{
]
);
}
+
+ /**
+ * @param Tag $tag
+ * @param int $page
+ *
+ * @Route("/tag/list/{slug}/{page}", name="tag_entries", defaults={"page" = "1"})
+ * @ParamConverter("tag", options={"mapping": {"slug": "slug"}})
+ *
+ * @return \Symfony\Component\HttpFoundation\Response
+ */
+ public function showEntriesForTagAction(Tag $tag, $page, Request $request)
+ {
+ $entriesByTag = $this->getDoctrine()
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findAllByTagId($this->getUser()->getId(), $tag->getId());
+
+ $pagerAdapter = new ArrayAdapter($entriesByTag);
+
+ $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')
+ ->prepare($pagerAdapter, $page);
+
+ try {
+ $entries->setCurrentPage($page);
+ } catch (OutOfRangeCurrentPageException $e) {
+ if ($page > 1) {
+ return $this->redirect($this->generateUrl($request->get('_route'), [
+ 'slug' => $tag->getSlug(),
+ 'page' => $entries->getNbPages(),
+ ]), 302);
+ }
+ }
+
+ return $this->render(
+ 'WallabagCoreBundle:Entry:entries.html.twig',
+ [
+ 'form' => null,
+ 'entries' => $entries,
+ 'currentPage' => $page,
+ ]
+ );
+ }
}
$this->addReference('bar-tag', $tag2);
+ $tag3 = new Tag();
+ $tag3->setLabel('baz');
+
+ $manager->persist($tag3);
+
+ $this->addReference('baz-tag', $tag3);
+
$manager->flush();
}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Helper;
+
+use Pagerfanta\Adapter\AdapterInterface;
+use Pagerfanta\Pagerfanta;
+use Symfony\Component\Routing\Router;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
+
+class PreparePagerForEntries
+{
+ private $user;
+ private $router;
+
+ public function __construct(TokenStorage $token, Router $router)
+ {
+ $this->user = $token->getToken()->getUser();
+ $this->router = $router;
+ }
+
+ /**
+ * @param AdapterInterface $adapter
+ * @param int $page
+ *
+ * @return null|Pagerfanta
+ */
+ public function prepare(AdapterInterface $adapter, $page = 1)
+ {
+ $entries = new Pagerfanta($adapter);
+ $entries->setMaxPerPage($this->user->getConfig()->getItemsPerPage());
+
+ return $entries;
+ }
+}
$this->getEntityManager()->flush();
}
+ /**
+ * Remove tags from all user entries.
+ *
+ * @param int $userId
+ * @param Array<Tag> $tags
+ */
+ public function removeTags($userId, $tags)
+ {
+ foreach ($tags as $tag) {
+ $this->removeTag($userId, $tag);
+ }
+ }
+
/**
* Find all entries that are attached to a give tag id.
*
class: Wallabag\CoreBundle\Helper\Redirect
arguments:
- "@router"
+
+ wallabag_core.helper.prepare_pager_for_entries:
+ class: Wallabag\CoreBundle\Helper\PreparePagerForEntries
+ arguments:
+ - "@security.token_storage"
+ - "@router"
# starred: 'Starred entries'
# archived: 'Archived entries'
# filtered: 'Filtered entries'
+ # filtered_tags: 'Filtered by tags'
list:
# number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
reading_time: 'estimeret læsetid'
starred: 'Favorisierte Einträge'
archived: 'Archivierte Einträge'
filtered: 'Gefilterte Einträge'
+ # filtered_tags: 'Filtered by tags'
list:
number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.'
reading_time: 'geschätzte Lesezeit'
starred: 'Starred entries'
archived: 'Archived entries'
filtered: 'Filtered entries'
+ filtered_tags: 'Filtered by tags'
list:
number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
reading_time: 'estimated reading time'
starred: 'Artículos favoritos'
archived: 'Artículos archivados'
filtered: 'Artículos filtrados'
+ # filtered_tags: 'Filtered by tags'
list:
number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.'
reading_time: 'tiempo estimado de lectura'
starred: 'مقالههای برگزیده'
archived: 'مقالههای بایگانیشده'
filtered: 'مقالههای فیلترشده'
+ # filtered_tags: 'Filtered by tags'
list:
number_on_the_page: '{0} هیج مقالهای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.'
reading_time: 'زمان تخمینی برای خواندن'
starred: 'Articles favoris'
archived: 'Articles lus'
filtered: 'Articles filtrés'
+ filtered_tags: 'Articles filtrés par tags'
list:
number_on_the_page: "{0} Il n'y a pas d'articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles."
reading_time: 'durée de lecture'
starred: 'Articles favorits'
archived: 'Articles legits'
filtered: 'Articles filtrats'
+ # filtered_tags: 'Filtered by tags'
list:
number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles."
reading_time: 'durada de lectura'
starred: 'Wpisy oznaczone gwiazdką'
archived: 'Zarchiwizowane wpisy'
filtered: 'Odfiltrowane wpisy'
+ # filtered_tags: 'Filtered by tags'
list:
number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.'
reading_time: 'szacunkowy czas czytania'
# starred: 'Starred entries'
# archived: 'Archived entries'
# filtered: 'Filtered entries'
+ # filtered_tags: 'Filtered by tags'
list:
# number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
reading_time: 'timp estimat de citire'
# starred: 'Starred entries'
# archived: 'Archived entries'
# filtered: 'Filtered entries'
+ # filtered_tags: 'Filtered by tags'
list:
number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.'
reading_time: 'tahmini okuma süresi'
{% extends "WallabagCoreBundle::layout.html.twig" %}
-{% block title %}{{ 'entry.page_titles.unread'|trans }}{% endblock %}
+{% block title %}
+ {% set currentRoute = app.request.attributes.get('_route') %}
+
+ {% if currentRoute == 'starred' %}
+ {{ 'entry.page_titles.starred'|trans }}
+ {% elseif currentRoute == 'archive' %}
+ {{ 'entry.page_titles.archived'|trans }}
+ {% elseif currentRoute == 'all' %}
+ {{ 'entry.page_titles.filtered'|trans }}
+ {% elseif currentRoute == 'tag_entries' %}
+ {{ 'entry.page_titles.filtered_tags'|trans }}
+ {% else %}
+ {{ 'entry.page_titles.unread'|trans }}
+ {% endif %}
+{% endblock %}
{% block content %}
{% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
</div>
{% endfor %}
-
<!-- Export -->
<aside id="download-form">
{% set currentRoute = app.request.attributes.get('_route') %}
{% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml' }) }}">XML</a></li>{% endif %}
</ul>
</aside>
+
<!-- Filter -->
- <aside id="filter-form">
+ {% if form is not null %}
+ <aside id="filter-form" class="">
<form method="get" action="{{ path('all') }}">
<h2>{{ 'entry.filters.title'|trans }}</h2>
<a href="javascript: void(null);" id="filter-form-close" class="close-button--popup close-button">×</a>
</div>
</form>
</aside>
-
- {% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
+ {% endif %}
{% endblock %}
<ul>
{% for tag in tags %}
- <li id="tag-{{ tag.id|e }}">{{tag.label}} ({{ tag.getEntriesByUserId(app.user.id) | length }})</li>
+ <li id="tag-{{ tag.id|e }}"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.entries.getValues | length }})</a></li>
{% endfor %}
</ul>
{% endblock %}
{{ 'entry.page_titles.archived'|trans }}
{% elseif currentRoute == 'all' %}
{{ 'entry.page_titles.filtered'|trans }}
+ {% elseif currentRoute == 'tag_entries' %}
+ {{ 'entry.page_titles.filtered_tags'|trans }}
{% else %}
{{ 'entry.page_titles.unread'|trans }}
{% endif %}
-
{% endblock %}
{% block content %}
</div>
<!-- Filters -->
+ {% if form is not null %}
<div id="filters" class="side-nav fixed right-aligned">
<form action="{{ path('all') }}">
</form>
</div>
- {% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
+ {% endif %}
+
{% endblock %}
<br />
<ul class="row data">
{% for tag in tags %}
- <li id="tag-{{ tag.id|e }}" class="col l4 m6 s12">{{tag.label}} ({{ tag.getEntriesByUserId(app.user.id) | length }})</li>
+ <li id="tag-{{ tag.id|e }}" class="col l4 m6 s12"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.entries.getValues | length }})</a></li>
{% endfor %}
</ul>
{% endblock %}
namespace Tests\Wallabag\ApiBundle\Controller;
use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
+use Wallabag\CoreBundle\Entity\Tag;
class WallabagRestControllerTest extends WallabagApiTestCase
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
- ->findOneWithTags(1);
+ ->findOneWithTags($this->user->getId());
$entry = $entry[0];
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
- ->findOneWithTags(1);
+ ->findOneWithTags($this->user->getId());
$entry = $entry[0];
if (!$entry) {
$this->assertEquals($tag['label'], $content['label']);
$this->assertEquals($tag['slug'], $content['slug']);
- $entries = $entry = $this->client->getContainer()
+ $entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag['id']);
$this->assertCount(0, $entries);
}
+ public function testDeleteTagByLabel()
+ {
+ $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
+ $entry = $this->client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findOneWithTags($this->user->getId());
+
+ $entry = $entry[0];
+
+ $tag = new Tag();
+ $tag->setLabel('Awesome tag for test');
+ $em->persist($tag);
+
+ $entry->addTag($tag);
+
+ $em->persist($entry);
+ $em->flush();
+
+ $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]);
+
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
+
+ $content = json_decode($this->client->getResponse()->getContent(), true);
+
+ $this->assertArrayHasKey('label', $content);
+ $this->assertEquals($tag->getLabel(), $content['label']);
+ $this->assertEquals($tag->getSlug(), $content['slug']);
+
+ $entries = $this->client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findAllByTagId($this->user->getId(), $tag->getId());
+
+ $this->assertCount(0, $entries);
+ }
+
+ public function testDeleteTagByLabelNotFound()
+ {
+ $this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']);
+
+ $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
+ }
+
+ public function testDeleteTagsByLabel()
+ {
+ $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
+ $entry = $this->client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findOneWithTags($this->user->getId());
+
+ $entry = $entry[0];
+
+ $tag = new Tag();
+ $tag->setLabel('Awesome tag for tagsLabel');
+ $em->persist($tag);
+
+ $tag2 = new Tag();
+ $tag2->setLabel('Awesome tag for tagsLabel 2');
+ $em->persist($tag2);
+
+ $entry->addTag($tag);
+ $entry->addTag($tag2);
+
+ $em->persist($entry);
+ $em->flush();
+
+ $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]);
+
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
+
+ $content = json_decode($this->client->getResponse()->getContent(), true);
+
+ $this->assertCount(2, $content);
+
+ $this->assertArrayHasKey('label', $content[0]);
+ $this->assertEquals($tag->getLabel(), $content[0]['label']);
+ $this->assertEquals($tag->getSlug(), $content[0]['slug']);
+
+ $this->assertArrayHasKey('label', $content[1]);
+ $this->assertEquals($tag2->getLabel(), $content[1]['label']);
+ $this->assertEquals($tag2->getSlug(), $content[1]['slug']);
+
+ $entries = $this->client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findAllByTagId($this->user->getId(), $tag->getId());
+
+ $this->assertCount(0, $entries);
+
+ $entries = $this->client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findAllByTagId($this->user->getId(), $tag2->getId());
+
+ $this->assertCount(0, $entries);
+ }
+
+ public function testDeleteTagsByLabelNotFound()
+ {
+ $this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']);
+
+ $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
+ }
+
public function testGetVersion()
{
$this->client->request('GET', '/api/version');
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
+
+ public function testShowEntriesForTagAction()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $entry = $client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Entry')
+ ->findOneByUsernameAndNotArchived('admin');
+
+ $tag = $client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Tag')
+ ->findOneByEntryAndTagLabel($entry, 'foo');
+
+ $crawler = $client->request('GET', '/tag/list/'.$tag->getSlug());
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertCount(2, $crawler->filter('div[class=entry]'));
+
+ $tag = $client->getContainer()
+ ->get('doctrine.orm.entity_manager')
+ ->getRepository('WallabagCoreBundle:Tag')
+ ->findOneByLabel('baz');
+
+ $crawler = $client->request('GET', '/tag/list/'.$tag->getSlug());
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertCount(0, $crawler->filter('div[class=entry]'));
+ }
}