diff --git a/app/AppKernel.php b/app/AppKernel.php index 2475fe162..1eacb348e 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -29,6 +29,7 @@ class AppKernel extends Kernel new FOS\OAuthServerBundle\FOSOAuthServerBundle(), new Wallabag\UserBundle\WallabagUserBundle(), new Scheb\TwoFactorBundle\SchebTwoFactorBundle(), + new KPhoen\RulerZBundle\KPhoenRulerZBundle(), ); if (in_array($this->getEnvironment(), array('dev', 'test'))) { diff --git a/app/config/config.yml b/app/config/config.yml index 285fbd7cf..82c5e7c99 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -189,3 +189,7 @@ scheb_two_factor: sender_email: %twofactor_sender% digits: 6 template: WallabagUserBundle:Authentication:form.html.twig + +kphoen_rulerz: + executors: + doctrine: true diff --git a/composer.json b/composer.json index 5892dc1aa..26608cdce 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,8 @@ "friendsofsymfony/oauth-server-bundle": "^1.4@dev", "scheb/two-factor-bundle": "~1.4", "grandt/phpepub": "~4.0", - "wallabag/php-mobi": "~1.0.0" + "wallabag/php-mobi": "~1.0.0", + "kphoen/rulerz-bundle": "~0.10" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2.0", diff --git a/composer.lock b/composer.lock index b7b5d142b..eea3f8a0d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "a9ec461e17166dcda1563dd55f6ff861", + "hash": "fed9468f6c830b0f81899daad7670af7", + "content-hash": "394f8a6ca5162f2d2756dbbee0ff5aae", "packages": [ { "name": "doctrine/annotations", @@ -1484,6 +1485,610 @@ ], "time": "2014-10-12 19:18:40" }, + { + "name": "hoa/compiler", + "version": "2.15.10.29", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Compiler.git", + "reference": "ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Compiler/zipball/ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39", + "reference": "ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0", + "hoa/file": "~0.0", + "hoa/iterator": "~1.0", + "hoa/math": "~0.0", + "hoa/regex": "~0.0", + "hoa/visitor": "~1.0" + }, + "require-dev": { + "hoa/json": "~1.0", + "hoa/test": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Compiler\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Compiler library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "algebraic", + "ast", + "compiler", + "context-free", + "coverage", + "exhaustive", + "grammar", + "isotropic", + "language", + "lexer", + "library", + "ll1", + "llk", + "parser", + "pp", + "random", + "regular", + "rule", + "sampler", + "syntax", + "token", + "trace", + "uniform" + ], + "time": "2015-10-29 21:35:12" + }, + { + "name": "hoa/core", + "version": "2.15.11.09", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Core.git", + "reference": "5538b1e90e2c66c90df5cc45e03fb85d047be900" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Core/zipball/5538b1e90e2c66c90df5cc45e03fb85d047be900", + "reference": "5538b1e90e2c66c90df5cc45e03fb85d047be900", + "shasum": "" + }, + "require": { + "ext-spl": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "suggest": { + "ext-mbstring": "ext/mbstring must be present (or a third implementation).", + "hoa/cli": "To use the `hoa` script." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Core\\": "." + }, + "files": [ + "Core.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Core library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "consistency", + "core", + "data", + "event", + "library", + "listener", + "parameter", + "protocol" + ], + "time": "2015-11-09 06:51:06" + }, + { + "name": "hoa/file", + "version": "0.15.11.09", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/File.git", + "reference": "f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/File/zipball/f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee", + "reference": "f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0", + "hoa/iterator": "~1.0", + "hoa/stream": "~0.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\File\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\File library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "Socket", + "directory", + "file", + "finder", + "library", + "link", + "temporary" + ], + "time": "2015-11-09 06:55:20" + }, + { + "name": "hoa/iterator", + "version": "1.15.10.29", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Iterator.git", + "reference": "a64ed9fd62579a34e4450134d6d1abdf77d54435" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Iterator/zipball/a64ed9fd62579a34e4450134d6d1abdf77d54435", + "reference": "a64ed9fd62579a34e4450134d6d1abdf77d54435", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Iterator\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Iterator library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "iterator", + "library" + ], + "time": "2015-10-29 21:37:16" + }, + { + "name": "hoa/math", + "version": "0.15.10.26", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Math.git", + "reference": "62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Math/zipball/62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e", + "reference": "62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e", + "shasum": "" + }, + "require": { + "hoa/compiler": "~2.0", + "hoa/core": "~2.0", + "hoa/iterator": "~1.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Math\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Math library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "arrangement", + "combination", + "combinatorics", + "counting", + "library", + "math", + "permutation", + "sampler", + "set" + ], + "time": "2015-10-26 15:22:52" + }, + { + "name": "hoa/regex", + "version": "0.15.08.13", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Regex.git", + "reference": "2ef8a77ef3885ca202fcd9c31a8e54c44cd04232" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Regex/zipball/2ef8a77ef3885ca202fcd9c31a8e54c44cd04232", + "reference": "2ef8a77ef3885ca202fcd9c31a8e54c44cd04232", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0", + "hoa/math": "~0.0", + "hoa/ustring": "~3.0", + "hoa/visitor": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Regex\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Regex library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "compiler", + "library", + "regex" + ], + "time": "2015-08-13 06:48:47" + }, + { + "name": "hoa/ruler", + "version": "1.15.11.09", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Ruler.git", + "reference": "9afc9ae032d40b6dc10bff85c9126cf516953925" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Ruler/zipball/9afc9ae032d40b6dc10bff85c9126cf516953925", + "reference": "9afc9ae032d40b6dc10bff85c9126cf516953925", + "shasum": "" + }, + "require": { + "hoa/compiler": "~2.0", + "hoa/core": "~2.0", + "hoa/file": "~0.0", + "hoa/visitor": "~1.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Ruler\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Ruler library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "library", + "ruler" + ], + "time": "2015-11-09 06:58:52" + }, + { + "name": "hoa/stream", + "version": "0.15.10.26", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Stream.git", + "reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Stream/zipball/011ab91d942f1d7096deade4c8a10fe57d51c5b3", + "reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Stream\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Stream library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "Context", + "bucket", + "composite", + "filter", + "in", + "library", + "out", + "protocol", + "stream", + "wrapper" + ], + "time": "2015-10-22 06:30:43" + }, + { + "name": "hoa/ustring", + "version": "3.15.11.09", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Ustring.git", + "reference": "8506be4910212b1a2beb9014763a8a4fbd871001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Ustring/zipball/8506be4910212b1a2beb9014763a8a4fbd871001", + "reference": "8506be4910212b1a2beb9014763a8a4fbd871001", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "suggest": { + "ext-iconv": "ext/iconv must be present (or a third implementation) to use Hoa\\Ustring::transcode().", + "ext-intl": "To get a better Hoa\\Ustring::toAscii() and Hoa\\Ustring::compareTo()." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Ustring\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Ustring library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "library", + "search", + "string", + "unicode" + ], + "time": "2015-11-09 06:44:33" + }, + { + "name": "hoa/visitor", + "version": "1.15.08.17", + "source": { + "type": "git", + "url": "https://github.com/hoaproject/Visitor.git", + "reference": "e30bfff741f71979f6476a41548e34afe8053c67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hoaproject/Visitor/zipball/e30bfff741f71979f6476a41548e34afe8053c67", + "reference": "e30bfff741f71979f6476a41548e34afe8053c67", + "shasum": "" + }, + "require": { + "hoa/core": "~2.0" + }, + "require-dev": { + "hoa/test": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hoa\\Visitor\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net" + }, + { + "name": "Hoa community", + "homepage": "http://hoa-project.net/" + } + ], + "description": "The Hoa\\Visitor library.", + "homepage": "http://hoa-project.net/", + "keywords": [ + "library", + "structure", + "visit", + "visitor" + ], + "time": "2015-08-17 06:30:58" + }, { "name": "htmlawed/htmlawed", "version": "1.1.19", @@ -1532,21 +2137,21 @@ }, { "name": "incenteev/composer-parameter-handler", - "version": "v2.1.1", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/Incenteev/ParameterHandler.git", - "reference": "84a205fe80a46101607bafbc423019527893ddd0" + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/84a205fe80a46101607bafbc423019527893ddd0", - "reference": "84a205fe80a46101607bafbc423019527893ddd0", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", "shasum": "" }, "require": { "php": ">=5.3.3", - "symfony/yaml": "~2.0" + "symfony/yaml": "~2.3|~3.0" }, "require-dev": { "composer/composer": "1.0.*@dev", @@ -1579,7 +2184,7 @@ "keywords": [ "parameters management" ], - "time": "2015-06-03 08:27:03" + "time": "2015-11-10 17:04:01" }, { "name": "j0k3r/graby", @@ -1671,20 +2276,19 @@ }, { "name": "j0k3r/php-readability", - "version": "v1.0.8", + "version": "v1.0.9", "source": { "type": "git", "url": "https://github.com/j0k3r/php-readability.git", - "reference": "f71c3a419623f821c245e0a003edfbf2c67f278e" + "reference": "41d7440c6e6130bacd50808342fe566e28f536fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/j0k3r/php-readability/zipball/f71c3a419623f821c245e0a003edfbf2c67f278e", - "reference": "f71c3a419623f821c245e0a003edfbf2c67f278e", + "url": "https://api.github.com/repos/j0k3r/php-readability/zipball/41d7440c6e6130bacd50808342fe566e28f536fb", + "reference": "41d7440c6e6130bacd50808342fe566e28f536fb", "shasum": "" }, "require": { - "ext-tidy": ">=1.2", "php": ">=5.3.3" }, "type": "library", @@ -1730,7 +2334,7 @@ "extraction", "html" ], - "time": "2015-09-23 19:09:38" + "time": "2015-11-10 08:55:29" }, { "name": "j0k3r/safecurl", @@ -2057,6 +2661,124 @@ ], "time": "2013-12-05 14:36:11" }, + { + "name": "kphoen/rulerz", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/K-Phoen/rulerz.git", + "reference": "608649b148ffdf3437600cc0f450d59b0579148d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/K-Phoen/rulerz/zipball/608649b148ffdf3437600cc0f450d59b0579148d", + "reference": "608649b148ffdf3437600cc0f450d59b0579148d", + "shasum": "" + }, + "require": { + "hoa/ruler": "~1.0", + "php": ">=5.4", + "symfony/property-access": "~2.3" + }, + "require-dev": { + "behat/behat": "~3.0", + "coduo/phpspec-data-provider-extension": "~1.0,!=1.0.2", + "doctrine/orm": "~2.4", + "elasticsearch/elasticsearch": "~1.0", + "illuminate/database": "~5.0", + "mikey179/vfsstream": "~1.4", + "phpspec/phpspec": "~2.0", + "pomm-project/cli": "~2.0@dev", + "pomm-project/foundation": "~2.0@dev", + "pomm-project/model-manager": "~2.0.@dev", + "ruflin/elastica": "~1.0", + "vlucas/phpdotenv": "~2.1" + }, + "suggest": { + "doctrine/orm": "To execute rules as Doctrine queries", + "elasticsearch/elasticsearch": "To execute rules as Elasticsearch queries", + "kphoen/rulerz-spec-builder": "If you want a specification builder", + "pomm-project/model-manager": "To execute rules as Pomm queries" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "RulerZ\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Gomez", + "email": "contact@kevingomez.fr" + } + ], + "description": "Powerful implementation of the Specification pattern", + "homepage": "https://github.com/K-Phoen/RulerZ", + "keywords": [ + "doctrine", + "specification" + ], + "time": "2015-10-31 20:54:37" + }, + { + "name": "kphoen/rulerz-bundle", + "version": "0.11.0", + "source": { + "type": "git", + "url": "https://github.com/K-Phoen/RulerZBundle.git", + "reference": "dcaaed69d8252fa1e3a25802f8cf697947570778" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/dcaaed69d8252fa1e3a25802f8cf697947570778", + "reference": "dcaaed69d8252fa1e3a25802f8cf697947570778", + "shasum": "" + }, + "require": { + "kphoen/rulerz": "~0.1, >=0.13.0", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/validator": "~2.3|~3.0" + }, + "require-dev": { + "matthiasnoback/symfony-dependency-injection-test": "~0.7", + "mikey179/vfsstream": "~1.0", + "phpunit/phpunit": "~4.8" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "KPhoen\\RulerZBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Gomez", + "email": "contact@kevingomez.fr" + } + ], + "description": "Symfony2 Bundle for RulerZ", + "homepage": "https://github.com/K-Phoen/RulerZBundle", + "keywords": [ + "doctrine", + "ruler", + "rulerz", + "specification" + ], + "time": "2015-11-13 13:00:14" + }, { "name": "kriswallsmith/assetic", "version": "v1.3.1", @@ -3014,25 +3736,25 @@ }, { "name": "sensio/framework-extra-bundle", - "version": "v3.0.10", + "version": "v3.0.11", "source": { "type": "git", "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", - "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c" + "reference": "a79e205737b58d557c05caef6dfa8f94d8084bca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/18fc2063c4d6569cdca47a39fbac32342eb65f3c", - "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/a79e205737b58d557c05caef6dfa8f94d8084bca", + "reference": "a79e205737b58d557c05caef6dfa8f94d8084bca", "shasum": "" }, "require": { "doctrine/common": "~2.2", - "symfony/framework-bundle": "~2.3" + "symfony/framework-bundle": "~2.3|~3.0" }, "require-dev": { - "symfony/expression-language": "~2.4", - "symfony/security-bundle": "~2.4" + "symfony/expression-language": "~2.4|~3.0", + "symfony/security-bundle": "~2.4|~3.0" }, "suggest": { "symfony/expression-language": "", @@ -3065,7 +3787,7 @@ "annotations", "controllers" ], - "time": "2015-08-03 11:59:27" + "time": "2015-10-28 15:47:04" }, { "name": "sensiolabs/security-checker", @@ -4516,16 +5238,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.8.16", + "version": "4.8.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e" + "reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e", - "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fa33d4ad96481b91df343d83e8c8aabed6b1dfd3", + "reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3", "shasum": "" }, "require": { @@ -4584,7 +5306,7 @@ "testing", "xunit" ], - "time": "2015-10-23 06:48:33" + "time": "2015-11-11 11:32:49" }, { "name": "phpunit/phpunit-mock-objects", diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php new file mode 100644 index 000000000..2cf3f8084 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php @@ -0,0 +1,66 @@ +setName('wallabag:tag:all') + ->setDescription('Tag all entries using the tagging rules.') + ->addArgument( + 'username', + InputArgument::REQUIRED, + 'User to tag entries for.' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + $user = $this->getUser($input->getArgument('username')); + } catch (NoResultException $e) { + $output->writeln(sprintf('User %s not found.', $input->getArgument('username'))); + + return 1; + } + $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger'); + + $output->write(sprintf('Tagging entries for user « %s »... ', $user->getUserName())); + + $entries = $tagger->tagAllForUser($user); + + $em = $this->getDoctrine()->getManager(); + foreach ($entries as $entry) { + $em->persist($entry); + } + $em->flush(); + + $output->writeln('Done.'); + } + + /** + * Fetches a user from its username. + * + * @param string $username + * + * @return \Wallabag\UserBundle\Entity\User + */ + private function getUser($username) + { + return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); + } + + private function getDoctrine() + { + return $this->getContainer()->get('doctrine'); + } +} diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 8bbe4ca06..7a187710f 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php @@ -7,9 +7,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; use Wallabag\CoreBundle\Entity\Config; +use Wallabag\CoreBundle\Entity\TaggingRule; use Wallabag\UserBundle\Entity\User; use Wallabag\CoreBundle\Form\Type\ChangePasswordType; use Wallabag\CoreBundle\Form\Type\UserInformationType; +use Wallabag\CoreBundle\Form\Type\TaggingRuleType; use Wallabag\CoreBundle\Form\Type\NewUserType; use Wallabag\CoreBundle\Form\Type\RssType; use Wallabag\CoreBundle\Tools\Utils; @@ -98,6 +100,24 @@ class ConfigController extends Controller return $this->redirect($this->generateUrl('config')); } + // handle tagging rule + $taggingRule = new TaggingRule(); + $newTaggingRule = $this->createForm(new TaggingRuleType(), $taggingRule, array('action' => $this->generateUrl('config').'#set5')); + $newTaggingRule->handleRequest($request); + + if ($newTaggingRule->isValid()) { + $taggingRule->setConfig($config); + $em->persist($taggingRule); + $em->flush(); + + $this->get('session')->getFlashBag()->add( + 'notice', + 'Tagging rules updated' + ); + + return $this->redirect($this->generateUrl('config')); + } + // handle adding new user $newUser = $userManager->createUser(); // enable created user by default @@ -136,6 +156,7 @@ class ConfigController extends Controller 'pwd' => $pwdForm->createView(), 'user' => $userForm->createView(), 'new_user' => $newUserForm->createView(), + 'new_tagging_rule' => $newTaggingRule->createView(), ), 'rss' => array( 'username' => $user->getUsername(), @@ -167,6 +188,33 @@ class ConfigController extends Controller return $request->headers->get('referer') ? $this->redirect($request->headers->get('referer')) : $this->redirectToRoute('config'); } + /** + * Deletes a tagging rule and redirect to the config homepage. + * + * @param TaggingRule $rule + * + * @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule") + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + */ + public function deleteTaggingRule(TaggingRule $rule) + { + if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) { + throw $this->createAccessDeniedException('You can not access this tagging ryle.'); + } + + $em = $this->getDoctrine()->getManager(); + $em->remove($rule); + $em->flush(); + + $this->get('session')->getFlashBag()->add( + 'notice', + 'Tagging rule deleted' + ); + + return $this->redirect($this->generateUrl('config')); + } + /** * Retrieve config for the current user. * If no config were found, create a new one. diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php index cb0c52c49..84b78a89b 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php @@ -6,6 +6,7 @@ use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Wallabag\CoreBundle\Entity\Config; +use Wallabag\CoreBundle\Entity\TaggingRule; class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface { @@ -15,6 +16,13 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface public function load(ObjectManager $manager) { $adminConfig = new Config($this->getReference('admin-user')); + $taggingRule = new TaggingRule(); + + $taggingRule->setConfig($adminConfig); + $taggingRule->setRule('title matches "wallabag"'); + $taggingRule->setTags(['wallabag']); + $manager->persist($taggingRule); + $adminConfig->setTheme('material'); $adminConfig->setItemsPerPage(30); $adminConfig->setLanguage('en_US'); diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php index b2a1915a4..2ca4182e6 100644 --- a/src/Wallabag/CoreBundle/Entity/Config.php +++ b/src/Wallabag/CoreBundle/Entity/Config.php @@ -2,6 +2,7 @@ namespace Wallabag\CoreBundle\Entity; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -76,12 +77,19 @@ class Config */ private $user; + /** + * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\TaggingRule", mappedBy="config", cascade={"remove"}) + * @ORM\OrderBy({"id" = "ASC"}) + */ + private $taggingRules; + /* * @param User $user */ public function __construct(\Wallabag\UserBundle\Entity\User $user) { $this->user = $user; + $this->taggingRules = new ArrayCollection(); } /** @@ -237,4 +245,24 @@ class Config { return $this->rssLimit; } + + /** + * @param TaggingRule $rule + * + * @return Config + */ + public function addTaggingRule(TaggingRule $rule) + { + $this->taggingRules[] = $rule; + + return $this; + } + + /** + * @return ArrayCollection + */ + public function getTaggingRules() + { + return $this->taggingRules; + } } diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 5aa582f8d..608ed2f0d 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php @@ -458,6 +458,10 @@ class Entry */ public function addTag(Tag $tag) { + if ($this->tags->contains($tag)) { + return; + } + $this->tags[] = $tag; $tag->addEntry($this); } diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php new file mode 100644 index 000000000..4eab590fd --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -0,0 +1,133 @@ +", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"} + * ) + * @ORM\Column(name="rule", type="string", nullable=false) + */ + private $rule; + + /** + * @var array + * + * @Assert\NotBlank() + * @ORM\Column(name="tags", type="simple_array", nullable=false) + */ + private $tags = []; + + /** + * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", inversedBy="taggingRules") + */ + private $config; + + /** + * Get id. + * + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * Set rule. + * + * @param string $rule + * + * @return TaggingRule + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule. + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set tags. + * + * @param array $tags + * + * @return TaggingRule + */ + public function setTags(array $tags) + { + $this->tags = $tags; + + return $this; + } + + /** + * Get tags. + * + * @return array + */ + public function getTags() + { + return $this->tags; + } + + /** + * Set config. + * + * @param Config $config + * + * @return TaggingRule + */ + public function setConfig(Config $config) + { + $this->config = $config; + + return $this; + } + + /** + * Get config. + * + * @return Config + */ + public function getConfig() + { + return $this->config; + } +} diff --git a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php new file mode 100644 index 000000000..23488d353 --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php @@ -0,0 +1,59 @@ +separator = $separator; + } + + /** + * Transforms a list to a string. + * + * @param array|null $list + * + * @return string + */ + public function transform($list) + { + if (null === $list) { + return ''; + } + + return implode($this->separator, $list); + } + + /** + * Transforms a string to a list. + * + * @param string $string + * + * @return array|null + */ + public function reverseTransform($string) + { + if ($string === null) { + return null; + } + + return array_values(array_filter(array_map('trim', explode($this->separator, $string)))); + } +} diff --git a/src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php b/src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php new file mode 100644 index 000000000..7fbba38ac --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php @@ -0,0 +1,38 @@ +add('rule', 'text', array('required' => true)) + ->add('save', 'submit') + ; + + $tagsField = $builder + ->create('tags', 'text') + ->addModelTransformer(new StringToListTransformer(',')); + + $builder->add($tagsField); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Wallabag\CoreBundle\Entity\TaggingRule', + )); + } + + public function getName() + { + return 'tagging_rule'; + } +} diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index 7fb413931..3d585e618 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php @@ -3,6 +3,7 @@ namespace Wallabag\CoreBundle\Helper; use Graby\Graby; +use Psr\Log\LoggerInterface as Logger; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Tools\Utils; @@ -13,10 +14,14 @@ use Wallabag\CoreBundle\Tools\Utils; class ContentProxy { protected $graby; + protected $tagger; + protected $logger; - public function __construct(Graby $graby) + public function __construct(Graby $graby, RuleBasedTagger $tagger, Logger $logger) { - $this->graby = $graby; + $this->graby = $graby; + $this->tagger = $tagger; + $this->logger = $logger; } /** @@ -59,6 +64,15 @@ class ContentProxy $entry->setPreviewPicture($content['open_graph']['og_image']); } + try { + $this->tagger->tag($entry); + } catch (\Exception $e) { + $this->logger->error('Error while trying to automatically tag an entry.', array( + 'entry_url' => $url, + 'error_msg' => $e->getMessage(), + )); + } + return $entry; } } diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php new file mode 100644 index 000000000..3f9953c09 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php @@ -0,0 +1,108 @@ +rulerz = $rulerz; + $this->tagRepository = $tagRepository; + $this->entryRepository = $entryRepository; + } + + /** + * Add tags from rules defined by the user. + * + * @param Entry $entry Entry to tag. + */ + public function tag(Entry $entry) + { + $rules = $this->getRulesForUser($entry->getUser()); + + foreach ($rules as $rule) { + if (!$this->rulerz->satisfies($entry, $rule->getRule())) { + continue; + } + + foreach ($rule->getTags() as $label) { + $tag = $this->getTag($entry->getUser(), $label); + + $entry->addTag($tag); + } + } + } + + /** + * Apply all the tagging rules defined by a user on its entries. + * + * @param User $user + * + * @return array A list of modified entries. + */ + public function tagAllForUser(User $user) + { + $rules = $this->getRulesForUser($user); + $entries = array(); + + foreach ($rules as $rule) { + $qb = $this->entryRepository->getBuilderForAllByUser($user->getId()); + $entries = $this->rulerz->filter($qb, $rule->getRule()); + + foreach ($entries as $entry) { + foreach ($rule->getTags() as $label) { + $tag = $this->getTag($user, $label); + + $entry->addTag($tag); + $entries[] = $entry; + } + } + } + + return $entries; + } + + /** + * Fetch a tag for a user. + * + * @param User $user + * @param string $label The tag's label. + * + * @return Tag + */ + private function getTag(User $user, $label) + { + $tag = $this->tagRepository->findOneByLabelAndUserId($label, $user->getId()); + + if (!$tag) { + $tag = new Tag($user); + $tag->setLabel($label); + } + + return $tag; + } + + /** + * Retrieves the tagging rules for a given user. + * + * @param User $user + * + * @return array + */ + private function getRulesForUser(User $user) + { + return $user->getConfig()->getTaggingRules(); + } +} diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php new file mode 100644 index 000000000..e6bb03b12 --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php @@ -0,0 +1,25 @@ + +

{% trans %}Tagging rules{% endtrans %}

+ +
    + {% for tagging_rule in app.user.config.taggingRules %} +
  • + if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} » + +
  • + {% endfor %} +
+ +
+ {{ form_errors(form.new_tagging_rule) }} + +
+
+ {{ form_label(form.new_tagging_rule.rule) }} + {{ form_errors(form.new_tagging_rule.rule) }} + {{ form_widget(form.new_tagging_rule.rule) }} +
+
+ +
+
+ {{ form_label(form.new_tagging_rule.tags) }} + {{ form_errors(form.new_tagging_rule.tags) }} + {{ form_widget(form.new_tagging_rule.tags) }} +
+
+ + {{ form_rest(form.new_tagging_rule) }} +
+ {% if is_granted('ROLE_SUPER_ADMIN') %}

{% trans %}Add a user{% endtrans %}

diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 8f121a2b7..d060311d4 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig @@ -15,8 +15,9 @@
  • {% trans %}RSS{% endtrans %}
  • {% trans %}User information{% endtrans %}
  • {% trans %}Password{% endtrans %}
  • +
  • {% trans %}Tagging rules{% endtrans %}
  • {% if is_granted('ROLE_SUPER_ADMIN') %} -
  • {% trans %}Add a user{% endtrans %}
  • +
  • {% trans %}Add a user{% endtrans %}
  • {% endif %} @@ -183,8 +184,155 @@ - {% if is_granted('ROLE_SUPER_ADMIN') %}
    +
    +
    +
      + {% for tagging_rule in app.user.config.taggingRules %} +
    • + if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} » + + + +
    • + {% endfor %} +
    +
    +
    + + {{ form_start(form.new_tagging_rule) }} + {{ form_errors(form.new_tagging_rule) }} + +
    +
    + {{ form_label(form.new_tagging_rule.rule) }} + {{ form_errors(form.new_tagging_rule.rule) }} + {{ form_widget(form.new_tagging_rule.rule) }} +
    +
    + +
    +
    + {{ form_label(form.new_tagging_rule.tags) }} + {{ form_errors(form.new_tagging_rule.tags) }} + {{ form_widget(form.new_tagging_rule.tags) }} +
    +
    + + + + + +
    +
    +

    {% trans %}FAQ{% endtrans %}

    + +
    {% trans %}What does « tagging rules » mean?{% endtrans %}
    +

    + {% trans %} + They are rules used by Wallabag to automatically tag new entries.
    + Each time a new entry is added, all the tagging rules will be used to add + the tags you configured, thus saving you the trouble to manually classify + your entries. + {% endtrans %} +

    + +
    {% trans %}How do I use them?{% endtrans %}
    +

    + {% trans %} + Let assume you want to tag new entries as « short reading » when the reading time is inferior to 3 minutes.
    + In that case, you should put « readingTime <= 3 » in the Rule field and « short reading » in the Tags + field.
    + Several tags can added simultaneously by separating them by a comma: « short reading, must read »
    + Complex rules can be written by using predefined operators: if « readingTime >= 5 AND domainName = "github.com" » then tag as « long reading, github » + {% endtrans %} +

    + +
    {% trans %}Which variables and operators can I use to write rules?{% endtrans %}
    +

    + {% trans %}The following variables and operators can be used to create tagging rules:{% endtrans %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {% trans %}Variable{% endtrans %}{% trans %}Meaning{% endtrans %}{% trans %}Operator{% endtrans %}{% trans %}Meaning{% endtrans %}
    title{% trans %}Title of the entry{% endtrans %}<={% trans %}Less than…{% endtrans %}
    url{% trans %}URL of the entry{% endtrans %}<{% trans %}Strictly less than…{% endtrans %}
    isArchived{% trans %}Whether the entry is archived or not{% endtrans %}=>{% trans %}Greater than…{% endtrans %}
    isStared{% trans %}Whether the entry is starred or not{% endtrans %}>{% trans %}Strictly greater than…{% endtrans %}
    content{% trans %}The entry's content{% endtrans %}={% trans %}Equal to…{% endtrans %}
    language{% trans %}The entry's language{% endtrans %}!={% trans %}Not equal to…{% endtrans %}
    mimetype{% trans %}The entry's mime-type{% endtrans %}OR{% trans %}One rule or another{% endtrans %}
    readingTime{% trans %}The estimated entry's reading time, in minutes{% endtrans %}AND{% trans %}One rule and another{% endtrans %}
    domainName{% trans %}The domain name of the entry{% endtrans %}matches + {% trans %} + Tests that a subject is matches a search (case-insensitive).
    + Example: title matches "football" + {% endtrans %} +
    +

    +
    +
    +
    + + {% if is_granted('ROLE_SUPER_ADMIN') %} +
    {{ form_start(form.new_user) }} {{ form_errors(form.new_user) }} diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php index 7085151ae..7b32354f8 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php @@ -479,4 +479,59 @@ class ConfigControllerTest extends WallabagCoreTestCase $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text'))); $this->assertContains($expectedMessage, $alert[0]); } + + public function testTaggingRuleCreation() + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/config'); + + $this->assertTrue($client->getResponse()->isSuccessful()); + + $form = $crawler->filter('button[id=tagging_rule_save]')->form(); + + $data = array( + 'tagging_rule[rule]' => 'readingTime <= 3', + 'tagging_rule[tags]' => 'short reading', + ); + + $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('Tagging rules updated', $alert[0]); + + $deleteLink = $crawler->filter('.delete')->last()->link(); + + $crawler = $client->click($deleteLink); + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $crawler = $client->followRedirect(); + $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text'))); + $this->assertContains('Tagging rule deleted', $alert[0]); + } + + public function dataForTaggingRuleFailed() + { + return array( + array( + array( + 'rss_config[rule]' => 'unknownVar <= 3', + 'rss_config[tags]' => 'cool tag', + ), + 'The variable « unknownVar » does not exist.', + ), + array( + array( + 'rss_config[rule]' => 'length(domainName) <= 42', + 'rss_config[tags]' => 'cool tag', + ), + 'The operator « length » does not exist.', + ), + ); + } } diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php index 56b4c9e41..af62aee8d 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php @@ -102,6 +102,44 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertContains('Google', $alert[0]); } + /** + * This test will require an internet connection. + */ + public function testPostNewThatWillBeTaggued() + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/new'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $form = $crawler->filter('button[type=submit]')->form(); + + $data = array( + 'entry[url]' => $url = 'https://github.com/wallabag/wallabag', + ); + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $crawler = $client->followRedirect(); + + $em = $client->getContainer() + ->get('doctrine.orm.entity_manager'); + $entry = $em + ->getRepository('WallabagCoreBundle:Entry') + ->findOneByUrl($url); + $tags = $entry->getTags(); + + $this->assertCount(1, $tags); + $this->assertEquals('wallabag', $tags[0]->getLabel()); + + $em->remove($entry); + $em->flush(); + } + public function testArchive() { $this->logInAs('admin'); diff --git a/src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php b/src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php new file mode 100644 index 000000000..d114e5f35 --- /dev/null +++ b/src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php @@ -0,0 +1,50 @@ +assertSame($expectedResult, $transformer->transform($inputData)); + } + + public function transformProvider() + { + return array( + array( null, '' ), + array( array(), '' ), + array( array('single value'), 'single value' ), + array( array('first value', 'second value'), 'first value,second value' ), + ); + } + + /** + * @dataProvider reverseTransformProvider + */ + public function testReverseTransformWithValidData($inputData, $expectedResult) + { + $transformer = new StringToListTransformer(); + + $this->assertSame($expectedResult, $transformer->reverseTransform($inputData)); + } + + public function reverseTransformProvider() + { + return array( + array( null, null ), + array( '', array() ), + array( 'single value', array('single value') ), + array( 'first value,second value', array('first value', 'second value') ), + array( 'first value, second value', array('first value', 'second value') ), + array( 'first value, , second value', array('first value', 'second value') ), + ); + } +} diff --git a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php index 4bce4708f..ef7cbd5b2 100644 --- a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php +++ b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php @@ -2,6 +2,9 @@ namespace Wallabag\CoreBundle\Tests\Helper; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Psr\Log\NullLogger; + use Wallabag\CoreBundle\Entity\Entry; use Wallabag\UserBundle\Entity\User; use Wallabag\CoreBundle\Helper\ContentProxy; @@ -10,6 +13,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase { public function testWithEmptyContent() { + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); + $graby = $this->getMockBuilder('Graby\Graby') ->setMethods(array('fetchContent')) ->disableOriginalConstructor() @@ -25,7 +32,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'language' => '', )); - $proxy = new ContentProxy($graby); + $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); $this->assertEquals('http://0.0.0.0', $entry->getUrl()); @@ -40,6 +47,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase public function testWithEmptyContentButOG() { + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); + $graby = $this->getMockBuilder('Graby\Graby') ->setMethods(array('fetchContent')) ->disableOriginalConstructor() @@ -59,7 +70,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby); + $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); $this->assertEquals('http://domain.io', $entry->getUrl()); @@ -74,6 +85,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase public function testWithContent() { + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); + $graby = $this->getMockBuilder('Graby\Graby') ->setMethods(array('fetchContent')) ->disableOriginalConstructor() @@ -94,7 +109,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby); + $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); $this->assertEquals('http://1.1.1.1', $entry->getUrl()); @@ -106,4 +121,17 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase $this->assertEquals(4.0, $entry->getReadingTime()); $this->assertEquals('1.1.1.1', $entry->getDomainName()); } + + private function getTaggerMock() + { + return $this->getMockBuilder('Wallabag\CoreBundle\Helper\RuleBasedTagger') + ->setMethods(array('tag')) + ->disableOriginalConstructor() + ->getMock(); + } + + private function getLogger() + { + return new NullLogger(); + } } diff --git a/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php new file mode 100644 index 000000000..5180f7ddb --- /dev/null +++ b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php @@ -0,0 +1,167 @@ +rulerz = $this->getRulerZMock(); + $this->tagRepository = $this->getTagRepositoryMock(); + $this->entryRepository = $this->getEntryRepositoryMock(); + + $this->tagger = new RuleBasedTagger($this->rulerz, $this->tagRepository, $this->entryRepository); + } + + public function testTagWithNoRule() + { + $entry = new Entry($this->getUser()); + + $this->tagger->tag($entry); + + $this->assertTrue($entry->getTags()->isEmpty()); + } + + public function testTagWithNoMatchingRule() + { + $taggingRule = $this->getTaggingRule('rule as string', array('foo', 'bar')); + $user = $this->getUser([$taggingRule]); + $entry = new Entry($user); + + $this->rulerz + ->expects($this->once()) + ->method('satisfies') + ->with($entry, 'rule as string') + ->willReturn(false); + + $this->tagger->tag($entry); + + $this->assertTrue($entry->getTags()->isEmpty()); + } + + public function testTagWithAMatchingRule() + { + $taggingRule = $this->getTaggingRule('rule as string', array('foo', 'bar')); + $user = $this->getUser([$taggingRule]); + $entry = new Entry($user); + + $this->rulerz + ->expects($this->once()) + ->method('satisfies') + ->with($entry, 'rule as string') + ->willReturn(true); + + $this->tagger->tag($entry); + + $this->assertFalse($entry->getTags()->isEmpty()); + + $tags = $entry->getTags(); + $this->assertSame('foo', $tags[0]->getLabel()); + $this->assertSame($user, $tags[0]->getUser()); + $this->assertSame('bar', $tags[1]->getLabel()); + $this->assertSame($user, $tags[1]->getUser()); + } + + public function testTagWithAMixOfMatchingRules() + { + $taggingRule = $this->getTaggingRule('bla bla', array('hey')); + $otherTaggingRule = $this->getTaggingRule('rule as string', array('foo')); + + $user = $this->getUser([$taggingRule, $otherTaggingRule]); + $entry = new Entry($user); + + $this->rulerz + ->method('satisfies') + ->will($this->onConsecutiveCalls(false, true)); + + $this->tagger->tag($entry); + + $this->assertFalse($entry->getTags()->isEmpty()); + + $tags = $entry->getTags(); + $this->assertSame('foo', $tags[0]->getLabel()); + $this->assertSame($user, $tags[0]->getUser()); + } + + public function testWhenTheTagExists() + { + $taggingRule = $this->getTaggingRule('rule as string', array('foo')); + $user = $this->getUser([$taggingRule]); + $entry = new Entry($user); + $tag = new Tag($user); + + $this->rulerz + ->expects($this->once()) + ->method('satisfies') + ->with($entry, 'rule as string') + ->willReturn(true); + + $this->tagRepository + ->expects($this->once()) + ->method('findOneByLabelAndUserId') + ->willReturn($tag); + + $this->tagger->tag($entry); + + $this->assertFalse($entry->getTags()->isEmpty()); + + $tags = $entry->getTags(); + $this->assertSame($tag, $tags[0]); + } + + private function getUser(array $taggingRules = []) + { + $user = new User(); + $config = new Config($user); + + $user->setConfig($config); + + foreach ($taggingRules as $rule) { + $config->addTaggingRule($rule); + } + + return $user; + } + + private function getTaggingRule($rule, array $tags) + { + $taggingRule = new TaggingRule(); + $taggingRule->setRule($rule); + $taggingRule->setTags($tags); + + return $taggingRule; + } + + private function getRulerZMock() + { + return $this->getMockBuilder('RulerZ\RulerZ') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getTagRepositoryMock() + { + return $this->getMockBuilder('Wallabag\CoreBundle\Repository\TagRepository') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getEntryRepositoryMock() + { + return $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index c020f3ca9..009c4881d 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php @@ -23,4 +23,19 @@ class UserRepository extends EntityRepository ->getQuery() ->getOneOrNullResult(); } + + /** + * Find a user by its username. + * + * @param string $username + * + * @return User + */ + public function findOneByUserName($username) + { + return $this->createQueryBuilder('u') + ->andWhere('u.username = :username')->setParameter('username', $username) + ->getQuery() + ->getSingleResult(); + } }