Merge pull request #1166 from wallabag/v2-rss

Add RSS feeds
This commit is contained in:
Nicolas Lœuillet 2015-04-01 21:08:56 +02:00
commit 1a93ee423b
27 changed files with 1074 additions and 88 deletions

View File

@ -16,7 +16,8 @@ tools:
php_code_sniffer: true php_code_sniffer: true
php_pdepend: true php_pdepend: true
sensiolabs_security_checker: true sensiolabs_security_checker: true
external_code_coverage: true external_code_coverage:
timeout: 1800
php_code_coverage: true php_code_coverage: true
php_sim: false php_sim: false
php_cpd: false php_cpd: false

View File

@ -31,11 +31,11 @@ install:
# build coverage only on one build, to speed up results feedbacks # build coverage only on one build, to speed up results feedbacks
before_script: before_script:
- if [[ "$TRAVIS_PHP_VERSION" = "5.6" ]]; then PHPUNIT_FLAGS="--coverage-clover=coverage.clover"; fi; - if [[ "$TRAVIS_PHP_VERSION" = "5.6" ]]; then PHPUNIT_FLAGS="--coverage-clover=coverage.clover"; else PHPUNIT_FLAGS=""; fi;
script: script:
- ant prepare - ant prepare
- phpunit $PHPUNIT_FLAGS - phpunit --exclude-group command-doctrine $PHPUNIT_FLAGS
after_script: after_script:
- | - |

View File

@ -42,3 +42,4 @@ parameters:
theme: baggy theme: baggy
language: en_US language: en_US
from_email: no-reply@wallabag.org from_email: no-reply@wallabag.org
rss_limit: 50

View File

@ -60,4 +60,5 @@ security:
- { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/forgot-password, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/forgot-password, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER } - { path: ^/, roles: ROLE_USER }

View File

@ -198,6 +198,7 @@ class InstallCommand extends ContainerAwareCommand
$config = new Config($user); $config = new Config($user);
$config->setTheme($this->getContainer()->getParameter('theme')); $config->setTheme($this->getContainer()->getParameter('theme'));
$config->setItemsPerPage($this->getContainer()->getParameter('items_on_page')); $config->setItemsPerPage($this->getContainer()->getParameter('items_on_page'));
$config->setRssLimit($this->getContainer()->getParameter('rss_limit'));
$config->setLanguage($this->getContainer()->getParameter('language')); $config->setLanguage($this->getContainer()->getParameter('language'));
$em->persist($config); $em->persist($config);

View File

@ -5,11 +5,14 @@ namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Wallabag\CoreBundle\Entity\Config; use Wallabag\CoreBundle\Entity\Config;
use Wallabag\CoreBundle\Entity\User; use Wallabag\CoreBundle\Entity\User;
use Wallabag\CoreBundle\Form\Type\ChangePasswordType; use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
use Wallabag\CoreBundle\Form\Type\UserType; use Wallabag\CoreBundle\Form\Type\UserType;
use Wallabag\CoreBundle\Form\Type\NewUserType; use Wallabag\CoreBundle\Form\Type\NewUserType;
use Wallabag\CoreBundle\Form\Type\RssType;
use Wallabag\CoreBundle\Tools\Utils;
class ConfigController extends Controller class ConfigController extends Controller
{ {
@ -77,6 +80,22 @@ class ConfigController extends Controller
return $this->redirect($this->generateUrl('config')); return $this->redirect($this->generateUrl('config'));
} }
// handle rss information
$rssForm = $this->createForm(new RssType(), $config);
$rssForm->handleRequest($request);
if ($rssForm->isValid()) {
$em->persist($config);
$em->flush();
$this->get('session')->getFlashBag()->add(
'notice',
'RSS information updated'
);
return $this->redirect($this->generateUrl('config'));
}
// handle adding new user // handle adding new user
$newUser = new User(); $newUser = new User();
$newUserForm = $this->createForm(new NewUserType(), $newUser); $newUserForm = $this->createForm(new NewUserType(), $newUser);
@ -88,6 +107,7 @@ class ConfigController extends Controller
$config = new Config($newUser); $config = new Config($newUser);
$config->setTheme($this->container->getParameter('theme')); $config->setTheme($this->container->getParameter('theme'));
$config->setItemsPerPage($this->container->getParameter('items_on_page')); $config->setItemsPerPage($this->container->getParameter('items_on_page'));
$config->setRssLimit($this->container->getParameter('rss_limit'));
$config->setLanguage($this->container->getParameter('language')); $config->setLanguage($this->container->getParameter('language'));
$em->persist($config); $em->persist($config);
@ -103,13 +123,43 @@ class ConfigController extends Controller
} }
return $this->render('WallabagCoreBundle:Config:index.html.twig', array( return $this->render('WallabagCoreBundle:Config:index.html.twig', array(
'configForm' => $configForm->createView(), 'form' => array(
'pwdForm' => $pwdForm->createView(), 'config' => $configForm->createView(),
'userForm' => $userForm->createView(), 'rss' => $rssForm->createView(),
'newUserForm' => $newUserForm->createView(), 'pwd' => $pwdForm->createView(),
'user' => $userForm->createView(),
'new_user' => $newUserForm->createView(),
),
'rss' => array(
'username' => $user->getUsername(),
'token' => $config->getRssToken(),
)
)); ));
} }
/**
* @param Request $request
*
* @Route("/generate-token", name="generate_token")
*
* @return JsonResponse
*/
public function generateTokenAction(Request $request)
{
$config = $this->getConfig();
$config->setRssToken(Utils::generateToken());
$em = $this->getDoctrine()->getManager();
$em->persist($config);
$em->flush();
if ($request->isXmlHttpRequest()) {
return new JsonResponse(array('token' => $config->getRssToken()));
}
return $request->headers->get('referer') ? $this->redirect($request->headers->get('referer')) : $this->redirectToRoute('config');
}
/** /**
* Retrieve config for the current user. * Retrieve config for the current user.
* If no config were found, create a new one. * If no config were found, create a new one.

View File

@ -0,0 +1,84 @@
<?php
namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Wallabag\CoreBundle\Entity\User;
use Wallabag\CoreBundle\Entity\Entry;
class RssController extends Controller
{
/**
* Shows unread entries for current user
*
* @Route("/{username}/{token}/unread.xml", name="unread_rss", defaults={"_format"="xml"})
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showUnreadAction(User $user)
{
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findUnreadByUser(
$user->getId(),
0,
$user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit')
);
return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', array(
'type' => 'unread',
'entries' => $entries,
));
}
/**
* Shows read entries for current user
*
* @Route("/{username}/{token}/archive.xml", name="archive_rss")
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showArchiveAction(User $user)
{
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findArchiveByUser(
$user->getId(),
0,
$user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit')
);
return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', array(
'type' => 'archive',
'entries' => $entries,
));
}
/**
* Shows starred entries for current user
*
* @Route("/{username}/{token}/starred.xml", name="starred_rss")
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showStarredAction(User $user)
{
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findStarredByUser(
$user->getId(),
0,
$user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit')
);
return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', array(
'type' => 'starred',
'entries' => $entries,
));
}
}

View File

@ -67,6 +67,26 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
$this->addReference('entry4', $entry4); $this->addReference('entry4', $entry4);
$entry5 = new Entry($this->getReference('admin-user'));
$entry5->setUrl('http://0.0.0.0');
$entry5->setTitle('test title entry5');
$entry5->setContent('This is my content /o/');
$entry5->setStarred(true);
$manager->persist($entry5);
$this->addReference('entry5', $entry5);
$entry6 = new Entry($this->getReference('admin-user'));
$entry6->setUrl('http://0.0.0.0');
$entry6->setTitle('test title entry6');
$entry6->setContent('This is my content /o/');
$entry6->setArchived(true);
$manager->persist($entry6);
$this->addReference('entry6', $entry6);
$manager->flush(); $manager->flush();
} }

View File

@ -32,12 +32,17 @@ class Config
private $theme; private $theme;
/** /**
* @var string * @var integer
* *
* @Assert\NotBlank() * @Assert\NotBlank()
* @Assert\Range(
* min = 1,
* max = 100000,
* maxMessage = "This will certainly kill the app"
* )
* @ORM\Column(name="items_per_page", type="integer", nullable=false) * @ORM\Column(name="items_per_page", type="integer", nullable=false)
*/ */
private $items_per_page; private $itemsPerPage;
/** /**
* @var string * @var string
@ -47,6 +52,25 @@ class Config
*/ */
private $language; private $language;
/**
* @var string
*
* @ORM\Column(name="rss_token", type="string", nullable=true)
*/
private $rssToken;
/**
* @var integer
*
* @ORM\Column(name="rss_limit", type="integer", nullable=true)
* @Assert\Range(
* min = 1,
* max = 100000,
* maxMessage = "This will certainly kill the app"
* )
*/
private $rssLimit;
/** /**
* @ORM\OneToOne(targetEntity="User", inversedBy="config") * @ORM\OneToOne(targetEntity="User", inversedBy="config")
*/ */
@ -58,8 +82,6 @@ class Config
public function __construct(User $user) public function __construct(User $user)
{ {
$this->user = $user; $this->user = $user;
$this->items_per_page = 12;
$this->language = 'en_US';
} }
/** /**
@ -96,26 +118,26 @@ class Config
} }
/** /**
* Set items_per_page * Set itemsPerPage
* *
* @param integer $itemsPerPage * @param integer $itemsPerPage
* @return Config * @return Config
*/ */
public function setItemsPerPage($itemsPerPage) public function setItemsPerPage($itemsPerPage)
{ {
$this->items_per_page = $itemsPerPage; $this->itemsPerPage = $itemsPerPage;
return $this; return $this;
} }
/** /**
* Get items_per_page * Get itemsPerPage
* *
* @return integer * @return integer
*/ */
public function getItemsPerPage() public function getItemsPerPage()
{ {
return $this->items_per_page; return $this->itemsPerPage;
} }
/** /**
@ -163,4 +185,50 @@ class Config
{ {
return $this->user; return $this->user;
} }
/**
* Set rssToken
*
* @param string $rssToken
* @return Config
*/
public function setRssToken($rssToken)
{
$this->rssToken = $rssToken;
return $this;
}
/**
* Get rssToken
*
* @return string
*/
public function getRssToken()
{
return $this->rssToken;
}
/**
* Set rssLimit
*
* @param string $rssLimit
* @return Config
*/
public function setRssLimit($rssLimit)
{
$this->rssLimit = $rssLimit;
return $this;
}
/**
* Get rssLimit
*
* @return string
*/
public function getRssLimit()
{
return $this->rssLimit;
}
} }

View File

@ -14,7 +14,7 @@ use JMS\Serializer\Annotation\Expose;
* User * User
* *
* @ORM\Table(name="user") * @ORM\Table(name="user")
* @ORM\Entity * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\UserRepository")
* @ORM\HasLifecycleCallbacks() * @ORM\HasLifecycleCallbacks()
* @ExclusionPolicy("all") * @ExclusionPolicy("all")
*/ */

View File

@ -24,7 +24,7 @@ class ConfigType extends AbstractType
{ {
$builder $builder
->add('theme', 'choice', array('choices' => $this->themes)) ->add('theme', 'choice', array('choices' => $this->themes))
->add('items_per_page', 'text') ->add('items_per_page')
->add('language') ->add('language')
->add('save', 'submit') ->add('save', 'submit')
; ;

View File

@ -0,0 +1,29 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class RssType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('rss_limit')
->add('save', 'submit')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Wallabag\CoreBundle\Entity\Config',
));
}
public function getName()
{
return 'rss_config';
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace Wallabag\CoreBundle\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Wallabag\CoreBundle\Entity\User;
/**
* ParamConverter used in the RSS controller to retrieve the right user according to
* username & token given in the url.
*
* @see http://stfalcon.com/en/blog/post/symfony2-custom-paramconverter
*/
class UsernameRssTokenConverter implements ParamConverterInterface
{
private $registry;
/**
* @param ManagerRegistry $registry Manager registry
*/
public function __construct(ManagerRegistry $registry = null)
{
$this->registry = $registry;
}
/**
* {@inheritdoc}
*
* Check, if object supported by our converter
*/
public function supports(ParamConverter $configuration)
{
// If there is no manager, this means that only Doctrine DBAL is configured
// In this case we can do nothing and just return
if (null === $this->registry || !count($this->registry->getManagers())) {
return false;
}
// Check, if option class was set in configuration
if (null === $configuration->getClass()) {
return false;
}
// Get actual entity manager for class
$em = $this->registry->getManagerForClass($configuration->getClass());
// Check, if class name is what we need
if ('Wallabag\CoreBundle\Entity\User' !== $em->getClassMetadata($configuration->getClass())->getName()) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*
* Applies converting
*
* @throws \InvalidArgumentException When route attributes are missing
* @throws NotFoundHttpException When object not found
*/
public function apply(Request $request, ParamConverter $configuration)
{
$username = $request->attributes->get('username');
$rssToken = $request->attributes->get('token');
// Check, if route attributes exists
if (null === $username || null === $rssToken) {
throw new \InvalidArgumentException('Route attribute is missing');
}
// Get actual entity manager for class
$em = $this->registry->getManagerForClass($configuration->getClass());
$userRepository = $em->getRepository($configuration->getClass());
// Try to find user by its username and config rss_token
$user = $userRepository->findOneByUsernameAndRsstoken($username, $rssToken);
if (null === $user || !($user instanceof User)) {
throw new NotFoundHttpException(sprintf('%s not found.', $configuration->getClass()));
}
// Map found user to the route's parameter
$request->attributes->set($configuration->getName(), $user);
}
}

View File

@ -26,7 +26,7 @@ class EntryRepository extends EntityRepository
->leftJoin('e.user', 'u') ->leftJoin('e.user', 'u')
->where('e.isArchived = false') ->where('e.isArchived = false')
->andWhere('u.id =:userId')->setParameter('userId', $userId) ->andWhere('u.id =:userId')->setParameter('userId', $userId)
->orderBy('e.createdAt', 'desc') ->orderBy('e.id', 'desc')
->getQuery(); ->getQuery();
$paginator = new Paginator($qb); $paginator = new Paginator($qb);
@ -52,7 +52,7 @@ class EntryRepository extends EntityRepository
->leftJoin('e.user', 'u') ->leftJoin('e.user', 'u')
->where('e.isArchived = true') ->where('e.isArchived = true')
->andWhere('u.id =:userId')->setParameter('userId', $userId) ->andWhere('u.id =:userId')->setParameter('userId', $userId)
->orderBy('e.createdAt', 'desc') ->orderBy('e.id', 'desc')
->getQuery(); ->getQuery();
$paginator = new Paginator($qb); $paginator = new Paginator($qb);
@ -78,7 +78,7 @@ class EntryRepository extends EntityRepository
->leftJoin('e.user', 'u') ->leftJoin('e.user', 'u')
->where('e.isStarred = true') ->where('e.isStarred = true')
->andWhere('u.id =:userId')->setParameter('userId', $userId) ->andWhere('u.id =:userId')->setParameter('userId', $userId)
->orderBy('e.createdAt', 'desc') ->orderBy('e.id', 'desc')
->getQuery(); ->getQuery();
$paginator = new Paginator($qb); $paginator = new Paginator($qb);
@ -111,7 +111,7 @@ class EntryRepository extends EntityRepository
} }
if ('created' === $sort) { if ('created' === $sort) {
$qb->orderBy('e.createdAt', $order); $qb->orderBy('e.id', $order);
} elseif ('updated' === $sort) { } elseif ('updated' === $sort) {
$qb->orderBy('e.updatedAt', $order); $qb->orderBy('e.updatedAt', $order);
} }

View File

@ -0,0 +1,26 @@
<?php
namespace Wallabag\CoreBundle\Repository;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
/**
* Find a user by its username and rss roken
*
* @param string $username
* @param string $rssToken
*
* @return User|null
*/
public function findOneByUsernameAndRsstoken($username, $rssToken)
{
return $this->createQueryBuilder('u')
->leftJoin('u.config', 'c')
->where('c.rssToken = :rss_token')->setParameter('rss_token', $rssToken)
->andWhere('u.username = :username')->setParameter('username', $username)
->getQuery()
->getOneOrNullResult();
}
}

View File

@ -36,3 +36,10 @@ services:
- @doctrine - @doctrine
tags: tags:
- { name: form.type, alias: forgot_password } - { name: form.type, alias: forgot_password }
wallabag_core.param_converter.username_rsstoken_converter:
class: Wallabag\CoreBundle\ParamConverter\UsernameRssTokenConverter
tags:
- { name: request.param_converter, converter: username_rsstoken_converter }
arguments:
- @doctrine

View File

@ -5,129 +5,173 @@
{% block content %} {% block content %}
<h2>{% trans %}Wallabag configuration{% endtrans %}</h2> <h2>{% trans %}Wallabag configuration{% endtrans %}</h2>
<form action="{{ path('config') }}" method="post" {{ form_enctype(configForm) }}> <form action="{{ path('config') }}" method="post" {{ form_enctype(form.config) }}>
{{ form_errors(configForm) }} {{ form_errors(form.config) }}
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(configForm.theme) }} {{ form_label(form.config.theme) }}
{{ form_errors(configForm.theme) }} {{ form_errors(form.config.theme) }}
{{ form_widget(configForm.theme) }} {{ form_widget(form.config.theme) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(configForm.items_per_page) }} {{ form_label(form.config.items_per_page) }}
{{ form_errors(configForm.items_per_page) }} {{ form_errors(form.config.items_per_page) }}
{{ form_widget(configForm.items_per_page) }} {{ form_widget(form.config.items_per_page) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(configForm.language) }} {{ form_label(form.config.language) }}
{{ form_errors(configForm.language) }} {{ form_errors(form.config.language) }}
{{ form_widget(configForm.language) }} {{ form_widget(form.config.language) }}
</div> </div>
</fieldset> </fieldset>
{{ form_rest(configForm) }} {{ form_rest(form.config) }}
</form>
<h2>{% trans %}RSS configuration{% endtrans %}</h2>
<form action="{{ path('config') }}" method="post" {{ form_enctype(form.rss) }}>
{{ form_errors(form.rss) }}
<fieldset class="w500p inline">
<div class="row">
<label>Rss token</label>
{% if rss.token %}
{{ rss.token }}
{% else %}
<em>No token</em>
{% endif %}
<a href="{{ path('generate_token') }}">Regenerate ?</a>
</div>
</fieldset>
<fieldset class="w500p inline">
<div class="row">
<label>Rss links:</label>
{% if rss.token %}
<ul>
<li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">unread</a></li>
<li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">fav</a></li>
<li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">archives</a></li>
</ul>
{% else %}
<strong>You need to generate a token first.</strong>
{% endif %}
</div>
</fieldset>
<fieldset class="w500p inline">
<div class="row">
{{ form_label(form.rss.rss_limit) }}
{{ form_errors(form.rss.rss_limit) }}
{{ form_widget(form.rss.rss_limit) }}
</div>
</fieldset>
{{ form_rest(form.rss) }}
</form> </form>
<h2>{% trans %}User information{% endtrans %}</h2> <h2>{% trans %}User information{% endtrans %}</h2>
<form action="{{ path('config') }}" method="post" {{ form_enctype(userForm) }}> <form action="{{ path('config') }}" method="post" {{ form_enctype(form.user) }}>
{{ form_errors(userForm) }} {{ form_errors(form.user) }}
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(userForm.username) }} {{ form_label(form.user.username) }}
{{ form_errors(userForm.username) }} {{ form_errors(form.user.username) }}
{{ form_widget(userForm.username) }} {{ form_widget(form.user.username) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(userForm.name) }} {{ form_label(form.user.name) }}
{{ form_errors(userForm.name) }} {{ form_errors(form.user.name) }}
{{ form_widget(userForm.name) }} {{ form_widget(form.user.name) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(userForm.email) }} {{ form_label(form.user.email) }}
{{ form_errors(userForm.email) }} {{ form_errors(form.user.email) }}
{{ form_widget(userForm.email) }} {{ form_widget(form.user.email) }}
</div> </div>
</fieldset> </fieldset>
{{ form_rest(userForm) }} {{ form_rest(form.user) }}
</form> </form>
<h2>{% trans %}Change your password{% endtrans %}</h2> <h2>{% trans %}Change your password{% endtrans %}</h2>
<form action="{{ path('config') }}" method="post" {{ form_enctype(pwdForm) }}> <form action="{{ path('config') }}" method="post" {{ form_enctype(form.pwd) }}>
{{ form_errors(pwdForm) }} {{ form_errors(form.pwd) }}
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(pwdForm.old_password) }} {{ form_label(form.pwd.old_password) }}
{{ form_errors(pwdForm.old_password) }} {{ form_errors(form.pwd.old_password) }}
{{ form_widget(pwdForm.old_password) }} {{ form_widget(form.pwd.old_password) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(pwdForm.new_password.first) }} {{ form_label(form.pwd.new_password.first) }}
{{ form_errors(pwdForm.new_password.first) }} {{ form_errors(form.pwd.new_password.first) }}
{{ form_widget(pwdForm.new_password.first) }} {{ form_widget(form.pwd.new_password.first) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(pwdForm.new_password.second) }} {{ form_label(form.pwd.new_password.second) }}
{{ form_errors(pwdForm.new_password.second) }} {{ form_errors(form.pwd.new_password.second) }}
{{ form_widget(pwdForm.new_password.second) }} {{ form_widget(form.pwd.new_password.second) }}
</div> </div>
</fieldset> </fieldset>
{{ form_rest(pwdForm) }} {{ form_rest(form.pwd) }}
</form> </form>
<h2>{% trans %}Add a user{% endtrans %}</h2> <h2>{% trans %}Add a user{% endtrans %}</h2>
<form action="{{ path('config') }}" method="post" {{ form_enctype(newUserForm) }}> <form action="{{ path('config') }}" method="post" {{ form_enctype(form.new_user) }}>
{{ form_errors(newUserForm) }} {{ form_errors(form.new_user) }}
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(newUserForm.username) }} {{ form_label(form.new_user.username) }}
{{ form_errors(newUserForm.username) }} {{ form_errors(form.new_user.username) }}
{{ form_widget(newUserForm.username) }} {{ form_widget(form.new_user.username) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(newUserForm.password) }} {{ form_label(form.new_user.password) }}
{{ form_errors(newUserForm.password) }} {{ form_errors(form.new_user.password) }}
{{ form_widget(newUserForm.password) }} {{ form_widget(form.new_user.password) }}
</div> </div>
</fieldset> </fieldset>
<fieldset class="w500p inline"> <fieldset class="w500p inline">
<div class="row"> <div class="row">
{{ form_label(newUserForm.email) }} {{ form_label(form.new_user.email) }}
{{ form_errors(newUserForm.email) }} {{ form_errors(form.new_user.email) }}
{{ form_widget(newUserForm.email) }} {{ form_widget(form.new_user.email) }}
</div> </div>
</fieldset> </fieldset>
{{ form_rest(newUserForm) }} {{ form_rest(form.new_user) }}
</form> </form>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,34 @@
<?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/">
<channel>
<title>wallabag — {{type}} feed</title>
<link>{{ url('unread') }}</link>
<pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate>
<generator>wallabag</generator>
<description>wallabag {{type}} elements</description>
{% for entry in entries %}
<item>
<title><![CDATA[{{ entry.title }}]]></title>
<source url="{{ url('view', { 'id': entry.id }) }}">wallabag</source>
<link>{{ url('view', { 'id': entry.id }) }}</link>
<guid>{{ url('view', { 'id': entry.id }) }}</guid>
<pubDate>{{ entry.createdAt|date('D, d M Y H:i:s') }}</pubDate>
<description>
<![CDATA[
{%- if entry.content|readingTime > 0 -%}
{% trans %}estimated reading time :{% endtrans %} {{ entry.content|readingTime }} min
{%- else -%}
{% trans %}estimated reading time :{% endtrans %} &lt; 1 min
{%- endif %}
{{ entry.content|raw -}}
]]>
</description>
</item>
{% endfor %}
</channel>
</rss>

View File

@ -89,7 +89,7 @@ form fieldset {
margin: 0; margin: 0;
} }
form input[type="text"], select, form input[type="password"], form input[type="url"], form input[type="email"] { form input[type="text"], form input[type="number"], select, form input[type="password"], form input[type="url"], form input[type="email"] {
border: 1px solid #999; border: 1px solid #999;
padding: 0.5em 1em; padding: 0.5em 1em;
min-width: 12em; min-width: 12em;

View File

@ -4,6 +4,7 @@ namespace Wallabag\CoreBundle\Tests\Command;
use Wallabag\CoreBundle\Tests\WallabagTestCase; use Wallabag\CoreBundle\Tests\WallabagTestCase;
use Wallabag\CoreBundle\Command\InstallCommand; use Wallabag\CoreBundle\Command\InstallCommand;
use Wallabag\CoreBundle\Tests\Mock\InstallCommandMock;
use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\ArrayInput;
@ -30,7 +31,7 @@ class InstallCommandTest extends WallabagTestCase
$this->container = static::$kernel->getContainer(); $this->container = static::$kernel->getContainer();
$application = new Application(static::$kernel); $application = new Application(static::$kernel);
$application->add(new InstallCommand()); $application->add(new InstallCommandMock());
$command = $application->find('wallabag:install'); $command = $application->find('wallabag:install');
@ -64,7 +65,7 @@ class InstallCommandTest extends WallabagTestCase
$this->container = static::$kernel->getContainer(); $this->container = static::$kernel->getContainer();
$application = new Application(static::$kernel); $application = new Application(static::$kernel);
$application->add(new InstallCommand()); $application->add(new InstallCommandMock());
$command = $application->find('wallabag:install'); $command = $application->find('wallabag:install');
@ -97,6 +98,9 @@ class InstallCommandTest extends WallabagTestCase
$this->assertContains('Droping database, creating database and schema', $tester->getDisplay()); $this->assertContains('Droping database, creating database and schema', $tester->getDisplay());
} }
/**
* @group command-doctrine
*/
public function testRunInstallCommandWithDatabaseRemoved() public function testRunInstallCommandWithDatabaseRemoved()
{ {
$this->container = static::$kernel->getContainer(); $this->container = static::$kernel->getContainer();
@ -148,7 +152,7 @@ class InstallCommandTest extends WallabagTestCase
$this->container = static::$kernel->getContainer(); $this->container = static::$kernel->getContainer();
$application = new Application(static::$kernel); $application = new Application(static::$kernel);
$application->add(new InstallCommand()); $application->add(new InstallCommandMock());
$command = $application->find('wallabag:install'); $command = $application->find('wallabag:install');
@ -181,6 +185,9 @@ class InstallCommandTest extends WallabagTestCase
$this->assertContains('Droping schema and creating schema', $tester->getDisplay()); $this->assertContains('Droping schema and creating schema', $tester->getDisplay());
} }
/**
* @group command-doctrine
*/
public function testRunInstallCommandChooseNothing() public function testRunInstallCommandChooseNothing()
{ {
$this->container = static::$kernel->getContainer(); $this->container = static::$kernel->getContainer();
@ -242,7 +249,7 @@ class InstallCommandTest extends WallabagTestCase
$this->container = static::$kernel->getContainer(); $this->container = static::$kernel->getContainer();
$application = new Application(static::$kernel); $application = new Application(static::$kernel);
$application->add(new InstallCommand()); $application->add(new InstallCommandMock());
$command = $application->find('wallabag:install'); $command = $application->find('wallabag:install');

View File

@ -28,6 +28,8 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertCount(1, $crawler->filter('button[id=config_save]')); $this->assertCount(1, $crawler->filter('button[id=config_save]'));
$this->assertCount(1, $crawler->filter('button[id=change_passwd_save]')); $this->assertCount(1, $crawler->filter('button[id=change_passwd_save]'));
$this->assertCount(1, $crawler->filter('button[id=user_save]')); $this->assertCount(1, $crawler->filter('button[id=user_save]'));
$this->assertCount(1, $crawler->filter('button[id=new_user_save]'));
$this->assertCount(1, $crawler->filter('button[id=rss_config_save]'));
} }
public function testUpdate() public function testUpdate()
@ -347,4 +349,128 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text'))); $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text')));
$this->assertContains('User "wallace" added', $alert[0]); $this->assertContains('User "wallace" added', $alert[0]);
} }
public function testRssUpdateResetToken()
{
$this->logInAs('admin');
$client = $this->getClient();
// reset the token
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
if (!$user) {
$this->markTestSkipped('No user found in db.');
}
$config = $user->getConfig();
$config->setRssToken(null);
$em->persist($config);
$em->flush();
$crawler = $client->request('GET', '/config');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(array('_text')));
$this->assertContains('You need to generate a token first.', $body[0]);
$client->request('GET', '/generate-token');
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(array('_text')));
$this->assertNotContains('You need to generate a token first.', $body[0]);
}
public function testGenerateTokenAjax()
{
$this->logInAs('admin');
$client = $this->getClient();
$client->request(
'GET',
'/generate-token',
array(),
array(),
array('HTTP_X-Requested-With' => 'XMLHttpRequest')
);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);;
$this->assertArrayHasKey('token', $content);
}
public function testRssUpdate()
{
$this->logInAs('admin');
$client = $this->getClient();
$crawler = $client->request('GET', '/config');
if (500 == $client->getResponse()->getStatusCode()) {
var_export($client->getResponse()->getContent());
die();
}
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$form = $crawler->filter('button[id=rss_config_save]')->form();
$data = array(
'rss_config[rss_limit]' => 12,
);
$client->submit($form, $data);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
$this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text')));
$this->assertContains('RSS information updated', $alert[0]);
}
public function dataForRssFailed()
{
return array(
array(
array(
'rss_config[rss_limit]' => 0,
),
'This value should be 1 or more.',
),
array(
array(
'rss_config[rss_limit]' => 1000000000000,
),
'This will certainly kill the app',
),
);
}
/**
* @dataProvider dataForRssFailed
*/
public function testRssFailed($data, $expectedMessage)
{
$this->logInAs('admin');
$client = $this->getClient();
$crawler = $client->request('GET', '/config');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$form = $crawler->filter('button[id=rss_config_save]')->form();
$crawler = $client->submit($form, $data);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
$this->assertContains($expectedMessage, $alert[0]);
}
} }

View File

@ -60,7 +60,7 @@ class EntryControllerTest extends WallabagTestCase
$form = $crawler->filter('button[type=submit]')->form(); $form = $crawler->filter('button[type=submit]')->form();
$data = array( $data = array(
'entry[url]' => 'https://www.mailjet.com/blog/mailjet-zapier-integrations-made-easy/', 'entry[url]' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html',
); );
$client->submit($form, $data); $client->submit($form, $data);
@ -70,7 +70,7 @@ class EntryControllerTest extends WallabagTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
$this->assertGreaterThan(1, $alert = $crawler->filter('h2 a')->extract(array('_text'))); $this->assertGreaterThan(1, $alert = $crawler->filter('h2 a')->extract(array('_text')));
$this->assertContains('Mailjet', $alert[0]); $this->assertContains('Google', $alert[0]);
} }
public function testArchive() public function testArchive()

View File

@ -0,0 +1,126 @@
<?php
namespace Wallabag\CoreBundle\Tests\Controller;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
class RssControllerTest extends WallabagTestCase
{
public function validateDom($xml, $nb = null)
{
$doc = new \DOMDocument();
$doc->loadXML($xml);
$xpath = new \DOMXpath($doc);
if (null === $nb) {
$this->assertGreaterThan(0, $xpath->query('//item')->length);
} else {
$this->assertEquals($nb, $xpath->query('//item')->length);
}
$this->assertEquals(1, $xpath->query('/rss')->length);
$this->assertEquals(1, $xpath->query('/rss/channel')->length);
foreach ($xpath->query('//item') as $item) {
$this->assertEquals(1, $xpath->query('title', $item)->length);
$this->assertEquals(1, $xpath->query('source', $item)->length);
$this->assertEquals(1, $xpath->query('link', $item)->length);
$this->assertEquals(1, $xpath->query('guid', $item)->length);
$this->assertEquals(1, $xpath->query('pubDate', $item)->length);
$this->assertEquals(1, $xpath->query('description', $item)->length);
}
}
public function dataForBadUrl()
{
return array(
array(
'/admin/YZIOAUZIAO/unread.xml'
),
array(
'/wallace/YZIOAUZIAO/starred.xml'
),
array(
'/wallace/YZIOAUZIAO/archives.xml'
),
);
}
/**
* @dataProvider dataForBadUrl
*/
public function testBadUrl($url)
{
$client = $this->getClient();
$client->request('GET', $url);
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
public function testUnread()
{
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
$config = $user->getConfig();
$config->setRssToken('SUPERTOKEN');
$config->setRssLimit(2);
$em->persist($config);
$em->flush();
$client->request('GET', '/admin/SUPERTOKEN/unread.xml');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent(), 2);
}
public function testStarred()
{
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
$config = $user->getConfig();
$config->setRssToken('SUPERTOKEN');
$config->setRssLimit(1);
$em->persist($config);
$em->flush();
$client = $this->getClient();
$client->request('GET', '/admin/SUPERTOKEN/starred.xml');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), 1);
$this->validateDom($client->getResponse()->getContent());
}
public function testArchives()
{
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
->getRepository('WallabagCoreBundle: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/archive.xml');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->validateDom($client->getResponse()->getContent());
}
}

View File

@ -105,7 +105,7 @@ class WallabagRestControllerTest extends WallabagTestCase
$this->assertGreaterThanOrEqual(1, count(json_decode($client->getResponse()->getContent()))); $this->assertGreaterThanOrEqual(1, count(json_decode($client->getResponse()->getContent())));
$this->assertContains('Mailjet', $client->getResponse()->getContent()); $this->assertContains('Google', $client->getResponse()->getContent());
$this->assertTrue( $this->assertTrue(
$client->getResponse()->headers->contains( $client->getResponse()->headers->contains(

View File

@ -0,0 +1,22 @@
<?php
namespace Wallabag\CoreBundle\Tests\Mock;
use Wallabag\CoreBundle\Command\InstallCommand;
/**
* This mock aims to speed the test of InstallCommand by avoid calling external command
* like all doctrine commands.
*
* This speed the test but as a downside, it doesn't allow to fully test the InstallCommand
*
* Launching tests to avoid doctrine command:
* phpunit --exclude-group command-doctrine
*/
class InstallCommandMock extends InstallCommand
{
protected function runCommand($command, $parameters = array())
{
return $this;
}
}

View File

@ -0,0 +1,220 @@
<?php
namespace Wallabag\CoreBundle\Tests\Command;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Wallabag\CoreBundle\ParamConverter\UsernameRssTokenConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Wallabag\CoreBundle\Entity\User;
class UsernameRssTokenConverterTest extends KernelTestCase
{
public function testSupportsWithNoRegistry()
{
$params = new ParamConverter(array());
$converter = new UsernameRssTokenConverter();
$this->assertFalse($converter->supports($params));
}
public function testSupportsWithNoRegistryManagers()
{
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$registry->expects($this->once())
->method('getManagers')
->will($this->returnValue(array()));
$params = new ParamConverter(array());
$converter = new UsernameRssTokenConverter($registry);
$this->assertFalse($converter->supports($params));
}
public function testSupportsWithNoConfigurationClass()
{
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$registry->expects($this->once())
->method('getManagers')
->will($this->returnValue(array('default' => null)));
$params = new ParamConverter(array());
$converter = new UsernameRssTokenConverter($registry);
$this->assertFalse($converter->supports($params));
}
public function testSupportsWithNotTheGoodClass()
{
$meta = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')
->disableOriginalConstructor()
->getMock();
$meta->expects($this->once())
->method('getName')
->will($this->returnValue('nothingrelated'));
$em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getClassMetadata')
->with('superclass')
->will($this->returnValue($meta));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$registry->expects($this->once())
->method('getManagers')
->will($this->returnValue(array('default' => null)));
$registry->expects($this->once())
->method('getManagerForClass')
->with('superclass')
->will($this->returnValue($em));
$params = new ParamConverter(array('class' => 'superclass'));
$converter = new UsernameRssTokenConverter($registry);
$this->assertFalse($converter->supports($params));
}
public function testSupportsWithGoodClass()
{
$meta = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')
->disableOriginalConstructor()
->getMock();
$meta->expects($this->once())
->method('getName')
->will($this->returnValue('Wallabag\CoreBundle\Entity\User'));
$em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getClassMetadata')
->with('WallabagCoreBundle:User')
->will($this->returnValue($meta));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$registry->expects($this->once())
->method('getManagers')
->will($this->returnValue(array('default' => null)));
$registry->expects($this->once())
->method('getManagerForClass')
->with('WallabagCoreBundle:User')
->will($this->returnValue($em));
$params = new ParamConverter(array('class' => 'WallabagCoreBundle:User'));
$converter = new UsernameRssTokenConverter($registry);
$this->assertTrue($converter->supports($params));
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Route attribute is missing
*/
public function testApplyEmptyRequest()
{
$params = new ParamConverter(array());
$converter = new UsernameRssTokenConverter();
$converter->apply(new Request(), $params);
}
/**
* @expectedException Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* @expectedExceptionMessage User not found
*/
public function testApplyUserNotFound()
{
$repo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\UserRepository')
->disableOriginalConstructor()
->getMock();
$repo->expects($this->once())
->method('findOneByUsernameAndRsstoken')
->with('test', 'test')
->will($this->returnValue(null));
$em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getRepository')
->with('WallabagCoreBundle:User')
->will($this->returnValue($repo));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$registry->expects($this->once())
->method('getManagerForClass')
->with('WallabagCoreBundle:User')
->will($this->returnValue($em));
$params = new ParamConverter(array('class' => 'WallabagCoreBundle:User'));
$converter = new UsernameRssTokenConverter($registry);
$request = new Request(array(), array(), array('username' => 'test', 'token' => 'test'));
$converter->apply($request, $params);
}
public function testApplyUserFound()
{
$user = new User();
$repo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\UserRepository')
->disableOriginalConstructor()
->getMock();
$repo->expects($this->once())
->method('findOneByUsernameAndRsstoken')
->with('test', 'test')
->will($this->returnValue($user));
$em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getRepository')
->with('WallabagCoreBundle:User')
->will($this->returnValue($repo));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$registry->expects($this->once())
->method('getManagerForClass')
->with('WallabagCoreBundle:User')
->will($this->returnValue($em));
$params = new ParamConverter(array('class' => 'WallabagCoreBundle:User', 'name' => 'user'));
$converter = new UsernameRssTokenConverter($registry);
$request = new Request(array(), array(), array('username' => 'test', 'token' => 'test'));
$converter->apply($request, $params);
$this->assertEquals($user, $request->attributes->get('user'));
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Wallabag\CoreBundle\Tools;
class Utils
{
/**
* Generate a token used for RSS
*
* @return string
*/
public static function generateToken()
{
if (ini_get('open_basedir') === '') {
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// alternative to /dev/urandom for Windows
$token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
} else {
$token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
}
} else {
$token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
}
return str_replace('+', '', $token);
}
}