Merge pull request #3207 from wallabag/tag-rss

Add RSS for tags
This commit is contained in:
Jérémy Benoist 2017-06-21 14:51:46 +02:00 committed by GitHub
commit 8c68acff2a
31 changed files with 258 additions and 103 deletions

View file

@ -61,6 +61,7 @@ security:
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /tags/(.*).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/share, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/share, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/settings, roles: ROLE_SUPER_ADMIN } - { path: ^/settings, roles: ROLE_SUPER_ADMIN }
- { path: ^/annotations, roles: ROLE_USER } - { path: ^/annotations, roles: ROLE_USER }

View file

@ -46,7 +46,7 @@ class CleanDuplicatesCommand extends ContainerAwareCommand
return 1; return 1;
} }
} else { } else {
$users = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findAll(); $users = $this->getContainer()->get('wallabag_user.user_repository')->findAll();
$output->writeln(sprintf('Cleaning through %d user accounts', count($users))); $output->writeln(sprintf('Cleaning through %d user accounts', count($users)));
@ -66,7 +66,7 @@ class CleanDuplicatesCommand extends ContainerAwareCommand
private function cleanDuplicates(User $user) private function cleanDuplicates(User $user)
{ {
$em = $this->getContainer()->get('doctrine.orm.entity_manager'); $em = $this->getContainer()->get('doctrine.orm.entity_manager');
$repo = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); $repo = $this->getContainer()->get('wallabag_core.entry_repository');
$entries = $repo->getAllEntriesIdAndUrl($user->getId()); $entries = $repo->getAllEntriesIdAndUrl($user->getId());
@ -109,7 +109,7 @@ class CleanDuplicatesCommand extends ContainerAwareCommand
*/ */
private function getUser($username) private function getUser($username)
{ {
return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
} }
private function getDoctrine() private function getDoctrine()

View file

@ -32,15 +32,14 @@ class ExportCommand extends ContainerAwareCommand
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
try { try {
$user = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($input->getArgument('username')); $user = $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($input->getArgument('username'));
} catch (NoResultException $e) { } catch (NoResultException $e) {
$output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username')));
return 1; return 1;
} }
$entries = $this->getDoctrine() $entries = $this->getContainer()->get('wallabag_core.entry_repository')
->getRepository('WallabagCoreBundle:Entry')
->getBuilderForAllByUser($user->getId()) ->getBuilderForAllByUser($user->getId())
->getQuery() ->getQuery()
->getResult(); ->getResult();

View file

@ -67,7 +67,7 @@ class ShowUserCommand extends ContainerAwareCommand
*/ */
private function getUser($username) private function getUser($username)
{ {
return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
} }
private function getDoctrine() private function getDoctrine()

View file

@ -59,7 +59,7 @@ class TagAllCommand extends ContainerAwareCommand
*/ */
private function getUser($username) private function getUser($username)
{ {
return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
} }
private function getDoctrine() private function getDoctrine()

View file

@ -152,8 +152,7 @@ class ConfigController extends Controller
], ],
'twofactor_auth' => $this->getParameter('twofactor_auth'), 'twofactor_auth' => $this->getParameter('twofactor_auth'),
'wallabag_url' => $this->getParameter('domain_name'), 'wallabag_url' => $this->getParameter('domain_name'),
'enabled_users' => $this->getDoctrine() 'enabled_users' => $this->get('wallabag_user.user_repository')
->getRepository('WallabagUserBundle:User')
->getSumEnabledUsers(), ->getSumEnabledUsers(),
]); ]);
} }
@ -257,9 +256,7 @@ class ConfigController extends Controller
// manually remove tags to avoid orphan tag // manually remove tags to avoid orphan tag
$this->removeAllTagsByUserId($this->getUser()->getId()); $this->removeAllTagsByUserId($this->getUser()->getId());
$this->getDoctrine() $this->get('wallabag_core.entry_repository')->removeAllByUserId($this->getUser()->getId());
->getRepository('WallabagCoreBundle:Entry')
->removeAllByUserId($this->getUser()->getId());
break; break;
case 'archived': case 'archived':
if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) {
@ -269,9 +266,7 @@ class ConfigController extends Controller
// manually remove tags to avoid orphan tag // manually remove tags to avoid orphan tag
$this->removeTagsForArchivedByUserId($this->getUser()->getId()); $this->removeTagsForArchivedByUserId($this->getUser()->getId());
$this->getDoctrine() $this->get('wallabag_core.entry_repository')->removeArchivedByUserId($this->getUser()->getId());
->getRepository('WallabagCoreBundle:Entry')
->removeArchivedByUserId($this->getUser()->getId());
break; break;
} }
@ -295,8 +290,7 @@ class ConfigController extends Controller
return; return;
} }
$this->getDoctrine() $this->get('wallabag_core.entry_repository')
->getRepository('WallabagCoreBundle:Entry')
->removeTags($userId, $tags); ->removeTags($userId, $tags);
// cleanup orphan tags // cleanup orphan tags
@ -318,7 +312,7 @@ class ConfigController extends Controller
*/ */
private function removeAllTagsByUserId($userId) private function removeAllTagsByUserId($userId)
{ {
$tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId); $tags = $this->get('wallabag_core.tag_repository')->findAllTags($userId);
$this->removeAllTagsByStatusAndUserId($tags, $userId); $this->removeAllTagsByStatusAndUserId($tags, $userId);
} }
@ -329,7 +323,7 @@ class ConfigController extends Controller
*/ */
private function removeTagsForArchivedByUserId($userId) private function removeTagsForArchivedByUserId($userId)
{ {
$tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findForArchivedArticlesByUser($userId); $tags = $this->get('wallabag_core.tag_repository')->findForArchivedArticlesByUser($userId);
$this->removeAllTagsByStatusAndUserId($tags, $userId); $this->removeAllTagsByStatusAndUserId($tags, $userId);
} }
@ -393,8 +387,7 @@ class ConfigController extends Controller
*/ */
public function deleteAccountAction(Request $request) public function deleteAccountAction(Request $request)
{ {
$enabledUsers = $this->getDoctrine() $enabledUsers = $this->get('wallabag_user.user_repository')
->getRepository('WallabagUserBundle:User')
->getSumEnabledUsers(); ->getSumEnabledUsers();
if ($enabledUsers <= 1) { if ($enabledUsers <= 1) {

View file

@ -7,7 +7,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
/** /**
* The try/catch can be removed once all formats will be implemented. * The try/catch can be removed once all formats will be implemented.
@ -57,16 +56,17 @@ class ExportController extends Controller
{ {
$method = ucfirst($category); $method = ucfirst($category);
$methodBuilder = 'getBuilderFor'.$method.'ByUser'; $methodBuilder = 'getBuilderFor'.$method.'ByUser';
$repository = $this->get('wallabag_core.entry_repository');
if ($category == 'tag_entries') { if ($category == 'tag_entries') {
$tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneBySlug($request->query->get('tag')); $tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag'));
$entries = $this->getDoctrine() $entries = $repository->findAllByTagId(
->getRepository('WallabagCoreBundle:Entry') $this->getUser()->getId(),
->findAllByTagId($this->getUser()->getId(), $tag->getId()); $tag->getId()
);
} else { } else {
$entries = $this->getDoctrine() $entries = $repository
->getRepository('WallabagCoreBundle:Entry')
->$methodBuilder($this->getUser()->getId()) ->$methodBuilder($this->getUser()->getId())
->getQuery() ->getQuery()
->getResult(); ->getResult();

View file

@ -3,13 +3,16 @@
namespace Wallabag\CoreBundle\Controller; namespace Wallabag\CoreBundle\Controller;
use Pagerfanta\Adapter\DoctrineORMAdapter; use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Adapter\ArrayAdapter;
use Pagerfanta\Exception\OutOfRangeCurrentPageException; use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
use Wallabag\UserBundle\Entity\User; use Wallabag\UserBundle\Entity\User;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@ -23,7 +26,7 @@ class RssController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function showUnreadAction(Request $request, User $user) public function showUnreadRSSAction(Request $request, User $user)
{ {
return $this->showEntries('unread', $user, $request->query->get('page', 1)); return $this->showEntries('unread', $user, $request->query->get('page', 1));
} }
@ -31,12 +34,12 @@ class RssController extends Controller
/** /**
* Shows read entries for current user. * Shows read entries for current user.
* *
* @Route("/{username}/{token}/archive.xml", name="archive_rss") * @Route("/{username}/{token}/archive.xml", name="archive_rss", defaults={"_format"="xml"})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function showArchiveAction(Request $request, User $user) public function showArchiveRSSAction(Request $request, User $user)
{ {
return $this->showEntries('archive', $user, $request->query->get('page', 1)); return $this->showEntries('archive', $user, $request->query->get('page', 1));
} }
@ -44,16 +47,88 @@ class RssController extends Controller
/** /**
* Shows starred entries for current user. * Shows starred entries for current user.
* *
* @Route("/{username}/{token}/starred.xml", name="starred_rss") * @Route("/{username}/{token}/starred.xml", name="starred_rss", defaults={"_format"="xml"})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function showStarredAction(Request $request, User $user) public function showStarredRSSAction(Request $request, User $user)
{ {
return $this->showEntries('starred', $user, $request->query->get('page', 1)); return $this->showEntries('starred', $user, $request->query->get('page', 1));
} }
/**
* Shows all entries for current user.
*
* @Route("/{username}/{token}/all.xml", name="all_rss", defaults={"_format"="xml"})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showAllRSSAction(Request $request, User $user)
{
return $this->showEntries('all', $user, $request->query->get('page', 1));
}
/**
* Shows entries associated to a tag for current user.
*
* @Route("/{username}/{token}/tags/{slug}.xml", name="tag_rss", defaults={"_format"="xml"})
* @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
* @ParamConverter("tag", options={"mapping": {"slug": "slug"}})
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showTagsAction(Request $request, User $user, Tag $tag)
{
$page = $request->query->get('page', 1);
$url = $this->generateUrl(
'tag_rss',
[
'username' => $user->getUsername(),
'token' => $user->getConfig()->getRssToken(),
'slug' => $tag->getSlug(),
],
UrlGeneratorInterface::ABSOLUTE_URL
);
$entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId(
$user->getId(),
$tag->getId()
);
$pagerAdapter = new ArrayAdapter($entriesByTag);
$entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare(
$pagerAdapter,
$user
);
if (null === $entries) {
throw $this->createNotFoundException('No entries found?');
}
try {
$entries->setCurrentPage($page);
} catch (OutOfRangeCurrentPageException $e) {
if ($page > 1) {
return $this->redirect($url.'?page='.$entries->getNbPages(), 302);
}
}
return $this->render(
'@WallabagCore/themes/common/Entry/entries.xml.twig',
[
'url_html' => $this->generateUrl('tag_entries', ['slug' => $tag->getSlug()], UrlGeneratorInterface::ABSOLUTE_URL),
'type' => 'tag ('.$tag->getLabel().')',
'url' => $url,
'entries' => $entries,
],
new Response('', 200, ['Content-Type' => 'application/rss+xml'])
);
}
/** /**
* Global method to retrieve entries depending on the given type * Global method to retrieve entries depending on the given type
* It returns the response to be send. * It returns the response to be send.
@ -66,7 +141,7 @@ class RssController extends Controller
*/ */
private function showEntries($type, User $user, $page = 1) private function showEntries($type, User $user, $page = 1)
{ {
$repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); $repository = $this->get('wallabag_core.entry_repository');
switch ($type) { switch ($type) {
case 'starred': case 'starred':
@ -81,6 +156,10 @@ class RssController extends Controller
$qb = $repository->getBuilderForUnreadByUser($user->getId()); $qb = $repository->getBuilderForUnreadByUser($user->getId());
break; break;
case 'all':
$qb = $repository->getBuilderForAllByUser($user->getId());
break;
default: default:
throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
} }
@ -108,10 +187,15 @@ class RssController extends Controller
} }
} }
return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ return $this->render(
'type' => $type, '@WallabagCore/themes/common/Entry/entries.xml.twig',
'url' => $url, [
'entries' => $entries, 'url_html' => $this->generateUrl($type, [], UrlGeneratorInterface::ABSOLUTE_URL),
]); 'type' => $type,
'url' => $url,
'entries' => $entries,
],
new Response('', 200, ['Content-Type' => 'application/rss+xml'])
);
} }
} }

View file

@ -84,16 +84,17 @@ class TagController extends Controller
*/ */
public function showTagAction() public function showTagAction()
{ {
$tags = $this->getDoctrine() $repository = $this->get('wallabag_core.entry_repository');
->getRepository('WallabagCoreBundle:Tag') $tags = $this->get('wallabag_core.tag_repository')
->findAllTags($this->getUser()->getId()); ->findAllTags($this->getUser()->getId());
$flatTags = []; $flatTags = [];
foreach ($tags as $tag) { foreach ($tags as $tag) {
$nbEntries = $this->getDoctrine() $nbEntries = $repository->countAllEntriesByUserIdAndTagId(
->getRepository('WallabagCoreBundle:Entry') $this->getUser()->getId(),
->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId()); $tag->getId()
);
$flatTags[] = [ $flatTags[] = [
'id' => $tag->getId(), 'id' => $tag->getId(),
@ -119,9 +120,10 @@ class TagController extends Controller
*/ */
public function showEntriesForTagAction(Tag $tag, $page, Request $request) public function showEntriesForTagAction(Tag $tag, $page, Request $request)
{ {
$entriesByTag = $this->getDoctrine() $entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId(
->getRepository('WallabagCoreBundle:Entry') $this->getUser()->getId(),
->findAllByTagId($this->getUser()->getId(), $tag->getId()); $tag->getId()
);
$pagerAdapter = new ArrayAdapter($entriesByTag); $pagerAdapter = new ArrayAdapter($entriesByTag);
@ -142,7 +144,7 @@ class TagController extends Controller
'form' => null, 'form' => null,
'entries' => $entries, 'entries' => $entries,
'currentPage' => $page, 'currentPage' => $page,
'tag' => $tag->getSlug(), 'tag' => $tag,
]); ]);
} }
} }

View file

@ -4,6 +4,7 @@ namespace Wallabag\CoreBundle\Helper;
use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Adapter\AdapterInterface;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Wallabag\UserBundle\Entity\User;
use Symfony\Component\Routing\Router; use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
@ -20,12 +21,15 @@ class PreparePagerForEntries
/** /**
* @param AdapterInterface $adapter * @param AdapterInterface $adapter
* @param User $user If user isn't logged in, we can force it (like for rss)
* *
* @return null|Pagerfanta * @return null|Pagerfanta
*/ */
public function prepare(AdapterInterface $adapter) public function prepare(AdapterInterface $adapter, User $user = null)
{ {
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; if (null === $user) {
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
}
if (null === $user || !is_object($user)) { if (null === $user || !is_object($user)) {
return; return;

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Nulstil token' token_reset: 'Nulstil token'
rss_links: 'RSS-Links' rss_links: 'RSS-Links'
rss_link: rss_link:
unread: 'ulæst' unread: 'Ulæst'
starred: 'favoritter' starred: 'Favoritter'
archive: 'arkiv' archive: 'Arkiv'
# all: 'All'
# rss_limit: 'Number of items in the feed' # rss_limit: 'Number of items in the feed'
form_user: form_user:
# two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion"

View file

@ -93,6 +93,7 @@ config:
unread: 'Ungelesene' unread: 'Ungelesene'
starred: 'Favoriten' starred: 'Favoriten'
archive: 'Archivierte' archive: 'Archivierte'
# all: 'All'
rss_limit: 'Anzahl der Einträge pro Feed' rss_limit: 'Anzahl der Einträge pro Feed'
form_user: form_user:
two_factor_description: "Wenn du die Zwei-Faktor-Authentifizierung aktivierst, erhältst du eine E-Mail mit einem Code bei jeder nicht vertrauenswürdigen Verbindung" two_factor_description: "Wenn du die Zwei-Faktor-Authentifizierung aktivierst, erhältst du eine E-Mail mit einem Code bei jeder nicht vertrauenswürdigen Verbindung"

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Regenerate your token' token_reset: 'Regenerate your token'
rss_links: 'RSS links' rss_links: 'RSS links'
rss_link: rss_link:
unread: 'unread' unread: 'Unread'
starred: 'starred' starred: 'Starred'
archive: 'archived' archive: 'Archived'
all: 'All'
rss_limit: 'Number of items in the feed' rss_limit: 'Number of items in the feed'
form_user: form_user:
two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connection." two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connection."

View file

@ -93,6 +93,7 @@ config:
unread: 'sin leer' unread: 'sin leer'
starred: 'favoritos' starred: 'favoritos'
archive: 'archivados' archive: 'archivados'
# all: 'All'
rss_limit: 'Límite de artículos en feed RSS' rss_limit: 'Límite de artículos en feed RSS'
form_user: form_user:
two_factor_description: "Con la autenticación en dos pasos recibirá código por e-mail en cada nueva conexión que no sea de confianza." two_factor_description: "Con la autenticación en dos pasos recibirá código por e-mail en cada nueva conexión que no sea de confianza."

View file

@ -93,6 +93,7 @@ config:
unread: 'خوانده‌نشده' unread: 'خوانده‌نشده'
starred: 'برگزیده' starred: 'برگزیده'
archive: 'بایگانی' archive: 'بایگانی'
# all: 'All'
rss_limit: 'محدودیت آر-اس-اس' rss_limit: 'محدودیت آر-اس-اس'
form_user: form_user:
two_factor_description: "با فعال‌کردن تأیید ۲مرحله‌ای هر بار که اتصال تأییدنشده‌ای برقرار شد، به شما یک کد از راه ایمیل فرستاده می‌شود" two_factor_description: "با فعال‌کردن تأیید ۲مرحله‌ای هر بار که اتصال تأییدنشده‌ای برقرار شد، به شما یک کد از راه ایمیل فرستاده می‌شود"

View file

@ -88,11 +88,12 @@ config:
no_token: "Aucun jeton généré" no_token: "Aucun jeton généré"
token_create: "Créez votre jeton" token_create: "Créez votre jeton"
token_reset: "Réinitialisez votre jeton" token_reset: "Réinitialisez votre jeton"
rss_links: "Adresse de vos flux RSS" rss_links: "Adresses de vos flux RSS"
rss_link: rss_link:
unread: "non lus" unread: "Non lus"
starred: "favoris" starred: "Favoris"
archive: "lus" archive: "Lus"
all: "Tous"
rss_limit: "Nombre darticles dans le flux" rss_limit: "Nombre darticles dans le flux"
form_user: form_user:
two_factor_description: "Activer lauthentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée." two_factor_description: "Activer lauthentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée."

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Rigenera il tuo token' token_reset: 'Rigenera il tuo token'
rss_links: 'Collegamenti RSS' rss_links: 'Collegamenti RSS'
rss_link: rss_link:
unread: 'non letti' unread: 'Non letti'
starred: 'preferiti' starred: 'Preferiti'
archive: 'archiviati' archive: 'Archiviati'
# all: 'All'
rss_limit: 'Numero di elementi nel feed' rss_limit: 'Numero di elementi nel feed'
form_user: form_user:
two_factor_description: "Abilitando l'\autenticazione a due fattori riceverai una e-mail con un codice per ogni nuova connesione non verificata" two_factor_description: "Abilitando l'\autenticazione a due fattori riceverai una e-mail con un codice per ogni nuova connesione non verificata"

View file

@ -88,11 +88,12 @@ config:
no_token: 'Pas cap de geton generat' no_token: 'Pas cap de geton generat'
token_create: 'Creatz vòstre geton' token_create: 'Creatz vòstre geton'
token_reset: 'Reïnicializatz vòstre geton' token_reset: 'Reïnicializatz vòstre geton'
rss_links: 'URL de vòstres fluxes RSS' rss_links: 'URLs de vòstres fluxes RSS'
rss_link: rss_link:
unread: 'pas legits' unread: 'Pas legits'
starred: 'favorits' starred: 'Favorits'
archive: 'legits' archive: 'Legits'
# all: 'All'
rss_limit: "Nombre d'articles dins un flux RSS" rss_limit: "Nombre d'articles dins un flux RSS"
form_user: form_user:
two_factor_description: "Activar l'autentificacion doble-factor vòl dire que recebretz un còdi per corrièl per cada novèla connexion pas aprovada." two_factor_description: "Activar l'autentificacion doble-factor vòl dire que recebretz un còdi per corrièl per cada novèla connexion pas aprovada."

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Zresetuj swojego tokena' token_reset: 'Zresetuj swojego tokena'
rss_links: 'RSS links' rss_links: 'RSS links'
rss_link: rss_link:
unread: 'nieprzeczytane' unread: 'Nieprzeczytane'
starred: 'oznaczone gwiazdką' starred: 'Oznaczone gwiazdką'
archive: 'archiwum' archive: 'Archiwum'
# all: 'All'
rss_limit: 'Link do RSS' rss_limit: 'Link do RSS'
form_user: form_user:
two_factor_description: "Włączenie autoryzacji dwuetapowej oznacza, że będziesz otrzymywał maile z kodem przy każdym nowym, niezaufanym połączeniu" two_factor_description: "Włączenie autoryzacji dwuetapowej oznacza, że będziesz otrzymywał maile z kodem przy każdym nowym, niezaufanym połączeniu"

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Gerar novamente seu token' token_reset: 'Gerar novamente seu token'
rss_links: 'Links RSS' rss_links: 'Links RSS'
rss_link: rss_link:
unread: 'não lido' unread: 'Não lido'
starred: 'destacado' starred: 'Destacado'
archive: 'arquivado' archive: 'Arquivado'
# all: 'All'
rss_limit: 'Número de itens no feed' rss_limit: 'Número de itens no feed'
form_user: form_user:
two_factor_description: 'Habilitar autenticação de dois passos significa que você receberá um e-mail com um código a cada nova conexão desconhecida.' two_factor_description: 'Habilitar autenticação de dois passos significa que você receberá um e-mail com um código a cada nova conexão desconhecida.'

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Resetează-ți token-ul' token_reset: 'Resetează-ți token-ul'
rss_links: 'Link-uri RSS' rss_links: 'Link-uri RSS'
rss_link: rss_link:
unread: 'unread' unread: 'Unread'
starred: 'starred' starred: 'Starred'
archive: 'archived' archive: 'Archived'
# all: 'All'
rss_limit: 'Limită RSS' rss_limit: 'Limită RSS'
form_user: form_user:
# two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion"

View file

@ -90,9 +90,10 @@ config:
token_reset: 'Belirteci (token) sıfırla' token_reset: 'Belirteci (token) sıfırla'
rss_links: 'RSS akış bağlantıları' rss_links: 'RSS akış bağlantıları'
rss_link: rss_link:
unread: 'okunmayan' unread: 'Okunmayan'
starred: 'favoriler' starred: 'Favoriler'
archive: 'arşiv' archive: 'Arşiv'
# all: 'All'
rss_limit: 'RSS içeriğinden talep edilecek makale limiti' rss_limit: 'RSS içeriğinden talep edilecek makale limiti'
form_user: form_user:
two_factor_description: "İki adımlı doğrulamayı aktifleştirdiğinizde, her yeni güvenilmeyen bağlantılarda size e-posta ile bir kod alacaksınız." two_factor_description: "İki adımlı doğrulamayı aktifleştirdiğinizde, her yeni güvenilmeyen bağlantılarda size e-posta ile bir kod alacaksınız."

View file

@ -82,7 +82,7 @@
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
<h3>{{ 'config.form_settings.android_configuration'|trans }}</h3> <h3>{{ 'config.form_settings.android_configuration'|trans }}</h3>
<a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" >Touch here to prefill your Android application</a> <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}">Touch here to prefill your Android application</a>
<br/> <br/>
<img id="androidQrcode" /> <img id="androidQrcode" />
<script> <script>
@ -106,7 +106,7 @@
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
<label>Rss token</label> <label>{{ 'config.form_rss.token_label'|trans }}</label>
{% if rss.token %} {% if rss.token %}
{{ rss.token }} {{ rss.token }}
{% else %} {% else %}
@ -128,9 +128,10 @@
<div class="row"> <div class="row">
<label>{{ 'config.form_rss.rss_links'|trans }}</label> <label>{{ 'config.form_rss.rss_links'|trans }}</label>
<ul> <ul>
<li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">unread</a></li> <li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.unread'|trans }}</a></li>
<li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">fav</a></li> <li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.starred'|trans }}</a></li>
<li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">archives</a></li> <li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.archive'|trans }}</a></li>
<li><a href="{{ path('all_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.all'|trans }}</a></li>
</ul> </ul>
</div> </div>
</fieldset> </fieldset>

View file

@ -1,5 +1,12 @@
{% extends "WallabagCoreBundle::layout.html.twig" %} {% extends "WallabagCoreBundle::layout.html.twig" %}
{% block head %}
{{ parent() }}
{% if tag is defined and app.user.config.rssToken %}
<link rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" />
{% endif %}
{% endblock %}
{% block title %} {% block title %}
{% set filter = '' %} {% set filter = '' %}
{% if tag is defined %} {% if tag is defined %}
@ -12,12 +19,15 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% set currentRoute = app.request.attributes.get('_route') %}
{% set listMode = app.user.config.listMode %} {% set listMode = app.user.config.listMode %}
<div class="results"> <div class="results">
<div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div> <div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div>
<div class="pagination"> <div class="pagination">
<a href="{{ path('switch_view_mode') }}"><i class="listMode-btn material-icons md-24">{% if listMode == 0 %}list{% else %}view_module{% endif %}</i></a> <a href="{{ path('switch_view_mode') }}"><i class="listMode-btn material-icons md-24">{% if listMode == 0 %}list{% else %}view_module{% endif %}</i></a>
{% if app.user.config.rssToken %}
{% include "@WallabagCore/themes/common/Entry/_rss_link.html.twig" %}
{% endif %}
<i class="btn-clickable download-btn material-icons md-24 js-export-action">file_download</i> <i class="btn-clickable download-btn material-icons md-24 js-export-action">file_download</i>
<i class="btn-clickable filter-btn material-icons md-24 js-filters-action">filter_list</i> <i class="btn-clickable filter-btn material-icons md-24 js-filters-action">filter_list</i>
{% if entries.getNbPages > 1 %} {% if entries.getNbPages > 1 %}
@ -76,7 +86,6 @@
<!-- Export --> <!-- Export -->
<aside id="download-form"> <aside id="download-form">
{% set currentRoute = app.request.attributes.get('_route') %}
{% set currentTag = '' %} {% set currentTag = '' %}
{% if tag is defined %} {% if tag is defined %}
{% set currentTag = tag %} {% set currentTag = tag %}

View file

@ -9,7 +9,12 @@
<ul> <ul>
{% for tag in tags %} {% for tag in tags %}
<li id="tag-{{ tag.id|e }}"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a></li> <li id="tag-{{ tag.id|e }}">
<a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a>
<a rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" class="right">
<i class="material-icons md-24">rss_feed</i>
</a>
</li>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -0,0 +1,6 @@
{% if tag is defined %}
<a rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" class="right"><i class="material-icons md-24">rss_feed</i></a>
{% elseif currentRoute in ['unread', 'starred', 'archive', 'all'] %}
<a rel="alternate" type="application/rss+xml" href="{{ path(currentRoute ~ '_rss', {'username': app.user.username, 'token': app.user.config.rssToken}) }}" class="right"><i class="material-icons">rss_feed</i></a>
{% endif %}

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/"> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
<channel> <channel>
<title>wallabag {{type}} feed</title> <title>wallabag - {{ type }} feed</title>
<link>{{ url(type) }}</link> <link>{{ url_html }}</link>
<link rel="self" href="{{ app.request.uri }}"/> <link rel="self" href="{{ app.request.uri }}"/>
{% if entries.hasPreviousPage -%} {% if entries.hasPreviousPage -%}
<link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/> <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/>
@ -13,7 +13,7 @@
<link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/> <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/>
<pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate>
<generator>wallabag</generator> <generator>wallabag</generator>
<description>wallabag {{type}} elements</description> <description>wallabag {{ type }} elements</description>
{% for entry in entries %} {% for entry in entries %}

View file

@ -157,6 +157,7 @@
<li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.unread'|trans }}</a></li> <li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.unread'|trans }}</a></li>
<li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.starred'|trans }}</a></li> <li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.starred'|trans }}</a></li>
<li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.archive'|trans }}</a></li> <li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.archive'|trans }}</a></li>
<li><a href="{{ path('all_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.all'|trans }}</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -1,9 +1,16 @@
{% extends "WallabagCoreBundle::layout.html.twig" %} {% extends "WallabagCoreBundle::layout.html.twig" %}
{% block head %}
{{ parent() }}
{% if tag is defined and app.user.config.rssToken %}
<link rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" />
{% endif %}
{% endblock %}
{% block title %} {% block title %}
{% set filter = '' %} {% set filter = '' %}
{% if tag is defined %} {% if tag is defined %}
{% set filter = tag %} {% set filter = tag.slug %}
{% endif %} {% endif %}
{% if searchTerm is defined and searchTerm is not empty %} {% if searchTerm is defined and searchTerm is not empty %}
{% set filter = searchTerm %} {% set filter = searchTerm %}
@ -13,10 +20,14 @@
{% block content %} {% block content %}
{% set listMode = app.user.config.listMode %} {% set listMode = app.user.config.listMode %}
{% set currentRoute = app.request.attributes.get('_route') %}
<div class="results clearfix"> <div class="results clearfix">
<div class="nb-results left"> <div class="nb-results left">
{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }} {{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}
<a href="{{ path('switch_view_mode') }}"><i class="material-icons">{% if listMode == 0 %}view_list{% else %}view_module{% endif %}</i></a> <a href="{{ path('switch_view_mode') }}"><i class="material-icons">{% if listMode == 0 %}view_list{% else %}view_module{% endif %}</i></a>
{% if app.user.config.rssToken %}
{% include "@WallabagCore/themes/common/Entry/_rss_link.html.twig" %}
{% endif %}
</div> </div>
{% if entries.getNbPages > 1 %} {% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
@ -46,10 +57,9 @@
<!-- Export --> <!-- Export -->
<div id="export" class="side-nav right-aligned"> <div id="export" class="side-nav right-aligned">
{% set currentRoute = app.request.attributes.get('_route') %}
{% set currentTag = '' %} {% set currentTag = '' %}
{% if tag is defined %} {% if tag is defined %}
{% set currentTag = tag %} {% set currentTag = tag.slug %}
{% endif %} {% endif %}
{% if currentRoute == 'homepage' %} {% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %} {% set currentRoute = 'unread' %}

View file

@ -14,6 +14,9 @@
{% for tag in tags %} {% for tag in tags %}
<li title="{{tag.label}} ({{ tag.nbEntries }})" id="tag-{{ tag.id }}" class="col l2 m2 s5"> <li title="{{tag.label}} ({{ tag.nbEntries }})" id="tag-{{ tag.id }}" class="col l2 m2 s5">
<a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a> <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a>
{% if app.user.config.rssToken %}
<a rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" class="right"><i class="material-icons">rss_feed</i></a>
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -6,7 +6,7 @@ use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
class RssControllerTest extends WallabagCoreTestCase class RssControllerTest extends WallabagCoreTestCase
{ {
public function validateDom($xml, $type, $nb = null) public function validateDom($xml, $type, $urlPagination, $nb = null)
{ {
$doc = new \DOMDocument(); $doc = new \DOMDocument();
$doc->loadXML($xml); $doc->loadXML($xml);
@ -23,7 +23,7 @@ class RssControllerTest extends WallabagCoreTestCase
$this->assertEquals(1, $xpath->query('/rss/channel')->length); $this->assertEquals(1, $xpath->query('/rss/channel')->length);
$this->assertEquals(1, $xpath->query('/rss/channel/title')->length); $this->assertEquals(1, $xpath->query('/rss/channel/title')->length);
$this->assertEquals('wallabag '.$type.' feed', $xpath->query('/rss/channel/title')->item(0)->nodeValue); $this->assertEquals('wallabag - '.$type.' feed', $xpath->query('/rss/channel/title')->item(0)->nodeValue);
$this->assertEquals(1, $xpath->query('/rss/channel/pubDate')->length); $this->assertEquals(1, $xpath->query('/rss/channel/pubDate')->length);
@ -34,10 +34,10 @@ class RssControllerTest extends WallabagCoreTestCase
$this->assertEquals('wallabag '.$type.' elements', $xpath->query('/rss/channel/description')->item(0)->nodeValue); $this->assertEquals('wallabag '.$type.' elements', $xpath->query('/rss/channel/description')->item(0)->nodeValue);
$this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="self"]')->length); $this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="self"]')->length);
$this->assertContains($type.'.xml', $xpath->query('/rss/channel/link[@rel="self"]')->item(0)->getAttribute('href')); $this->assertContains($urlPagination.'.xml', $xpath->query('/rss/channel/link[@rel="self"]')->item(0)->getAttribute('href'));
$this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="last"]')->length); $this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="last"]')->length);
$this->assertContains($type.'.xml?page=', $xpath->query('/rss/channel/link[@rel="last"]')->item(0)->getAttribute('href')); $this->assertContains($urlPagination.'.xml?page=', $xpath->query('/rss/channel/link[@rel="last"]')->item(0)->getAttribute('href'));
foreach ($xpath->query('//item') as $item) { foreach ($xpath->query('//item') as $item) {
$this->assertEquals(1, $xpath->query('title', $item)->length); $this->assertEquals(1, $xpath->query('title', $item)->length);
@ -94,7 +94,7 @@ class RssControllerTest extends WallabagCoreTestCase
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent(), 'unread', 2); $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread', 2);
} }
public function testStarred() public function testStarred()
@ -116,7 +116,7 @@ class RssControllerTest extends WallabagCoreTestCase
$this->assertEquals(200, $client->getResponse()->getStatusCode(), 1); $this->assertEquals(200, $client->getResponse()->getStatusCode(), 1);
$this->validateDom($client->getResponse()->getContent(), 'starred'); $this->validateDom($client->getResponse()->getContent(), 'starred', 'starred');
} }
public function testArchives() public function testArchives()
@ -138,7 +138,7 @@ class RssControllerTest extends WallabagCoreTestCase
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent(), 'archive'); $this->validateDom($client->getResponse()->getContent(), 'archive', 'archive');
} }
public function testPagination() public function testPagination()
@ -159,13 +159,38 @@ class RssControllerTest extends WallabagCoreTestCase
$client->request('GET', '/admin/SUPERTOKEN/unread.xml'); $client->request('GET', '/admin/SUPERTOKEN/unread.xml');
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent(), 'unread'); $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread');
$client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=2'); $client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=2');
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent(), 'unread'); $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread');
$client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=3000'); $client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=3000');
$this->assertEquals(302, $client->getResponse()->getStatusCode()); $this->assertEquals(302, $client->getResponse()->getStatusCode());
} }
public function testTags()
{
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
->getRepository('WallabagUserBundle:User')
->findOneByUsername('admin');
$config = $user->getConfig();
$config->setRssToken('SUPERTOKEN');
$config->setRssLimit(null);
$em->persist($config);
$em->flush();
$client = $this->getClient();
$client->request('GET', '/admin/SUPERTOKEN/tags/foo-bar.xml');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent(), 'tag (foo bar)', 'tags/foo-bar');
$client->request('GET', '/admin/SUPERTOKEN/tags/foo-bar.xml?page=3000');
$this->assertEquals(302, $client->getResponse()->getStatusCode());
}
} }