Merge pull request #1167 from wallabag/v2-api-bundle

Move API stuff in ApiBundle
This commit is contained in:
Nicolas Lœuillet 2015-06-01 15:49:49 +02:00
commit 2878416f8b
56 changed files with 1141 additions and 788 deletions

View file

@ -30,16 +30,16 @@ install:
- composer self-update
# build coverage only on one build, to speed up results feedbacks
before_script:
- if [[ "$TRAVIS_PHP_VERSION" = "5.6" ]]; then PHPUNIT_FLAGS="--coverage-clover=coverage.clover"; else PHPUNIT_FLAGS=""; fi;
# before_script:
# - if [[ "$TRAVIS_PHP_VERSION" = "5.6" ]]; then PHPUNIT_FLAGS="--coverage-clover=coverage.clover"; else PHPUNIT_FLAGS=""; fi;
script:
- ant prepare
- phpunit --exclude-group command-doctrine $PHPUNIT_FLAGS
- bin/phpunit --exclude-group command-doctrine --debug $PHPUNIT_FLAGS
after_script:
- |
if [ $TRAVIS_PHP_VERSION = '5.6' ]; then
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage.clover
fi
# after_script:
# - |
# if [ $TRAVIS_PHP_VERSION = '5.6' ]; then
# wget https://scrutinizer-ci.com/ocular.phar
# php ocular.phar code-coverage:upload --format=php-clover coverage.clover
# fi

View file

@ -23,7 +23,8 @@ class AppKernel extends Kernel
new Nelmio\CorsBundle\NelmioCorsBundle(),
new Liip\ThemeBundle\LiipThemeBundle(),
new Wallabag\CoreBundle\WallabagCoreBundle(),
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle()
new Wallabag\ApiBundle\WallabagApiBundle(),
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
);
if (in_array($this->getEnvironment(), array('dev', 'test'))) {

View file

@ -77,7 +77,7 @@ class Requirement
}
/**
* Returns the help text for resolving the problem
* Returns the help text for resolving the problem.
*
* @return string The help text
*/
@ -119,10 +119,10 @@ class PhpIniRequirement extends Requirement
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
@ -221,10 +221,10 @@ class RequirementCollection implements IteratorAggregate
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
@ -239,10 +239,10 @@ class RequirementCollection implements IteratorAggregate
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
@ -542,11 +542,22 @@ class SymfonyRequirements extends RequirementCollection
/* optional recommendations follow */
$this->addRecommendation(
file_get_contents(__FILE__) === file_get_contents(__DIR__.'/../vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php'),
'Requirements file should be up-to-date',
'Your requirements file is outdated. Run composer install and re-check your configuration.'
);
if (file_exists(__DIR__.'/../vendor/composer')) {
require_once __DIR__.'/../vendor/autoload.php';
try {
$r = new \ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle');
$contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php');
} catch (\ReflectionException $e) {
$contents = '';
}
$this->addRecommendation(
file_get_contents(__FILE__) === $contents,
'Requirements file should be up-to-date',
'Your requirements file is outdated. Run composer install and re-check your configuration.'
);
}
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.4', '>='),

View file

@ -1,3 +1,7 @@
wallabag_api:
resource: "@WallabagApiBundle/Resources/config/routing.yml"
prefix: /
app:
resource: @WallabagCoreBundle/Controller/
type: annotation

View file

@ -1,3 +1,3 @@
Rest_Wallabag:
type : rest
resource: "@WallabagCoreBundle/Resources/config/routing_rest.yml"
resource: "@WallabagApiBundle/Resources/config/routing_rest.yml"

628
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
<?php
namespace Wallabag\CoreBundle\Controller;
namespace Wallabag\ApiBundle\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
@ -47,6 +47,7 @@ class WallabagRestController extends Controller
* {"name"="username", "dataType"="string", "required"=true, "description"="username"}
* }
* )
*
* @return array
*/
public function getSaltAction($username)
@ -62,6 +63,7 @@ class WallabagRestController extends Controller
return array($user->getSalt() ?: null);
}
/**
* Retrieve all entries. It could be filtered by many options.
*
@ -76,6 +78,7 @@ class WallabagRestController extends Controller
* {"name"="tags", "dataType"="string", "required"=false, "format"="api%2Crest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
* }
* )
*
* @return Entry
*/
public function getEntriesAction(Request $request)
@ -86,17 +89,13 @@ class WallabagRestController extends Controller
$order = $request->query->get('order', 'desc');
$page = (int) $request->query->get('page', 1);
$perPage = (int) $request->query->get('perPage', 30);
$tags = $request->query->get('tags', array());
$tags = $request->query->get('tags', []);
$pager = $this
->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order);
if (0 === $pager->getNbResults()) {
throw $this->createNotFoundException();
}
$pager->setCurrentPage($page);
$pager->setMaxPerPage($perPage);
@ -108,32 +107,31 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($paginatedCollection, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Retrieve a single entry
* Retrieve a single entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return Entry
*/
public function getEntryAction(Entry $entry)
{
if ($entry->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
$json = $this->get('serializer')->serialize($entry, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Create an entry
* Create an entry.
*
* @ApiDoc(
* parameters={
@ -142,6 +140,7 @@ class WallabagRestController extends Controller
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* }
* )
*
* @return Entry
*/
public function postEntriesAction(Request $request)
@ -165,11 +164,11 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($entry, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Change several properties of an entry
* Change several properties of an entry.
*
* @ApiDoc(
* requirements={
@ -182,17 +181,16 @@ class WallabagRestController extends Controller
* {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false", "description"="starred the entry."},
* }
* )
*
* @return Entry
*/
public function patchEntriesAction(Entry $entry, Request $request)
{
if ($entry->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
$title = $request->request->get("title");
$isArchived = $request->request->get("archive");
$isStarred = $request->request->get("star");
$title = $request->request->get('title');
$isArchived = $request->request->get('archive');
$isStarred = $request->request->get('star');
if (!is_null($title)) {
$entry->setTitle($title);
@ -216,24 +214,23 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($entry, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Delete **permanently** an entry
* Delete **permanently** an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return Entry
*/
public function deleteEntriesAction(Entry $entry)
{
if ($entry->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
$em = $this->getDoctrine()->getManager();
$em->remove($entry);
@ -241,11 +238,11 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($entry, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Retrieve all tags for an entry
* Retrieve all tags for an entry.
*
* @ApiDoc(
* requirements={
@ -255,17 +252,15 @@ class WallabagRestController extends Controller
*/
public function getEntriesTagsAction(Entry $entry)
{
if ($entry->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
$json = $this->get('serializer')->serialize($entry->getTags(), 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Add one or more tags to an entry
* Add one or more tags to an entry.
*
* @ApiDoc(
* requirements={
@ -278,9 +273,7 @@ class WallabagRestController extends Controller
*/
public function postEntriesTagsAction(Request $request, Entry $entry)
{
if ($entry->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
@ -293,24 +286,22 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($entry, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Permanently remove one tag for an entry
* Permanently remove one tag for an entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="string", "requirement"="\w+", "description"="The tag"},
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*/
public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
{
if ($entry->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
$entry->removeTag($tag);
$em = $this->getDoctrine()->getManager();
@ -319,11 +310,11 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($entry, 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Retrieve all tags
* Retrieve all tags.
*
* @ApiDoc()
*/
@ -331,23 +322,21 @@ class WallabagRestController extends Controller
{
$json = $this->get('serializer')->serialize($this->getUser()->getTags(), 'json');
return new Response($json, 200, array('application/json'));
return $this->renderJsonResponse($json);
}
/**
* Permanently remove one tag from **every** entry
* Permanently remove one tag from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="string", "requirement"="\w+", "description"="The tag"}
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
* }
* )
*/
public function deleteTagAction(Tag $tag)
{
if ($tag->getUser()->getId() != $this->getUser()->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$tag->getUser()->getId().', logged user id: '.$this->getUser()->getId());
}
$this->validateUserAccess($tag->getUser()->getId(), $this->getUser()->getId());
$em = $this->getDoctrine()->getManager();
$em->remove($tag);
@ -355,6 +344,33 @@ class WallabagRestController extends Controller
$json = $this->get('serializer')->serialize($tag, 'json');
return $this->renderJsonResponse($json);
}
/**
* Validate that the first id is equal to the second one.
* If not, throw exception. It means a user try to access information from an other user.
*
* @param int $requestUserId User id from the requested source
* @param int $currentUserId User id from the retrieved source
*/
private function validateUserAccess($requestUserId, $currentUserId)
{
if ($requestUserId != $currentUserId) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$currentUserId);
}
}
/**
* Send a JSON Response.
* We don't use the Symfony JsonRespone, because it takes an array as parameter instead of a JSON string.
*
* @param string $json
*
* @return Response
*/
private function renderJsonResponse($json)
{
return new Response($json, 200, array('application/json'));
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Wallabag\ApiBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {@inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('wallabag_api');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Wallabag\CoreBundle\DependencyInjection\Security\Factory;
namespace Wallabag\ApiBundle\DependencyInjection\Security\Factory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

View file

@ -0,0 +1,25 @@
<?php
namespace Wallabag\ApiBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class WallabagApiExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
public function getAlias()
{
return 'wallabag_api';
}
}

View file

@ -0,0 +1,4 @@
entries:
type: rest
resource: "WallabagApiBundle:WallabagRest"
name_prefix: api_

View file

@ -0,0 +1,12 @@
services:
wsse.security.authentication.provider:
class: Wallabag\ApiBundle\Security\Authentication\Provider\WsseProvider
public: false
arguments: ['', '%kernel.cache_dir%/security/nonces']
wsse.security.authentication.listener:
class: Wallabag\ApiBundle\Security\Firewall\WsseListener
public: false
tags:
- { name: monolog.logger, channel: wsse }
arguments: ['@security.context', '@security.authentication.manager', '@logger']

View file

@ -1,12 +1,13 @@
<?php
namespace Wallabag\CoreBundle\Security\Authentication\Provider;
namespace Wallabag\ApiBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken;
use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
class WsseProvider implements AuthenticationProviderInterface
{
@ -29,7 +30,7 @@ class WsseProvider implements AuthenticationProviderInterface
$user = $this->userProvider->loadUserByUsername($token->getUsername());
if (!$user) {
throw new AuthenticationException("Bad credentials. Did you forgot your username?");
throw new AuthenticationException('Bad credentials. Did you forgot your username?');
}
if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
@ -46,12 +47,12 @@ class WsseProvider implements AuthenticationProviderInterface
{
// Check created time is not in the future
if (strtotime($created) > time()) {
throw new AuthenticationException("Back to the future...");
throw new AuthenticationException('Back to the future...');
}
// Expire timestamp after 5 minutes
if (time() - strtotime($created) > 300) {
throw new AuthenticationException("Too late for this timestamp... Watch your watch.");
throw new AuthenticationException('Too late for this timestamp... Watch your watch.');
}
// Validate nonce is unique within 5 minutes
@ -65,7 +66,7 @@ class WsseProvider implements AuthenticationProviderInterface
$expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));
if ($digest !== $expected) {
throw new AuthenticationException("Bad credentials ! Digest is not as expected.");
throw new AuthenticationException('Bad credentials ! Digest is not as expected.');
}
return $digest === $expected;

View file

@ -1,5 +1,6 @@
<?php
namespace Wallabag\CoreBundle\Security\Authentication\Token;
namespace Wallabag\ApiBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

View file

@ -1,6 +1,6 @@
<?php
namespace Wallabag\CoreBundle\Security\Firewall;
namespace Wallabag\ApiBundle\Security\Firewall;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@ -8,7 +8,7 @@ use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken;
use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
use Psr\Log\LoggerInterface;
class WsseListener implements ListenerInterface

View file

@ -0,0 +1,410 @@
<?php
namespace Wallabag\ApiBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class WallabagRestControllerTest extends WebTestCase
{
protected static $salt;
/**
* Grab the salt once and store it to be available for all tests.
*/
public static function setUpBeforeClass()
{
$client = self::createClient();
$user = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
self::$salt = $user->getSalt();
}
/**
* Generate HTTP headers for authenticate user on API.
*
* @param string $username
* @param string $password
*
* @return array
*/
private function generateHeaders($username, $password)
{
$encryptedPassword = sha1($password.$username.self::$salt);
$nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
$now = new \DateTime('now', new \DateTimeZone('UTC'));
$created = (string) $now->format('Y-m-d\TH:i:s\Z');
$digest = base64_encode(sha1(base64_decode($nonce).$created.$encryptedPassword, true));
return array(
'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
);
}
public function testGetSalt()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$user = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey(0, $content);
$this->assertEquals($user->getSalt(), $content[0]);
$client->request('GET', '/api/salts/notfound.json');
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
public function testWithBadHeaders()
{
$client = $this->createClient();
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByIsArchived(false);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$badHeaders = array(
'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="admin", PasswordDigest="Wr0ngDig3st", Nonce="n0Nc3", Created="2015-01-01T13:37:00Z"',
);
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $badHeaders);
$this->assertEquals(403, $client->getResponse()->getStatusCode());
}
public function testGetOneEntry()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(array('user' => 1, 'isArchived' => false));
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertCount(count($entry->getTags()), $content['tags']);
$this->assertTrue(
$client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetOneEntryWrongUser()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(array('user' => 2, 'isArchived' => false));
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertEquals(403, $client->getResponse()->getStatusCode());
}
public function testGetEntries()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/entries', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
$client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetStarredEntries()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/entries', array('archive' => 1), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
$client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testDeleteEntry()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
// We'll try to delete this entry again
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
public function testPostEntry()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('POST', '/api/entries.json', array(
'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',
'tags' => 'google',
), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$this->assertEquals('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', $content['url']);
$this->assertEquals(false, $content['is_archived']);
$this->assertEquals(false, $content['is_starred']);
$this->assertCount(1, $content['tags']);
}
public function testPatchEntry()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array(
'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(),
'star' => true,
'archive' => false,
), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertEquals('New awesome title', $content['title']);
$this->assertGreaterThan($nbTags, count($content['tags']));
}
public function testGetTagsEntry()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags(1);
$entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$tags = array();
foreach ($entry->getTags() as $tag) {
$tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel());
}
$client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers);
$this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent());
}
public function testPostTagsOnEntry()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$nbTags = count($entry->getTags());
$newTags = 'tag1,tag2,tag3';
$client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags + 3, count($content['tags']));
$entryDB = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->find($entry->getId());
$tagsInDB = array();
foreach ($entryDB->getTags()->toArray() as $tag) {
$tagsInDB[$tag->getId()] = $tag->getLabel();
}
foreach (explode(',', $newTags) as $tag) {
$this->assertContains($tag, $tagsInDB);
}
}
public function testDeleteOneTagEntrie()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$tag = $entry->getTags()[0];
$client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags - 1, count($content['tags']));
}
public function testGetUserTags()
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/tags.json', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content);
$this->assertArrayHasKey('id', $content[0]);
$this->assertArrayHasKey('label', $content[0]);
return end($content);
}
/**
* @depends testGetUserTags
*/
public function testDeleteUserTag($tag)
{
$client = $this->createClient();
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('DELETE', '/api/tags/'.$tag['id'].'.json', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content);
$this->assertEquals($tag['label'], $content['label']);
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Wallabag\ApiBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Wallabag\ApiBundle\DependencyInjection\Security\Factory\WsseFactory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class WallabagApiBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new WsseFactory());
}
}

View file

@ -225,7 +225,7 @@ class InstallCommand extends ContainerAwareCommand
}
/**
* Run a command
* Run a command.
*
* @param string $command
* @param array $parameters Parameters to this command (usually 'force' => true)
@ -266,9 +266,9 @@ class InstallCommand extends ContainerAwareCommand
}
/**
* Check if the database already exists
* Check if the database already exists.
*
* @return boolean
* @return bool
*/
private function isDatabasePresent()
{
@ -300,9 +300,9 @@ class InstallCommand extends ContainerAwareCommand
/**
* Check if the schema is already created.
* If we found at least oen table, it means the schema exists
* If we found at least oen table, it means the schema exists.
*
* @return boolean
* @return bool
*/
private function isSchemaPresent()
{

View file

@ -133,7 +133,7 @@ class ConfigController extends Controller
'rss' => array(
'username' => $user->getUsername(),
'token' => $config->getRssToken(),
)
),
));
}

View file

@ -50,7 +50,7 @@ class EntryController extends Controller
}
/**
* Shows unread entries for current user
* Shows unread entries for current user.
*
* @Route("/unread", name="unread")
*
@ -70,7 +70,7 @@ class EntryController extends Controller
}
/**
* Shows read entries for current user
* Shows read entries for current user.
*
* @Route("/archive", name="archive")
*
@ -90,7 +90,7 @@ class EntryController extends Controller
}
/**
* Shows starred entries for current user
* Shows starred entries for current user.
*
* @Route("/starred", name="starred")
*
@ -110,7 +110,7 @@ class EntryController extends Controller
}
/**
* Shows entry content
* Shows entry content.
*
* @param Entry $entry
*
@ -129,7 +129,7 @@ class EntryController extends Controller
}
/**
* Changes read status for an entry
* Changes read status for an entry.
*
* @param Request $request
* @param Entry $entry
@ -154,7 +154,7 @@ class EntryController extends Controller
}
/**
* Changes favorite status for an entry
* Changes favorite status for an entry.
*
* @param Request $request
* @param Entry $entry
@ -179,7 +179,7 @@ class EntryController extends Controller
}
/**
* Deletes entry
* Deletes entry.
*
* @param Request $request
* @param Entry $entry
@ -205,7 +205,7 @@ class EntryController extends Controller
}
/**
* Check if the logged user can manage the given entry
* Check if the logged user can manage the given entry.
*
* @param Entry $entry
*/

View file

@ -11,7 +11,7 @@ use Wallabag\CoreBundle\Entity\Entry;
class RssController extends Controller
{
/**
* Shows unread entries for current user
* 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")
@ -35,7 +35,7 @@ class RssController extends Controller
}
/**
* Shows read entries for current user
* Shows read entries for current user.
*
* @Route("/{username}/{token}/archive.xml", name="archive_rss")
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
@ -59,7 +59,7 @@ class RssController extends Controller
}
/**
* Shows starred entries for current user
* Shows starred entries for current user.
*
* @Route("/{username}/{token}/starred.xml", name="starred_rss")
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")

View file

@ -30,9 +30,10 @@ class SecurityController extends Controller
}
/**
* Request forgot password: show form
* Request forgot password: show form.
*
* @Route("/forgot-password", name="forgot_password")
*
* @Method({"GET", "POST"})
*/
public function forgotPasswordAction(Request $request)
@ -73,9 +74,10 @@ class SecurityController extends Controller
}
/**
* Tell the user to check his email provider
* Tell the user to check his email provider.
*
* @Route("/forgot-password/check-email", name="forgot_password_check_email")
*
* @Method({"GET"})
*/
public function checkEmailAction(Request $request)
@ -93,9 +95,10 @@ class SecurityController extends Controller
}
/**
* Reset user password
* Reset user password.
*
* @Route("/forgot-password/{token}", name="forgot_password_reset")
*
* @Method({"GET", "POST"})
*/
public function resetAction(Request $request, $token)

View file

@ -39,9 +39,9 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
$entry3->setContent('This is my content /o/');
$tag1 = new Tag($this->getReference('bob-user'));
$tag1->setLabel("foo");
$tag1->setLabel('foo');
$tag2 = new Tag($this->getReference('bob-user'));
$tag2->setLabel("bar");
$tag2->setLabel('bar');
$entry3->addTag($tag1);
$entry3->addTag($tag2);
@ -56,9 +56,9 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
$entry4->setContent('This is my content /o/');
$tag1 = new Tag($this->getReference('admin-user'));
$tag1->setLabel("foo");
$tag1->setLabel('foo');
$tag2 = new Tag($this->getReference('admin-user'));
$tag2->setLabel("bar");
$tag2->setLabel('bar');
$entry4->addTag($tag1);
$entry4->addTag($tag2);

View file

@ -3,15 +3,15 @@
namespace Wallabag\CoreBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class WallabagCoreExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}

View file

@ -25,7 +25,7 @@ class PrefixedNamingStrategy implements NamingStrategy
*/
public function classToTableName($className)
{
return strtolower($this->prefix . substr($className, strrpos($className, '\\') + 1));
return strtolower($this->prefix.substr($className, strrpos($className, '\\') + 1));
}
/**
@ -49,7 +49,7 @@ class PrefixedNamingStrategy implements NamingStrategy
*/
public function joinColumnName($propertyName)
{
return $propertyName . '_' . $this->referenceColumnName();
return $propertyName.'_'.$this->referenceColumnName();
}
/**
@ -62,7 +62,7 @@ class PrefixedNamingStrategy implements NamingStrategy
// ie: not "wallabag_entry_wallabag_tag" but "wallabag_entry_tag"
$target = substr($targetEntity, strrpos($targetEntity, '\\') + 1);
return strtolower($this->classToTableName($sourceEntity) . '_' .$target);
return strtolower($this->classToTableName($sourceEntity).'_'.$target);
}
/**
@ -70,6 +70,14 @@ class PrefixedNamingStrategy implements NamingStrategy
*/
public function joinKeyColumnName($entityName, $referencedColumnName = null)
{
return strtolower($this->classToTableName($entityName) . '_' .($referencedColumnName ?: $this->referenceColumnName()));
return strtolower($this->classToTableName($entityName).'_'.($referencedColumnName ?: $this->referenceColumnName()));
}
/**
* {@inheritdoc}
*/
public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null)
{
return $propertyName.'_'.$embeddedColumnName;
}
}

View file

@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Config
* Config.
*
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\ConfigRepository")
* @ORM\Table
@ -15,7 +15,7 @@ use Symfony\Component\Validator\Constraints as Assert;
class Config
{
/**
* @var integer
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
@ -32,7 +32,7 @@ class Config
private $theme;
/**
* @var integer
* @var int
*
* @Assert\NotBlank()
* @Assert\Range(
@ -60,7 +60,7 @@ class Config
private $rssToken;
/**
* @var integer
* @var int
*
* @ORM\Column(name="rss_limit", type="integer", nullable=true)
* @Assert\Range(
@ -85,9 +85,9 @@ class Config
}
/**
* Get id
* Get id.
*
* @return integer
* @return int
*/
public function getId()
{
@ -95,9 +95,10 @@ class Config
}
/**
* Set theme
* Set theme.
*
* @param string $theme
*
* @param string $theme
* @return Config
*/
public function setTheme($theme)
@ -108,7 +109,7 @@ class Config
}
/**
* Get theme
* Get theme.
*
* @return string
*/
@ -118,9 +119,10 @@ class Config
}
/**
* Set itemsPerPage
* Set itemsPerPage.
*
* @param int $itemsPerPage
*
* @param integer $itemsPerPage
* @return Config
*/
public function setItemsPerPage($itemsPerPage)
@ -131,9 +133,9 @@ class Config
}
/**
* Get itemsPerPage
* Get itemsPerPage.
*
* @return integer
* @return int
*/
public function getItemsPerPage()
{
@ -141,9 +143,10 @@ class Config
}
/**
* Set language
* Set language.
*
* @param string $language
*
* @param string $language
* @return Config
*/
public function setLanguage($language)
@ -154,7 +157,7 @@ class Config
}
/**
* Get language
* Get language.
*
* @return string
*/
@ -164,9 +167,10 @@ class Config
}
/**
* Set user
* Set user.
*
* @param \Wallabag\CoreBundle\Entity\User $user
*
* @param \Wallabag\CoreBundle\Entity\User $user
* @return Config
*/
public function setUser(\Wallabag\CoreBundle\Entity\User $user = null)
@ -177,7 +181,7 @@ class Config
}
/**
* Get user
* Get user.
*
* @return \Wallabag\CoreBundle\Entity\User
*/
@ -187,9 +191,10 @@ class Config
}
/**
* Set rssToken
* Set rssToken.
*
* @param string $rssToken
*
* @param string $rssToken
* @return Config
*/
public function setRssToken($rssToken)
@ -200,7 +205,7 @@ class Config
}
/**
* Get rssToken
* Get rssToken.
*
* @return string
*/
@ -210,9 +215,10 @@ class Config
}
/**
* Set rssLimit
* Set rssLimit.
*
* @param string $rssLimit
*
* @param string $rssLimit
* @return Config
*/
public function setRssLimit($rssLimit)
@ -223,7 +229,7 @@ class Config
}
/**
* Get rssLimit
* Get rssLimit.
*
* @return string
*/

View file

@ -9,7 +9,7 @@ use Hateoas\Configuration\Annotation as Hateoas;
use JMS\Serializer\Annotation\XmlRoot;
/**
* Entry
* Entry.
*
* @XmlRoot("entry")
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository")
@ -21,7 +21,7 @@ class Entry
{
/** @Serializer\XmlAttribute */
/**
* @var integer
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
@ -45,14 +45,14 @@ class Entry
private $url;
/**
* @var boolean
* @var bool
*
* @ORM\Column(name="is_archived", type="boolean")
*/
private $isArchived = false;
/**
* @var boolean
* @var bool
*
* @ORM\Column(name="is_starred", type="boolean")
*/
@ -94,7 +94,7 @@ class Entry
private $mimetype;
/**
* @var integer
* @var int
*
* @ORM\Column(name="reading_type", type="integer", nullable=true)
*/
@ -108,7 +108,7 @@ class Entry
private $domainName;
/**
* @var boolean
* @var bool
*
* @ORM\Column(name="is_public", type="boolean", nullable=true, options={"default" = false})
*/
@ -135,9 +135,9 @@ class Entry
}
/**
* Get id
* Get id.
*
* @return integer
* @return int
*/
public function getId()
{
@ -145,9 +145,10 @@ class Entry
}
/**
* Set title
* Set title.
*
* @param string $title
*
* @param string $title
* @return Entry
*/
public function setTitle($title)
@ -158,7 +159,7 @@ class Entry
}
/**
* Get title
* Get title.
*
* @return string
*/
@ -168,9 +169,10 @@ class Entry
}
/**
* Set url
* Set url.
*
* @param string $url
*
* @param string $url
* @return Entry
*/
public function setUrl($url)
@ -181,7 +183,7 @@ class Entry
}
/**
* Get url
* Get url.
*
* @return string
*/
@ -191,9 +193,10 @@ class Entry
}
/**
* Set isArchived
* Set isArchived.
*
* @param string $isArchived
*
* @param string $isArchived
* @return Entry
*/
public function setArchived($isArchived)
@ -204,7 +207,7 @@ class Entry
}
/**
* Get isArchived
* Get isArchived.
*
* @return string
*/
@ -221,9 +224,10 @@ class Entry
}
/**
* Set isStarred
* Set isStarred.
*
* @param string $isStarred
*
* @param string $isStarred
* @return Entry
*/
public function setStarred($isStarred)
@ -234,7 +238,7 @@ class Entry
}
/**
* Get isStarred
* Get isStarred.
*
* @return string
*/
@ -251,9 +255,10 @@ class Entry
}
/**
* Set content
* Set content.
*
* @param string $content
*
* @param string $content
* @return Entry
*/
public function setContent($content)
@ -264,7 +269,7 @@ class Entry
}
/**
* Get content
* Get content.
*
* @return string
*/
@ -375,7 +380,7 @@ class Entry
}
/**
* @return boolean
* @return bool
*/
public function isPublic()
{
@ -383,7 +388,7 @@ class Entry
}
/**
* @param boolean $isPublic
* @param bool $isPublic
*/
public function setPublic($isPublic)
{

View file

@ -9,7 +9,7 @@ use JMS\Serializer\Annotation\Expose;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Tag
* Tag.
*
* @XmlRoot("tag")
* @ORM\Table
@ -19,7 +19,7 @@ use Doctrine\Common\Collections\ArrayCollection;
class Tag
{
/**
* @var integer
* @var int
*
* @Expose
* @ORM\Column(name="id", type="integer")
@ -52,9 +52,9 @@ class Tag
$this->entries = new ArrayCollection();
}
/**
* Get id
* Get id.
*
* @return integer
* @return int
*/
public function getId()
{
@ -62,9 +62,10 @@ class Tag
}
/**
* Set label
* Set label.
*
* @param string $label
*
* @param string $label
* @return Tag
*/
public function setLabel($label)
@ -75,7 +76,7 @@ class Tag
}
/**
* Get label
* Get label.
*
* @return string
*/

View file

@ -12,7 +12,7 @@ use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
/**
* User
* User.
*
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\UserRepository")
* @ORM\Table
@ -25,7 +25,7 @@ use JMS\Serializer\Annotation\Expose;
class User implements AdvancedUserInterface, \Serializable
{
/**
* @var integer
* @var int
*
* @Expose
* @ORM\Column(name="id", type="integer")
@ -142,9 +142,9 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get id
* Get id.
*
* @return integer
* @return int
*/
public function getId()
{
@ -152,9 +152,10 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Set username
* Set username.
*
* @param string $username
*
* @param string $username
* @return User
*/
public function setUsername($username)
@ -165,7 +166,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get username
* Get username.
*
* @return string
*/
@ -191,9 +192,10 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Set password
* Set password.
*
* @param string $password
*
* @param string $password
* @return User
*/
public function setPassword($password)
@ -208,7 +210,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get password
* Get password.
*
* @return string
*/
@ -218,9 +220,10 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Set name
* Set name.
*
* @param string $name
*
* @param string $name
* @return User
*/
public function setName($name)
@ -231,7 +234,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get name
* Get name.
*
* @return string
*/
@ -241,9 +244,10 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Set email
* Set email.
*
* @param string $email
*
* @param string $email
* @return User
*/
public function setEmail($email)
@ -254,7 +258,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get email
* Get email.
*
* @return string
*/
@ -341,8 +345,7 @@ class User implements AdvancedUserInterface, \Serializable
public function unserialize($serialized)
{
list(
$this->id,
) = unserialize($serialized);
$this->id) = unserialize($serialized);
}
public function isEqualTo(UserInterface $user)
@ -370,9 +373,10 @@ class User implements AdvancedUserInterface, \Serializable
return $this->isActive;
}
/**
* Set config
* Set config.
*
* @param \Wallabag\CoreBundle\Entity\Config $config
*
* @param \Wallabag\CoreBundle\Entity\Config $config
* @return User
*/
public function setConfig(\Wallabag\CoreBundle\Entity\Config $config = null)
@ -383,7 +387,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get config
* Get config.
*
* @return \Wallabag\CoreBundle\Entity\Config
*/
@ -393,9 +397,10 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Set confirmationToken
* Set confirmationToken.
*
* @param string $confirmationToken
*
* @param string $confirmationToken
* @return User
*/
public function setConfirmationToken($confirmationToken)
@ -406,7 +411,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get confirmationToken
* Get confirmationToken.
*
* @return string
*/
@ -416,9 +421,10 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Set passwordRequestedAt
* Set passwordRequestedAt.
*
* @param \DateTime $passwordRequestedAt
*
* @param \DateTime $passwordRequestedAt
* @return User
*/
public function setPasswordRequestedAt($passwordRequestedAt)
@ -429,7 +435,7 @@ class User implements AdvancedUserInterface, \Serializable
}
/**
* Get passwordRequestedAt
* Get passwordRequestedAt.
*
* @return \DateTime
*/

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -1,4 +1,5 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;

View file

@ -5,15 +5,16 @@ namespace Wallabag\CoreBundle\Helper;
final class Tools
{
/**
* Download a file (typically, for downloading pictures on web server)
* Download a file (typically, for downloading pictures on web server).
*
* @param $url
*
* @return bool|mixed|string
*/
public static function getFile($url)
{
$timeout = 15;
$useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
$useragent = 'Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0';
if (in_array('curl', get_loaded_extensions())) {
# Fetch feed from URL
@ -32,7 +33,7 @@ final class Tools
# FeedBurner requires a proper USER-AGENT...
curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
curl_setopt($curl, CURLOPT_ENCODING, 'gzip, deflate');
curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
$data = curl_exec($curl);
@ -45,7 +46,7 @@ final class Tools
array(
'http' => array(
'timeout' => $timeout,
'header' => "User-Agent: ".$useragent,
'header' => 'User-Agent: '.$useragent,
'follow_location' => true,
),
'ssl' => array(
@ -91,9 +92,10 @@ final class Tools
}
/**
* Encode a URL by using a salt
* Encode a URL by using a salt.
*
* @param $string
*
* @return string
*/
public static function encodeString($string)

View file

@ -10,7 +10,7 @@ use Pagerfanta\Pagerfanta;
class EntryRepository extends EntityRepository
{
/**
* Retrieves unread entries for a user
* Retrieves unread entries for a user.
*
* @param int $userId
* @param int $firstResult
@ -35,7 +35,7 @@ class EntryRepository extends EntityRepository
}
/**
* Retrieves read entries for a user
* Retrieves read entries for a user.
*
* @param int $userId
* @param int $firstResult
@ -61,7 +61,7 @@ class EntryRepository extends EntityRepository
}
/**
* Retrieves starred entries for a user
* Retrieves starred entries for a user.
*
* @param int $userId
* @param int $firstResult
@ -87,7 +87,7 @@ class EntryRepository extends EntityRepository
}
/**
* Find Entries
* Find Entries.
*
* @param int $userId
* @param bool $isArchived

View file

@ -7,7 +7,7 @@ use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
/**
* Find a user by its username and rss roken
* Find a user by its username and rss roken.
*
* @param string $username
* @param string $rssToken

View file

@ -1,4 +0,0 @@
entries:
type: rest
resource: "WallabagCoreBundle:WallabagRest"
name_prefix: api_

View file

@ -4,18 +4,6 @@ services:
tags:
- { name: twig.extension }
wsse.security.authentication.provider:
class: Wallabag\CoreBundle\Security\Authentication\Provider\WsseProvider
public: false
arguments: ['', '%kernel.cache_dir%/security/nonces']
wsse.security.authentication.listener:
class: Wallabag\CoreBundle\Security\Firewall\WsseListener
public: false
tags:
- { name: monolog.logger, channel: wsse }
arguments: ['@security.context', '@security.authentication.manager', '@logger']
wallabag_core.helper.detect_active_theme:
class: Wallabag\CoreBundle\Helper\DetectActiveTheme
arguments:

View file

@ -7,8 +7,7 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
/**
* This override just add en extra variable (username) to be able to salt the password
* the way Wallabag v1 does. It will avoid to break compatibility with Wallabag v1
*
* the way Wallabag v1 does. It will avoid to break compatibility with Wallabag v1.
*/
class WallabagPasswordEncoder extends BasePasswordEncoder
{

View file

@ -45,7 +45,7 @@ class WallabagAuthenticationProvider extends UserAuthenticationProvider
throw new BadCredentialsException('The credentials were changed from another session.');
}
} else {
if ("" === ($presentedPassword = $token->getCredentials())) {
if ('' === ($presentedPassword = $token->getCredentials())) {
throw new BadCredentialsException('The presented password cannot be empty.');
}

View file

@ -9,7 +9,7 @@ final class Extractor
{
public static function extract($url)
{
$pageContent = Extractor::getPageContent(new Url(base64_encode($url)));
$pageContent = self::getPageContent(new Url(base64_encode($url)));
$title = $pageContent['rss']['channel']['item']['title'] ?: 'Untitled';
$body = $pageContent['rss']['channel']['item']['description'];
@ -21,9 +21,10 @@ final class Extractor
}
/**
* Get the content for a given URL (by a call to FullTextFeed)
* Get the content for a given URL (by a call to FullTextFeed).
*
* @param Url $url
*
* @param Url $url
* @return mixed
*/
public static function getPageContent(Url $url)
@ -49,12 +50,12 @@ final class Extractor
$scope = function () {
extract(func_get_arg(1));
$_GET = $_REQUEST = array(
"url" => $url->getUrl(),
"max" => 5,
"links" => "preserve",
"exc" => "",
"format" => "json",
"submit" => "Create Feed",
'url' => $url->getUrl(),
'max' => 5,
'links' => 'preserve',
'exc' => '',
'format' => 'json',
'submit' => 'Create Feed',
);
ob_start();
require func_get_arg(0);
@ -67,11 +68,11 @@ final class Extractor
// Silence $scope function to avoid
// issues with FTRSS when error_reporting is to high
// FTRSS generates PHP warnings which break output
$json = @$scope(__DIR__."/../../../../vendor/wallabag/Fivefilters_Libraries/makefulltextfeed.php", array("url" => $url));
$json = @$scope(__DIR__.'/../../../../vendor/wallabag/Fivefilters_Libraries/makefulltextfeed.php', array('url' => $url));
// Clearing and restoring context
foreach ($GLOBALS as $key => $value) {
if ($key != "GLOBALS" && $key != "_SESSION") {
if ($key != 'GLOBALS' && $key != '_SESSION') {
unset($GLOBALS[$key]);
}
}

View file

@ -2,7 +2,7 @@
namespace Wallabag\CoreBundle\Tests\Command;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
use Wallabag\CoreBundle\Command\InstallCommand;
use Wallabag\CoreBundle\Tests\Mock\InstallCommandMock;
use Symfony\Bundle\FrameworkBundle\Console\Application;
@ -12,7 +12,7 @@ use Symfony\Component\Console\Output\NullOutput;
use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
class InstallCommandTest extends WallabagTestCase
class InstallCommandTest extends WallabagCoreTestCase
{
public static function tearDownAfterClass()
{

View file

@ -2,9 +2,9 @@
namespace Wallabag\CoreBundle\Tests\Controller;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
class ConfigControllerTest extends WallabagTestCase
class ConfigControllerTest extends WallabagCoreTestCase
{
public function testLogin()
{
@ -397,7 +397,7 @@ class ConfigControllerTest extends WallabagTestCase
);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);;
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('token', $content);
}

View file

@ -2,10 +2,10 @@
namespace Wallabag\CoreBundle\Tests\Controller;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
use Doctrine\ORM\AbstractQuery;
class EntryControllerTest extends WallabagTestCase
class EntryControllerTest extends WallabagCoreTestCase
{
public function testLogin()
{

View file

@ -2,9 +2,9 @@
namespace Wallabag\CoreBundle\Tests\Controller;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
class RssControllerTest extends WallabagTestCase
class RssControllerTest extends WallabagCoreTestCase
{
public function validateDom($xml, $nb = null)
{
@ -36,13 +36,13 @@ class RssControllerTest extends WallabagTestCase
{
return array(
array(
'/admin/YZIOAUZIAO/unread.xml'
'/admin/YZIOAUZIAO/unread.xml',
),
array(
'/wallace/YZIOAUZIAO/starred.xml'
'/wallace/YZIOAUZIAO/starred.xml',
),
array(
'/wallace/YZIOAUZIAO/archives.xml'
'/wallace/YZIOAUZIAO/archives.xml',
),
);
}

View file

@ -2,11 +2,11 @@
namespace Wallabag\CoreBundle\Tests\Controller;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
class SecurityControllerTest extends WallabagTestCase
class SecurityControllerTest extends WallabagCoreTestCase
{
public function testLogin()
{

View file

@ -1,214 +0,0 @@
<?php
namespace Wallabag\CoreBundle\Tests\Controller;
use Wallabag\CoreBundle\Tests\WallabagTestCase;
class WallabagRestControllerTest extends WallabagTestCase
{
/**
* Generate HTTP headers for authenticate user on API
*
* @param $username
* @param $password
* @param $salt
*
* @return array
*/
private function generateHeaders($username, $password, $salt)
{
$encryptedPassword = sha1($password.$username.$salt);
$nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
$now = new \DateTime('now', new \DateTimeZone('UTC'));
$created = (string) $now->format('Y-m-d\TH:i:s\Z');
$digest = base64_encode(sha1(base64_decode($nonce).$created.$encryptedPassword, true));
return array(
'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
);
}
public function testGetSalt()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->assertNotEmpty(json_decode($client->getResponse()->getContent()));
$client->request('GET', '/api/salts/notfound.json');
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
public function testWithBadHeaders()
{
$client = $this->createClient();
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByIsArchived(false);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$badHeaders = array(
'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="admin", PasswordDigest="Wr0ngDig3st", Nonce="n0Nc3", Created="2015-01-01T13:37:00Z"',
);
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $badHeaders);
$this->assertEquals(403, $client->getResponse()->getStatusCode());
}
public function testGetOneEntry()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
$headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByIsArchived(false);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertContains($entry->getTitle(), $client->getResponse()->getContent());
$this->assertTrue(
$client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetEntries()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
$headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$client->request('GET', '/api/entries', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->assertGreaterThanOrEqual(1, count(json_decode($client->getResponse()->getContent())));
$this->assertContains('Google', $client->getResponse()->getContent());
$this->assertTrue(
$client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testDeleteEntry()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
$headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
// We'll try to delete this entry again
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
$headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
public function testGetTagsEntry()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
$headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags(1);
$entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$tags = array();
foreach ($entry->getTags() as $tag) {
$tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel());
}
$client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers);
$this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent());
}
public function testPostTagsOnEntry()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
$headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$newTags = 'tag1,tag2,tag3';
$client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$entryDB = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->find($entry->getId());
$tagsInDB = array();
foreach ($entryDB->getTags()->toArray() as $tag) {
$tagsInDB[$tag->getId()] = $tag->getLabel();
}
foreach (explode(',', $newTags) as $tag) {
$this->assertContains($tag, $tagsInDB);
}
}
}

View file

@ -4,7 +4,7 @@ namespace Wallabag\CoreBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
abstract class WallabagTestCase extends WebTestCase
abstract class WallabagCoreTestCase extends WebTestCase
{
private $client = null;

View file

@ -5,7 +5,7 @@ namespace Wallabag\CoreBundle\Tools;
class Utils
{
/**
* Generate a token used for RSS
* Generate a token used for RSS.
*
* @return string
*/
@ -22,6 +22,7 @@ class Utils
$token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
}
return str_replace('+', '', $token);
// remove character which can broken the url
return str_replace(array('+', '/'), '', $token);
}
}

View file

@ -13,9 +13,10 @@ class WallabagExtension extends \Twig_Extension
}
/**
* Returns the domain name for a URL
* Returns the domain name for a URL.
*
* @param $url
*
* @return string
*/
public static function getDomainName($url)
@ -24,9 +25,10 @@ class WallabagExtension extends \Twig_Extension
}
/**
* For a given text, we calculate reading time for an article
* For a given text, we calculate reading time for an article.
*
* @param $text
*
* @return float
*/
public static function getReadingTime($text)

View file

@ -3,16 +3,7 @@
namespace Wallabag\CoreBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Wallabag\CoreBundle\DependencyInjection\Security\Factory\WsseFactory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class WallabagCoreBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new WsseFactory());
}
}