From c3510620ad0718d2ab1f856e3a838360a5ade314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 16:54:21 +0200 Subject: [PATCH 01/32] PoC of rule-based tagging --- app/AppKernel.php | 1 + composer.json | 3 +- composer.lock | 777 +++++++++++++++++- .../CoreBundle/Helper/ContentProxy.php | 8 +- .../CoreBundle/Helper/RuleBasedTagger.php | 82 ++ .../CoreBundle/Resources/config/services.yml | 14 + 6 files changed, 855 insertions(+), 30 deletions(-) create mode 100644 src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php 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/composer.json b/composer.json index b6a9c8541..ceefab02d 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": "dev-master" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2.0", diff --git a/composer.lock b/composer.lock index b7b5d142b..b29ac98ae 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": "657caf7a678eb246a7055756dfa47d5d", + "content-hash": "95a1be6941e76e6a72622f183f4c0425", "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": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/K-Phoen/RulerZBundle.git", + "reference": "86148898a052e349f880537c20d8102f617ebc17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/86148898a052e349f880537c20d8102f617ebc17", + "reference": "86148898a052e349f880537c20d8102f617ebc17", + "shasum": "" + }, + "require": { + "kphoen/rulerz": "~0.1, >=0.13.0", + "symfony/framework-bundle": "~2.3", + "symfony/validator": "~2.3" + }, + "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-01 11:57:49" + }, { "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", @@ -5118,7 +5840,8 @@ "minimum-stability": "dev", "stability-flags": { "friendsofsymfony/user-bundle": 20, - "friendsofsymfony/oauth-server-bundle": 20 + "friendsofsymfony/oauth-server-bundle": 20, + "kphoen/rulerz-bundle": 20 }, "prefer-stable": true, "prefer-lowest": false, diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index 7fb413931..dc6e1184c 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php @@ -13,10 +13,12 @@ use Wallabag\CoreBundle\Tools\Utils; class ContentProxy { protected $graby; + protected $tagger; - public function __construct(Graby $graby) + public function __construct(Graby $graby, RuleBasedTagger $tagger) { - $this->graby = $graby; + $this->graby = $graby; + $this->tagger = $tagger; } /** @@ -59,6 +61,8 @@ class ContentProxy $entry->setPreviewPicture($content['open_graph']['og_image']); } + $this->tagger->tag($entry); + return $entry; } } diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php new file mode 100644 index 000000000..012450b69 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php @@ -0,0 +1,82 @@ +rulerz = $rulerz; + $this->tagRepository = $tagRepository; + } + + /** + * 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['rule'])) { + continue; + } + + foreach ($rule['tags'] as $label) { + $tag = $this->getTag($entry->getUser(), $label); + + $entry->addTag($tag); + } + } + } + + /** + * 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; + } + + private function getRulesForUser(User $user) + { + return [ + [ + 'rule' => 'domainName = "github.com"', + 'tags' => ['github'], + ], + [ + 'rule' => 'readingTime >= 15', + 'tags' => ['long reading'], + ], + [ + 'rule' => 'readingTime <= 3 ', + 'tags' => ['short reading'], + ], + ]; + } +} diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 8e21b0528..4cf46b339 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -53,6 +53,20 @@ services: class: Wallabag\CoreBundle\Helper\ContentProxy arguments: - @wallabag_core.graby + - @wallabag_core.rule_based_tagger + + wallabag_core.rule_based_tagger: + class: Wallabag\CoreBundle\Helper\RuleBasedTagger + arguments: + - @rulerz + - @wallabag_core.tag_repository + + wallabag_core.tag_repository: + class: Wallabag\CoreBundle\Repository\TagRepository + factory_service: doctrine.orm.default_entity_manager + factory_method: getRepository + arguments: + - WallabagCoreBundle:Tag wallabag_core.registration_confirmed: class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener From ac9fec610a6485b39c856d9cb7d263ce8c5f1223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 17:14:50 +0200 Subject: [PATCH 02/32] Add TaggingRule entity --- src/Wallabag/CoreBundle/Entity/Config.php | 26 ++++ .../CoreBundle/Entity/TaggingRule.php | 128 ++++++++++++++++++ .../CoreBundle/Helper/RuleBasedTagger.php | 26 ++-- 3 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 src/Wallabag/CoreBundle/Entity/TaggingRule.php diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php index b2a1915a4..496fadb45 100644 --- a/src/Wallabag/CoreBundle/Entity/Config.php +++ b/src/Wallabag/CoreBundle/Entity/Config.php @@ -76,12 +76,18 @@ class Config */ private $user; + /** + * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\TaggingRule", mappedBy="config", cascade={"remove"}) + */ + private $taggingRules; + /* * @param User $user */ public function __construct(\Wallabag\UserBundle\Entity\User $user) { $this->user = $user; + $this->taggingRules = new ArrayCollection(); } /** @@ -237,4 +243,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/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php new file mode 100644 index 000000000..6d03a34da --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -0,0 +1,128 @@ +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/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php index 012450b69..bb9337797 100644 --- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php @@ -30,11 +30,11 @@ class RuleBasedTagger $rules = $this->getRulesForUser($entry->getUser()); foreach ($rules as $rule) { - if (!$this->rulerz->satisfies($entry, $rule['rule'])) { + if (!$this->rulerz->satisfies($entry, $rule->getRule())) { continue; } - foreach ($rule['tags'] as $label) { + foreach ($rule->getTags() as $label) { $tag = $this->getTag($entry->getUser(), $label); $entry->addTag($tag); @@ -62,21 +62,15 @@ class RuleBasedTagger return $tag; } + /** + * Retrieves the tagging rules for a given user. + * + * @param User $user + * + * @return array + */ private function getRulesForUser(User $user) { - return [ - [ - 'rule' => 'domainName = "github.com"', - 'tags' => ['github'], - ], - [ - 'rule' => 'readingTime >= 15', - 'tags' => ['long reading'], - ], - [ - 'rule' => 'readingTime <= 3 ', - 'tags' => ['short reading'], - ], - ]; + return $user->getConfig()->getTaggingRules(); } } From f19f9f62d13c62f18884e8bd0fa67403e8cad8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 17:30:58 +0200 Subject: [PATCH 03/32] Add a form to create tagging rules --- .../Controller/ConfigController.php | 21 ++++++++ .../StringToListTransformer.php | 49 +++++++++++++++++++ .../CoreBundle/Form/Type/TaggingRuleType.php | 38 ++++++++++++++ .../themes/material/Config/index.html.twig | 31 +++++++++++- 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php create mode 100644 src/Wallabag/CoreBundle/Form/Type/TaggingRuleType.php diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 8bbe4ca06..24b86344b 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); + $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(), diff --git a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php new file mode 100644 index 000000000..332a91b87 --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php @@ -0,0 +1,49 @@ +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) { + return null; + } + + return 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/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 8f121a2b7..d27a8ca6f 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 %}Tags{% endtrans %}
  • {% if is_granted('ROLE_SUPER_ADMIN') %} -
  • {% trans %}Add a user{% endtrans %}
  • +
  • {% trans %}Add a user{% endtrans %}
  • {% endif %} @@ -183,6 +184,34 @@ +
    +
    + {{ form_errors(form.pwd) }} + +
    +
    + {{ 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) }} +
    +
    + + + + +
    +
    + {% if is_granted('ROLE_SUPER_ADMIN') %}
    {{ form_start(form.new_user) }} From 9cbb404b4a2f76ad387ee31160bcf461048bc003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 17:37:05 +0200 Subject: [PATCH 04/32] Add missing tagging rule repository --- .../CoreBundle/Repository/TaggingRuleRepository.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php diff --git a/src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php b/src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php new file mode 100644 index 000000000..de3807388 --- /dev/null +++ b/src/Wallabag/CoreBundle/Repository/TaggingRuleRepository.php @@ -0,0 +1,9 @@ + Date: Sun, 11 Oct 2015 17:37:19 +0200 Subject: [PATCH 05/32] Display the tagging rules in the config --- .../views/themes/material/Config/index.html.twig | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 d27a8ca6f..1a8526f33 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 @@ -185,6 +185,18 @@
    +
    +
    +
      + {% for tagging_rule in app.user.config.taggingRules %} +
    • + if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} » +
    • + {% endfor %} +
    +
    +
    +
    {{ form_errors(form.pwd) }} From e9fbd2d12e94c96d540f6f98758f6bc92a65e7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 17:46:53 +0200 Subject: [PATCH 06/32] Add a table explaining the available variables --- .../themes/material/Config/index.html.twig | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) 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 1a8526f33..885718f82 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 @@ -220,8 +220,63 @@ - + +
    +
    +

    + {% trans %}The following variables can be used to create tagging rules:{% endtrans %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    VariableMeaning
    title{% trans %}Title of the entry{% endtrans %}
    url{% trans %}URL of the entry{% endtrans %}
    isArchived{% trans %}Whether the entry is archived or not{% endtrans %}
    isStared{% trans %}Whether the entry is starred or not{% endtrans %}
    content{% trans %}The entry's content{% endtrans %}
    language{% trans %}The entry's language{% endtrans %}
    mimetype{% trans %}The entry's mime-type{% endtrans %}
    readingTime{% trans %}The estimated entry's reading time, in minutes{% endtrans %}
    domainName{% trans %}The domain name of the entry{% endtrans %}
    +

    +
    +
    {% if is_granted('ROLE_SUPER_ADMIN') %} From 1d7b350b259ccc0110318a377ae3b3752ec6b9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 22:22:24 +0200 Subject: [PATCH 07/32] Add missing use statement --- src/Wallabag/CoreBundle/Entity/Config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php index 496fadb45..1204efa85 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; From f530f7f5e19eb611ba79856d3ca3b7aebd2af367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 11 Oct 2015 22:27:47 +0200 Subject: [PATCH 08/32] Fix ContentProxyTest --- .../Tests/Helper/ContentProxyTest.php | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php index 4bce4708f..1688a48a7 100644 --- a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php +++ b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php @@ -10,6 +10,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 +29,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'language' => '', )); - $proxy = new ContentProxy($graby); + $proxy = new ContentProxy($graby, $tagger); $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); $this->assertEquals('http://0.0.0.0', $entry->getUrl()); @@ -40,6 +44,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 +67,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby); + $proxy = new ContentProxy($graby, $tagger); $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); $this->assertEquals('http://domain.io', $entry->getUrl()); @@ -74,6 +82,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 +106,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby); + $proxy = new ContentProxy($graby, $tagger); $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); $this->assertEquals('http://1.1.1.1', $entry->getUrl()); @@ -106,4 +118,12 @@ 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(); + } } From 003fa77438b13ceb9ceda245d6ad257801453d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Mon, 12 Oct 2015 21:43:24 +0200 Subject: [PATCH 09/32] Add tests for the StringToListTransformer class --- .../StringToListTransformer.php | 14 +++++- .../StringToListTransformerTest.php | 50 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/Wallabag/CoreBundle/Tests/Form/DataTransformer/StringToListTransformerTest.php diff --git a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php index 332a91b87..23488d353 100644 --- a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php +++ b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php @@ -6,10 +6,20 @@ use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; +/** + * Transforms a comma-separated list to a proper PHP array. + * Example: the string "foo, bar" will become the array ["foo", "bar"] + */ class StringToListTransformer implements DataTransformerInterface { + /** + * @var string + */ private $separator; + /** + * @param string $separator The separator used in the list. + */ public function __construct($separator = ',') { $this->separator = $separator; @@ -40,10 +50,10 @@ class StringToListTransformer implements DataTransformerInterface */ public function reverseTransform($string) { - if (!$string) { + if ($string === null) { return null; } - return array_filter(array_map('trim', explode($this->separator, $string))); + return array_values(array_filter(array_map('trim', explode($this->separator, $string)))); } } 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') ), + ); + } +} From 71ef0ed2542e3dff3b64910ef9e3543fa568ffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 14 Oct 2015 20:24:02 +0200 Subject: [PATCH 10/32] =?UTF-8?q?Rename=20the=20=C2=AB=20Tags=20=C2=BB=20t?= =?UTF-8?q?ab=20to=20=C2=AB=20Tagging=20rules=20=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/views/themes/material/Config/index.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 885718f82..993c58265 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,7 +15,7 @@
  • {% trans %}RSS{% endtrans %}
  • {% trans %}User information{% endtrans %}
  • {% trans %}Password{% endtrans %}
  • -
  • {% trans %}Tags{% endtrans %}
  • +
  • {% trans %}Tagging rules{% endtrans %}
  • {% if is_granted('ROLE_SUPER_ADMIN') %}
  • {% trans %}Add a user{% endtrans %}
  • {% endif %} From 3447d1ee07bb64e57e69164127529f957dd47822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 14 Oct 2015 22:48:58 +0200 Subject: [PATCH 11/32] =?UTF-8?q?Add=20na=C3=AFve=20validation=20for=20tag?= =?UTF-8?q?ging=20rules=20(only=20checks=20the=20syntax)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Wallabag/CoreBundle/Entity/TaggingRule.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 6d03a34da..20c152582 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -4,6 +4,7 @@ namespace Wallabag\CoreBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; +use KPhoen\RulerZBundle\Validator\Constraints as RulerZAssert; /** * Config. @@ -27,6 +28,7 @@ class TaggingRule * @var string * * @Assert\NotBlank() + * @RulerZAssert\ValidRule() * @ORM\Column(name="rule", type="string", nullable=false) */ private $rule; From 1dc4e5da2e281ee8acc69ff025c42369cf778387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 17 Oct 2015 16:57:41 +0200 Subject: [PATCH 12/32] Also validate used variables when creating tagging rules --- src/Wallabag/CoreBundle/Entity/TaggingRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 20c152582..97a29336e 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -28,7 +28,7 @@ class TaggingRule * @var string * * @Assert\NotBlank() - * @RulerZAssert\ValidRule() + * @RulerZAssert\ValidRule(allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}) * @ORM\Column(name="rule", type="string", nullable=false) */ private $rule; From 1c9cd2a7f03a66a1ae5132ff270e94a7fe6eb9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 17 Oct 2015 17:45:51 +0200 Subject: [PATCH 13/32] Errors in the automatic tagging do not prevent the entry from being added --- src/Wallabag/CoreBundle/Helper/ContentProxy.php | 14 ++++++++++++-- .../CoreBundle/Resources/config/services.yml | 1 + .../CoreBundle/Tests/Helper/ContentProxyTest.php | 11 ++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index dc6e1184c..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; @@ -14,11 +15,13 @@ class ContentProxy { protected $graby; protected $tagger; + protected $logger; - public function __construct(Graby $graby, RuleBasedTagger $tagger) + public function __construct(Graby $graby, RuleBasedTagger $tagger, Logger $logger) { $this->graby = $graby; $this->tagger = $tagger; + $this->logger = $logger; } /** @@ -61,7 +64,14 @@ class ContentProxy $entry->setPreviewPicture($content['open_graph']['og_image']); } - $this->tagger->tag($entry); + 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/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 4cf46b339..0100a62d9 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -54,6 +54,7 @@ services: arguments: - @wallabag_core.graby - @wallabag_core.rule_based_tagger + - @logger wallabag_core.rule_based_tagger: class: Wallabag\CoreBundle\Helper\RuleBasedTagger diff --git a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php index 1688a48a7..f59edb0c9 100644 --- a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php +++ b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php @@ -29,7 +29,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'language' => '', )); - $proxy = new ContentProxy($graby, $tagger); + $proxy = new ContentProxy($graby, $tagger, $this->getLoggerMock()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); $this->assertEquals('http://0.0.0.0', $entry->getUrl()); @@ -67,7 +67,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby, $tagger); + $proxy = new ContentProxy($graby, $tagger, $this->getLoggerMock()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); $this->assertEquals('http://domain.io', $entry->getUrl()); @@ -106,7 +106,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby, $tagger); + $proxy = new ContentProxy($graby, $tagger, $this->getLoggerMock()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); $this->assertEquals('http://1.1.1.1', $entry->getUrl()); @@ -126,4 +126,9 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); } + + private function getLoggerMock() + { + return $this->getMock('Psr\Log\LoggerInterface'); + } } From 5a166c5c1af645b1b1ba77ba5f2e24094e7b60e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 17 Oct 2015 18:34:18 +0200 Subject: [PATCH 14/32] Add tests for the RuleBasedTagger class --- .../Tests/Helper/RuleBasedTaggerTest.php | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php diff --git a/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php new file mode 100644 index 000000000..56a1ed61e --- /dev/null +++ b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php @@ -0,0 +1,158 @@ +rulerz = $this->getRulerZMock(); + $this->repository = $this->getTagRepositoryMock(); + + $this->tagger = new RuleBasedTagger($this->rulerz, $this->repository); + } + + 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->repository + ->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(); + } +} From c23fc05df86c3cdb8eb40994b98d057f4a81b02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 18 Oct 2015 14:32:58 +0200 Subject: [PATCH 15/32] Validate used operators when creating tagging rules --- src/Wallabag/CoreBundle/Entity/TaggingRule.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 97a29336e..472ae5824 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -28,7 +28,10 @@ class TaggingRule * @var string * * @Assert\NotBlank() - * @RulerZAssert\ValidRule(allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}) + * @RulerZAssert\ValidRule( + * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, + * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or"} + * ) * @ORM\Column(name="rule", type="string", nullable=false) */ private $rule; From cad8cda7af06234a63b86253da1d813e7b0fd0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 18 Oct 2015 15:18:12 +0200 Subject: [PATCH 16/32] Use a tagged version of kphoen/rulerz-bundle --- composer.json | 2 +- composer.lock | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index ceefab02d..a9c734ab5 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ "scheb/two-factor-bundle": "~1.4", "grandt/phpepub": "~4.0", "wallabag/php-mobi": "~1.0.0", - "kphoen/rulerz-bundle": "dev-master" + "kphoen/rulerz-bundle": "~0.7,>=0.7.2" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2.0", diff --git a/composer.lock b/composer.lock index b29ac98ae..b88c56e86 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +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": "657caf7a678eb246a7055756dfa47d5d", - "content-hash": "95a1be6941e76e6a72622f183f4c0425", + "hash": "8719c5ec3c13338ff792ce5ed8d12926", + "content-hash": "3f28af906ef3d4eaac41583bfcc5bd85", "packages": [ { "name": "doctrine/annotations", @@ -2731,16 +2731,16 @@ }, { "name": "kphoen/rulerz-bundle", - "version": "dev-master", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/K-Phoen/RulerZBundle.git", - "reference": "86148898a052e349f880537c20d8102f617ebc17" + "reference": "5d10ab2093b2192619c83333506c574d726cee07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/86148898a052e349f880537c20d8102f617ebc17", - "reference": "86148898a052e349f880537c20d8102f617ebc17", + "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/5d10ab2093b2192619c83333506c574d726cee07", + "reference": "5d10ab2093b2192619c83333506c574d726cee07", "shasum": "" }, "require": { @@ -2777,7 +2777,7 @@ "rulerz", "specification" ], - "time": "2015-11-01 11:57:49" + "time": "2015-11-01 11:57:02" }, { "name": "kriswallsmith/assetic", @@ -5840,8 +5840,7 @@ "minimum-stability": "dev", "stability-flags": { "friendsofsymfony/user-bundle": 20, - "friendsofsymfony/oauth-server-bundle": 20, - "kphoen/rulerz-bundle": 20 + "friendsofsymfony/oauth-server-bundle": 20 }, "prefer-stable": true, "prefer-lowest": false, From 625acf335298186b4ff983f9321900d1238e854b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 24 Oct 2015 15:11:06 +0200 Subject: [PATCH 17/32] Add a command to automatically tag all entries for a user --- app/config/config.yml | 4 ++ composer.json | 2 +- composer.lock | 4 +- .../CoreBundle/Command/TagAllCommand.php | 66 +++++++++++++++++++ src/Wallabag/CoreBundle/Entity/Entry.php | 4 ++ .../CoreBundle/Helper/RuleBasedTagger.php | 38 ++++++++++- .../CoreBundle/Resources/config/services.yml | 8 +++ .../Tests/Helper/RuleBasedTaggerTest.php | 19 ++++-- .../UserBundle/Repository/UserRepository.php | 15 +++++ 9 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 src/Wallabag/CoreBundle/Command/TagAllCommand.php 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 a9c734ab5..bf40e471d 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ "scheb/two-factor-bundle": "~1.4", "grandt/phpepub": "~4.0", "wallabag/php-mobi": "~1.0.0", - "kphoen/rulerz-bundle": "~0.7,>=0.7.2" + "kphoen/rulerz-bundle": "~0.7,>=0.7.3" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2.0", diff --git a/composer.lock b/composer.lock index b88c56e86..c470656ad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +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": "8719c5ec3c13338ff792ce5ed8d12926", - "content-hash": "3f28af906ef3d4eaac41583bfcc5bd85", + "hash": "a1f0e0b886031d96a9756de581d7d557", + "content-hash": "d0a24cdd93bd431af386715f24e3e389", "packages": [ { "name": "doctrine/annotations", 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/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/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php index bb9337797..3f9953c09 100644 --- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php @@ -6,6 +6,7 @@ use RulerZ\RulerZ; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; +use Wallabag\CoreBundle\Repository\EntryRepository; use Wallabag\CoreBundle\Repository\TagRepository; use Wallabag\UserBundle\Entity\User; @@ -13,11 +14,13 @@ class RuleBasedTagger { private $rulerz; private $tagRepository; + private $entryRepository; - public function __construct(RulerZ $rulerz, TagRepository $tagRepository) + public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository) { - $this->rulerz = $rulerz; - $this->tagRepository = $tagRepository; + $this->rulerz = $rulerz; + $this->tagRepository = $tagRepository; + $this->entryRepository = $entryRepository; } /** @@ -42,6 +45,35 @@ class RuleBasedTagger } } + /** + * 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. * diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 0100a62d9..cef4450d1 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -61,6 +61,14 @@ services: arguments: - @rulerz - @wallabag_core.tag_repository + - @wallabag_core.entry_repository + + wallabag_core.entry_repository: + class: Wallabag\CoreBundle\Repository\EntryRepository + factory_service: doctrine.orm.default_entity_manager + factory_method: getRepository + arguments: + - WallabagCoreBundle:Entry wallabag_core.tag_repository: class: Wallabag\CoreBundle\Repository\TagRepository diff --git a/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php index 56a1ed61e..5180f7ddb 100644 --- a/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Helper/RuleBasedTaggerTest.php @@ -12,15 +12,17 @@ use Wallabag\CoreBundle\Helper\RuleBasedTagger; class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase { private $rulerz; - private $repository; + private $tagRepository; + private $entryRepository; private $tagger; public function setUp() { - $this->rulerz = $this->getRulerZMock(); - $this->repository = $this->getTagRepositoryMock(); + $this->rulerz = $this->getRulerZMock(); + $this->tagRepository = $this->getTagRepositoryMock(); + $this->entryRepository = $this->getEntryRepositoryMock(); - $this->tagger = new RuleBasedTagger($this->rulerz, $this->repository); + $this->tagger = new RuleBasedTagger($this->rulerz, $this->tagRepository, $this->entryRepository); } public function testTagWithNoRule() @@ -106,7 +108,7 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase ->with($entry, 'rule as string') ->willReturn(true); - $this->repository + $this->tagRepository ->expects($this->once()) ->method('findOneByLabelAndUserId') ->willReturn($tag); @@ -155,4 +157,11 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase ->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(); + } } From 52e423f30761561fa2ad82c708d9fa9186562b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 25 Oct 2015 10:45:15 +0100 Subject: [PATCH 18/32] Provide a way to delete tagging rules --- .../Controller/ConfigController.php | 27 +++++++++++++++++++ .../themes/material/Config/index.html.twig | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 24b86344b..de1536c91 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php @@ -188,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/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 993c58265..1457fe51e 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 @@ -191,6 +191,9 @@ {% for tagging_rule in app.user.config.taggingRules %}
  • if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} » + + +
  • {% endfor %} From 9b88658c047514a75bc9a9e3578cbeba32c5c5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 25 Oct 2015 14:34:43 +0100 Subject: [PATCH 19/32] Update baggy theme --- .../views/themes/baggy/Config/index.html.twig | 33 +++++++++++++++++++ .../themes/material/Config/index.html.twig | 4 +-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig index 7a7d6af1f..cc797c632 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig @@ -145,6 +145,39 @@ {{ form_rest(form.pwd) }} +

    {% 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 1457fe51e..810c02fa1 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 @@ -200,8 +200,8 @@ -
    - {{ form_errors(form.pwd) }} + + {{ form_errors(form.new_tagging_rule) }}
    From 8a99c7a86b138faee5dc92ab6ecbd281dbd19451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 31 Oct 2015 16:21:56 +0100 Subject: [PATCH 20/32] Add a few functional tests for the tagging rules creation form --- .../Tests/Controller/ConfigControllerTest.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php index 7085151ae..4eb67ffdc 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')->eq(0)->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.', + ), + ); + } } From 7b1648961d2a9770e801f414d297efe3aace07e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 31 Oct 2015 16:29:38 +0100 Subject: [PATCH 21/32] Fix incorrect comment. --- src/Wallabag/CoreBundle/Entity/TaggingRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 472ae5824..851af9329 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -7,7 +7,7 @@ use Symfony\Component\Validator\Constraints as Assert; use KPhoen\RulerZBundle\Validator\Constraints as RulerZAssert; /** - * Config. + * Tagging rule. * * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\TaggingRuleRepository") * @ORM\Table From 0c5bcd82bafbc91820857f7647186431273bc483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sat, 31 Oct 2015 16:38:49 +0100 Subject: [PATCH 22/32] Use Psr\Log\NullLogger instead of creating a mock --- .../CoreBundle/Tests/Helper/ContentProxyTest.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php b/src/Wallabag/CoreBundle/Tests/Helper/ContentProxyTest.php index f59edb0c9..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; @@ -29,7 +32,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'language' => '', )); - $proxy = new ContentProxy($graby, $tagger, $this->getLoggerMock()); + $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()); @@ -67,7 +70,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby, $tagger, $this->getLoggerMock()); + $proxy = new ContentProxy($graby, $tagger, $this->getLogger()); $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); $this->assertEquals('http://domain.io', $entry->getUrl()); @@ -106,7 +109,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ), )); - $proxy = new ContentProxy($graby, $tagger, $this->getLoggerMock()); + $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()); @@ -127,8 +130,8 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ->getMock(); } - private function getLoggerMock() + private function getLogger() { - return $this->getMock('Psr\Log\LoggerInterface'); + return new NullLogger(); } } From 6cbbf1481ac56bcd5c9bbf7fe3d7dbb0d3bf1745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 11 Nov 2015 16:39:28 +0100 Subject: [PATCH 23/32] Update rulerz-bundle --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index bf40e471d..949f7e32b 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ "scheb/two-factor-bundle": "~1.4", "grandt/phpepub": "~4.0", "wallabag/php-mobi": "~1.0.0", - "kphoen/rulerz-bundle": "~0.7,>=0.7.3" + "kphoen/rulerz-bundle": "~0.9,>=0.9.1" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2.0", diff --git a/composer.lock b/composer.lock index c470656ad..9a4397bad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +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": "a1f0e0b886031d96a9756de581d7d557", - "content-hash": "d0a24cdd93bd431af386715f24e3e389", + "hash": "9372aa161ce94f8b5506d7e9cc73a884", + "content-hash": "e703c21d623d708ff9528c298d7ab05f", "packages": [ { "name": "doctrine/annotations", @@ -2731,16 +2731,16 @@ }, { "name": "kphoen/rulerz-bundle", - "version": "0.9.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/K-Phoen/RulerZBundle.git", - "reference": "5d10ab2093b2192619c83333506c574d726cee07" + "reference": "0d8d6ca57ebf2c914c967bc13cdf13c5ebb2de62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/5d10ab2093b2192619c83333506c574d726cee07", - "reference": "5d10ab2093b2192619c83333506c574d726cee07", + "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/0d8d6ca57ebf2c914c967bc13cdf13c5ebb2de62", + "reference": "0d8d6ca57ebf2c914c967bc13cdf13c5ebb2de62", "shasum": "" }, "require": { @@ -2777,7 +2777,7 @@ "rulerz", "specification" ], - "time": "2015-11-01 11:57:02" + "time": "2015-11-11 15:37:07" }, { "name": "kriswallsmith/assetic", From b7b20054947676ab0d4c89c3c4a64db4e52db203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 11 Nov 2015 16:44:57 +0100 Subject: [PATCH 24/32] Fix the creation of the repository services --- src/Wallabag/CoreBundle/Resources/config/services.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index cef4450d1..03d335609 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -65,15 +65,13 @@ services: wallabag_core.entry_repository: class: Wallabag\CoreBundle\Repository\EntryRepository - factory_service: doctrine.orm.default_entity_manager - factory_method: getRepository + factory: [ @doctrine.orm.default_entity_manager, getRepository ] arguments: - WallabagCoreBundle:Entry wallabag_core.tag_repository: class: Wallabag\CoreBundle\Repository\TagRepository - factory_service: doctrine.orm.default_entity_manager - factory_method: getRepository + factory: [ @doctrine.orm.default_entity_manager, getRepository ] arguments: - WallabagCoreBundle:Tag From 5c514b0be320d683c22a3044d875a5e4b5fe6ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Wed, 11 Nov 2015 17:06:36 +0100 Subject: [PATCH 25/32] Improve the tagging rules documentation --- .../themes/material/Config/index.html.twig | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) 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 810c02fa1..a6fd2f11d 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 @@ -227,14 +227,40 @@
    +

    {% trans %}FAQ{% endtrans %}

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

    - {% trans %}The following variables can be used to create tagging rules:{% 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 %} - - + + + + @@ -242,38 +268,56 @@ + + + + + + + + + + + + + + + + + +
    VariableMeaning{% 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 %}
    From a6e27f74663637ecc4a4cf84028e6b5a3556a6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Fri, 13 Nov 2015 14:37:58 +0100 Subject: [PATCH 26/32] Add matches operator --- composer.json | 2 +- composer.lock | 18 +++++++++--------- src/Wallabag/CoreBundle/Entity/TaggingRule.php | 2 +- .../CoreBundle/Operator/Doctrine/Matches.php | 15 +++++++++++++++ .../CoreBundle/Operator/PHP/Matches.php | 11 +++++++++++ .../CoreBundle/Resources/config/services.yml | 10 ++++++++++ 6 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php create mode 100644 src/Wallabag/CoreBundle/Operator/PHP/Matches.php diff --git a/composer.json b/composer.json index 949f7e32b..8fc5ce5bf 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ "scheb/two-factor-bundle": "~1.4", "grandt/phpepub": "~4.0", "wallabag/php-mobi": "~1.0.0", - "kphoen/rulerz-bundle": "~0.9,>=0.9.1" + "kphoen/rulerz-bundle": "~0.10" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2.0", diff --git a/composer.lock b/composer.lock index 9a4397bad..eea3f8a0d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +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": "9372aa161ce94f8b5506d7e9cc73a884", - "content-hash": "e703c21d623d708ff9528c298d7ab05f", + "hash": "fed9468f6c830b0f81899daad7670af7", + "content-hash": "394f8a6ca5162f2d2756dbbee0ff5aae", "packages": [ { "name": "doctrine/annotations", @@ -2731,22 +2731,22 @@ }, { "name": "kphoen/rulerz-bundle", - "version": "0.9.1", + "version": "0.11.0", "source": { "type": "git", "url": "https://github.com/K-Phoen/RulerZBundle.git", - "reference": "0d8d6ca57ebf2c914c967bc13cdf13c5ebb2de62" + "reference": "dcaaed69d8252fa1e3a25802f8cf697947570778" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/0d8d6ca57ebf2c914c967bc13cdf13c5ebb2de62", - "reference": "0d8d6ca57ebf2c914c967bc13cdf13c5ebb2de62", + "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", - "symfony/validator": "~2.3" + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/validator": "~2.3|~3.0" }, "require-dev": { "matthiasnoback/symfony-dependency-injection-test": "~0.7", @@ -2777,7 +2777,7 @@ "rulerz", "specification" ], - "time": "2015-11-11 15:37:07" + "time": "2015-11-13 13:00:14" }, { "name": "kriswallsmith/assetic", diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 851af9329..4eab590fd 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -30,7 +30,7 @@ class TaggingRule * @Assert\NotBlank() * @RulerZAssert\ValidRule( * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, - * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or"} + * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"} * ) * @ORM\Column(name="rule", type="string", nullable=false) */ diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php new file mode 100644 index 000000000..dc47c982a --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php @@ -0,0 +1,15 @@ + Date: Fri, 13 Nov 2015 20:48:51 +0100 Subject: [PATCH 27/32] Add phpdoc for all Matches implementations --- src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php | 10 ++++++++++ src/Wallabag/CoreBundle/Operator/PHP/Matches.php | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php index dc47c982a..e6bb03b12 100644 --- a/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php @@ -2,6 +2,16 @@ namespace Wallabag\CoreBundle\Operator\Doctrine; +/** + * Provides a "matches" operator used for tagging rules. + * + * It asserts that a given pattern is contained in a subject, in a + * case-insensitive way. + * + * This operator will be used to compile tagging rules in DQL, usable + * by Doctrine ORM. + * It's registered in RulerZ using a service (wallabag.operator.doctrine.matches); + */ class Matches { public function __invoke($subject, $pattern) diff --git a/src/Wallabag/CoreBundle/Operator/PHP/Matches.php b/src/Wallabag/CoreBundle/Operator/PHP/Matches.php index 4768900ce..987ed2a50 100644 --- a/src/Wallabag/CoreBundle/Operator/PHP/Matches.php +++ b/src/Wallabag/CoreBundle/Operator/PHP/Matches.php @@ -2,6 +2,16 @@ namespace Wallabag\CoreBundle\Operator\PHP; +/** + * Provides a "matches" operator used for tagging rules. + * + * It asserts that a given pattern is contained in a subject, in a + * case-insensitive way. + * + * This operator will be used to compile tagging rules in PHP, usable + * directly on Entry objects for instance. + * It's registered in RulerZ using a service (wallabag.operator.array.matches); + */ class Matches { public function __invoke($subject, $pattern) From aeff8aa765579259c1427dc469ff78ebdebfc72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Fri, 13 Nov 2015 20:57:46 +0100 Subject: [PATCH 28/32] Document the matches operator in the FAQ --- .../views/themes/material/Config/index.html.twig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 a6fd2f11d..e08393a23 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 @@ -316,8 +316,13 @@ 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 %} + From 958671a7ae2f19c4b919ed4ba0161e7d300411b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Fri, 13 Nov 2015 21:23:39 +0100 Subject: [PATCH 29/32] Add a quick test --- .../DataFixtures/ORM/LoadConfigData.php | 8 +++++ .../Tests/Controller/ConfigControllerTest.php | 2 +- .../Tests/Controller/EntryControllerTest.php | 35 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) 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/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php index 4eb67ffdc..7b32354f8 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php @@ -505,7 +505,7 @@ class ConfigControllerTest extends WallabagCoreTestCase $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text'))); $this->assertContains('Tagging rules updated', $alert[0]); - $deleteLink = $crawler->filter('.delete')->eq(0)->link(); + $deleteLink = $crawler->filter('.delete')->last()->link(); $crawler = $client->click($deleteLink); $this->assertEquals(302, $client->getResponse()->getStatusCode()); diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php index 56b4c9e41..9f2bcd6cf 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php @@ -102,6 +102,41 @@ 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); + $this->assertCount(1, $entry->getTags()); + + $em->remove($entry); + $em->flush(); + } + public function testArchive() { $this->logInAs('admin'); From 69edb774eb360ea33646fcc81cd4ea71fa137680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Mon, 16 Nov 2015 13:34:00 +0100 Subject: [PATCH 30/32] Assert that the tag has is the good one --- .../CoreBundle/Tests/Controller/EntryControllerTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php index 9f2bcd6cf..af62aee8d 100644 --- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php +++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php @@ -131,7 +131,10 @@ class EntryControllerTest extends WallabagCoreTestCase $entry = $em ->getRepository('WallabagCoreBundle:Entry') ->findOneByUrl($url); - $this->assertCount(1, $entry->getTags()); + $tags = $entry->getTags(); + + $this->assertCount(1, $tags); + $this->assertEquals('wallabag', $tags[0]->getLabel()); $em->remove($entry); $em->flush(); From c13eda461f88ba16c88d7c398322a59b294e6c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Mon, 16 Nov 2015 14:01:43 +0100 Subject: [PATCH 31/32] Clean the tagging rule creation form --- src/Wallabag/CoreBundle/Controller/ConfigController.php | 2 +- .../Resources/views/themes/material/Config/index.html.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index de1536c91..7a187710f 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php @@ -102,7 +102,7 @@ class ConfigController extends Controller // handle tagging rule $taggingRule = new TaggingRule(); - $newTaggingRule = $this->createForm(new TaggingRuleType(), $taggingRule); + $newTaggingRule = $this->createForm(new TaggingRuleType(), $taggingRule, array('action' => $this->generateUrl('config').'#set5')); $newTaggingRule->handleRequest($request); if ($newTaggingRule->isValid()) { 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 e08393a23..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 @@ -200,7 +200,7 @@

    - + {{ form_start(form.new_tagging_rule) }} {{ form_errors(form.new_tagging_rule) }}
    @@ -332,7 +332,7 @@
    {% if is_granted('ROLE_SUPER_ADMIN') %} -
    +
    {{ form_start(form.new_user) }} {{ form_errors(form.new_user) }} From 752b90d1f2e279d3662d5431b09c7587df2937ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Sun, 29 Nov 2015 16:19:02 +0100 Subject: [PATCH 32/32] Fix tagging rules ordering --- src/Wallabag/CoreBundle/Entity/Config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php index 1204efa85..2ca4182e6 100644 --- a/src/Wallabag/CoreBundle/Entity/Config.php +++ b/src/Wallabag/CoreBundle/Entity/Config.php @@ -79,6 +79,7 @@ class Config /** * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\TaggingRule", mappedBy="config", cascade={"remove"}) + * @ORM\OrderBy({"id" = "ASC"}) */ private $taggingRules;