Merge pull request #1478 from K-Phoen/rule-based-tags

Rule based tags
This commit is contained in:
Jeremy Benoist 2015-12-06 14:31:26 +01:00
commit a7f1921f7d
26 changed files with 1888 additions and 34 deletions

View file

@ -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'))) {

View file

@ -189,3 +189,7 @@ scheb_two_factor:
sender_email: %twofactor_sender%
digits: 6
template: WallabagUserBundle:Authentication:form.html.twig
kphoen_rulerz:
executors:
doctrine: true

View file

@ -57,7 +57,8 @@
"friendsofsymfony/oauth-server-bundle": "^1.4@dev",
"scheb/two-factor-bundle": "~1.4",
"grandt/phpepub": "~4.0",
"wallabag/php-mobi": "~1.0.0"
"wallabag/php-mobi": "~1.0.0",
"kphoen/rulerz-bundle": "~0.10"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "~2.2.0",

774
composer.lock generated
View file

@ -4,7 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "a9ec461e17166dcda1563dd55f6ff861",
"hash": "fed9468f6c830b0f81899daad7670af7",
"content-hash": "394f8a6ca5162f2d2756dbbee0ff5aae",
"packages": [
{
"name": "doctrine/annotations",
@ -1484,6 +1485,610 @@
],
"time": "2014-10-12 19:18:40"
},
{
"name": "hoa/compiler",
"version": "2.15.10.29",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Compiler.git",
"reference": "ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Compiler/zipball/ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39",
"reference": "ec0849fd3c1472fbcd86c3c961981f0cfe1f8d39",
"shasum": ""
},
"require": {
"hoa/core": "~2.0",
"hoa/file": "~0.0",
"hoa/iterator": "~1.0",
"hoa/math": "~0.0",
"hoa/regex": "~0.0",
"hoa/visitor": "~1.0"
},
"require-dev": {
"hoa/json": "~1.0",
"hoa/test": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Compiler\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Compiler library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"algebraic",
"ast",
"compiler",
"context-free",
"coverage",
"exhaustive",
"grammar",
"isotropic",
"language",
"lexer",
"library",
"ll1",
"llk",
"parser",
"pp",
"random",
"regular",
"rule",
"sampler",
"syntax",
"token",
"trace",
"uniform"
],
"time": "2015-10-29 21:35:12"
},
{
"name": "hoa/core",
"version": "2.15.11.09",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Core.git",
"reference": "5538b1e90e2c66c90df5cc45e03fb85d047be900"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Core/zipball/5538b1e90e2c66c90df5cc45e03fb85d047be900",
"reference": "5538b1e90e2c66c90df5cc45e03fb85d047be900",
"shasum": ""
},
"require": {
"ext-spl": "*",
"php": ">=5.4.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"suggest": {
"ext-mbstring": "ext/mbstring must be present (or a third implementation).",
"hoa/cli": "To use the `hoa` script."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Core\\": "."
},
"files": [
"Core.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Core library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"consistency",
"core",
"data",
"event",
"library",
"listener",
"parameter",
"protocol"
],
"time": "2015-11-09 06:51:06"
},
{
"name": "hoa/file",
"version": "0.15.11.09",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/File.git",
"reference": "f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/File/zipball/f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee",
"reference": "f46fe552ff79cb6c93a2ff9c25cfbc134fbd57ee",
"shasum": ""
},
"require": {
"hoa/core": "~2.0",
"hoa/iterator": "~1.0",
"hoa/stream": "~0.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\File\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\File library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"Socket",
"directory",
"file",
"finder",
"library",
"link",
"temporary"
],
"time": "2015-11-09 06:55:20"
},
{
"name": "hoa/iterator",
"version": "1.15.10.29",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Iterator.git",
"reference": "a64ed9fd62579a34e4450134d6d1abdf77d54435"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Iterator/zipball/a64ed9fd62579a34e4450134d6d1abdf77d54435",
"reference": "a64ed9fd62579a34e4450134d6d1abdf77d54435",
"shasum": ""
},
"require": {
"hoa/core": "~2.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Iterator\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Iterator library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"iterator",
"library"
],
"time": "2015-10-29 21:37:16"
},
{
"name": "hoa/math",
"version": "0.15.10.26",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Math.git",
"reference": "62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Math/zipball/62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e",
"reference": "62631c65d9a4f1b8bb4c4a3d6cdff0e8971d684e",
"shasum": ""
},
"require": {
"hoa/compiler": "~2.0",
"hoa/core": "~2.0",
"hoa/iterator": "~1.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Math\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Math library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"arrangement",
"combination",
"combinatorics",
"counting",
"library",
"math",
"permutation",
"sampler",
"set"
],
"time": "2015-10-26 15:22:52"
},
{
"name": "hoa/regex",
"version": "0.15.08.13",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Regex.git",
"reference": "2ef8a77ef3885ca202fcd9c31a8e54c44cd04232"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Regex/zipball/2ef8a77ef3885ca202fcd9c31a8e54c44cd04232",
"reference": "2ef8a77ef3885ca202fcd9c31a8e54c44cd04232",
"shasum": ""
},
"require": {
"hoa/core": "~2.0",
"hoa/math": "~0.0",
"hoa/ustring": "~3.0",
"hoa/visitor": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Regex\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Regex library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"compiler",
"library",
"regex"
],
"time": "2015-08-13 06:48:47"
},
{
"name": "hoa/ruler",
"version": "1.15.11.09",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Ruler.git",
"reference": "9afc9ae032d40b6dc10bff85c9126cf516953925"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Ruler/zipball/9afc9ae032d40b6dc10bff85c9126cf516953925",
"reference": "9afc9ae032d40b6dc10bff85c9126cf516953925",
"shasum": ""
},
"require": {
"hoa/compiler": "~2.0",
"hoa/core": "~2.0",
"hoa/file": "~0.0",
"hoa/visitor": "~1.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Ruler\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Ruler library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"library",
"ruler"
],
"time": "2015-11-09 06:58:52"
},
{
"name": "hoa/stream",
"version": "0.15.10.26",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Stream.git",
"reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Stream/zipball/011ab91d942f1d7096deade4c8a10fe57d51c5b3",
"reference": "011ab91d942f1d7096deade4c8a10fe57d51c5b3",
"shasum": ""
},
"require": {
"hoa/core": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Stream\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Stream library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"Context",
"bucket",
"composite",
"filter",
"in",
"library",
"out",
"protocol",
"stream",
"wrapper"
],
"time": "2015-10-22 06:30:43"
},
{
"name": "hoa/ustring",
"version": "3.15.11.09",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Ustring.git",
"reference": "8506be4910212b1a2beb9014763a8a4fbd871001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Ustring/zipball/8506be4910212b1a2beb9014763a8a4fbd871001",
"reference": "8506be4910212b1a2beb9014763a8a4fbd871001",
"shasum": ""
},
"require": {
"hoa/core": "~2.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"suggest": {
"ext-iconv": "ext/iconv must be present (or a third implementation) to use Hoa\\Ustring::transcode().",
"ext-intl": "To get a better Hoa\\Ustring::toAscii() and Hoa\\Ustring::compareTo()."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Ustring\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Ustring library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"library",
"search",
"string",
"unicode"
],
"time": "2015-11-09 06:44:33"
},
{
"name": "hoa/visitor",
"version": "1.15.08.17",
"source": {
"type": "git",
"url": "https://github.com/hoaproject/Visitor.git",
"reference": "e30bfff741f71979f6476a41548e34afe8053c67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hoaproject/Visitor/zipball/e30bfff741f71979f6476a41548e34afe8053c67",
"reference": "e30bfff741f71979f6476a41548e34afe8053c67",
"shasum": ""
},
"require": {
"hoa/core": "~2.0"
},
"require-dev": {
"hoa/test": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Hoa\\Visitor\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name": "Hoa community",
"homepage": "http://hoa-project.net/"
}
],
"description": "The Hoa\\Visitor library.",
"homepage": "http://hoa-project.net/",
"keywords": [
"library",
"structure",
"visit",
"visitor"
],
"time": "2015-08-17 06:30:58"
},
{
"name": "htmlawed/htmlawed",
"version": "1.1.19",
@ -1532,21 +2137,21 @@
},
{
"name": "incenteev/composer-parameter-handler",
"version": "v2.1.1",
"version": "v2.1.2",
"source": {
"type": "git",
"url": "https://github.com/Incenteev/ParameterHandler.git",
"reference": "84a205fe80a46101607bafbc423019527893ddd0"
"reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/84a205fe80a46101607bafbc423019527893ddd0",
"reference": "84a205fe80a46101607bafbc423019527893ddd0",
"url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc",
"reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/yaml": "~2.0"
"symfony/yaml": "~2.3|~3.0"
},
"require-dev": {
"composer/composer": "1.0.*@dev",
@ -1579,7 +2184,7 @@
"keywords": [
"parameters management"
],
"time": "2015-06-03 08:27:03"
"time": "2015-11-10 17:04:01"
},
{
"name": "j0k3r/graby",
@ -1671,20 +2276,19 @@
},
{
"name": "j0k3r/php-readability",
"version": "v1.0.8",
"version": "v1.0.9",
"source": {
"type": "git",
"url": "https://github.com/j0k3r/php-readability.git",
"reference": "f71c3a419623f821c245e0a003edfbf2c67f278e"
"reference": "41d7440c6e6130bacd50808342fe566e28f536fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/j0k3r/php-readability/zipball/f71c3a419623f821c245e0a003edfbf2c67f278e",
"reference": "f71c3a419623f821c245e0a003edfbf2c67f278e",
"url": "https://api.github.com/repos/j0k3r/php-readability/zipball/41d7440c6e6130bacd50808342fe566e28f536fb",
"reference": "41d7440c6e6130bacd50808342fe566e28f536fb",
"shasum": ""
},
"require": {
"ext-tidy": ">=1.2",
"php": ">=5.3.3"
},
"type": "library",
@ -1730,7 +2334,7 @@
"extraction",
"html"
],
"time": "2015-09-23 19:09:38"
"time": "2015-11-10 08:55:29"
},
{
"name": "j0k3r/safecurl",
@ -2057,6 +2661,124 @@
],
"time": "2013-12-05 14:36:11"
},
{
"name": "kphoen/rulerz",
"version": "0.14.0",
"source": {
"type": "git",
"url": "https://github.com/K-Phoen/rulerz.git",
"reference": "608649b148ffdf3437600cc0f450d59b0579148d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/K-Phoen/rulerz/zipball/608649b148ffdf3437600cc0f450d59b0579148d",
"reference": "608649b148ffdf3437600cc0f450d59b0579148d",
"shasum": ""
},
"require": {
"hoa/ruler": "~1.0",
"php": ">=5.4",
"symfony/property-access": "~2.3"
},
"require-dev": {
"behat/behat": "~3.0",
"coduo/phpspec-data-provider-extension": "~1.0,!=1.0.2",
"doctrine/orm": "~2.4",
"elasticsearch/elasticsearch": "~1.0",
"illuminate/database": "~5.0",
"mikey179/vfsstream": "~1.4",
"phpspec/phpspec": "~2.0",
"pomm-project/cli": "~2.0@dev",
"pomm-project/foundation": "~2.0@dev",
"pomm-project/model-manager": "~2.0.@dev",
"ruflin/elastica": "~1.0",
"vlucas/phpdotenv": "~2.1"
},
"suggest": {
"doctrine/orm": "To execute rules as Doctrine queries",
"elasticsearch/elasticsearch": "To execute rules as Elasticsearch queries",
"kphoen/rulerz-spec-builder": "If you want a specification builder",
"pomm-project/model-manager": "To execute rules as Pomm queries"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"RulerZ\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kévin Gomez",
"email": "contact@kevingomez.fr"
}
],
"description": "Powerful implementation of the Specification pattern",
"homepage": "https://github.com/K-Phoen/RulerZ",
"keywords": [
"doctrine",
"specification"
],
"time": "2015-10-31 20:54:37"
},
{
"name": "kphoen/rulerz-bundle",
"version": "0.11.0",
"source": {
"type": "git",
"url": "https://github.com/K-Phoen/RulerZBundle.git",
"reference": "dcaaed69d8252fa1e3a25802f8cf697947570778"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/K-Phoen/RulerZBundle/zipball/dcaaed69d8252fa1e3a25802f8cf697947570778",
"reference": "dcaaed69d8252fa1e3a25802f8cf697947570778",
"shasum": ""
},
"require": {
"kphoen/rulerz": "~0.1, >=0.13.0",
"symfony/framework-bundle": "~2.3|~3.0",
"symfony/validator": "~2.3|~3.0"
},
"require-dev": {
"matthiasnoback/symfony-dependency-injection-test": "~0.7",
"mikey179/vfsstream": "~1.0",
"phpunit/phpunit": "~4.8"
},
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"KPhoen\\RulerZBundle\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kévin Gomez",
"email": "contact@kevingomez.fr"
}
],
"description": "Symfony2 Bundle for RulerZ",
"homepage": "https://github.com/K-Phoen/RulerZBundle",
"keywords": [
"doctrine",
"ruler",
"rulerz",
"specification"
],
"time": "2015-11-13 13:00:14"
},
{
"name": "kriswallsmith/assetic",
"version": "v1.3.1",
@ -3014,25 +3736,25 @@
},
{
"name": "sensio/framework-extra-bundle",
"version": "v3.0.10",
"version": "v3.0.11",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
"reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c"
"reference": "a79e205737b58d557c05caef6dfa8f94d8084bca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/18fc2063c4d6569cdca47a39fbac32342eb65f3c",
"reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c",
"url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/a79e205737b58d557c05caef6dfa8f94d8084bca",
"reference": "a79e205737b58d557c05caef6dfa8f94d8084bca",
"shasum": ""
},
"require": {
"doctrine/common": "~2.2",
"symfony/framework-bundle": "~2.3"
"symfony/framework-bundle": "~2.3|~3.0"
},
"require-dev": {
"symfony/expression-language": "~2.4",
"symfony/security-bundle": "~2.4"
"symfony/expression-language": "~2.4|~3.0",
"symfony/security-bundle": "~2.4|~3.0"
},
"suggest": {
"symfony/expression-language": "",
@ -3065,7 +3787,7 @@
"annotations",
"controllers"
],
"time": "2015-08-03 11:59:27"
"time": "2015-10-28 15:47:04"
},
{
"name": "sensiolabs/security-checker",
@ -4516,16 +5238,16 @@
},
{
"name": "phpunit/phpunit",
"version": "4.8.16",
"version": "4.8.18",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e"
"reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e",
"reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fa33d4ad96481b91df343d83e8c8aabed6b1dfd3",
"reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3",
"shasum": ""
},
"require": {
@ -4584,7 +5306,7 @@
"testing",
"xunit"
],
"time": "2015-10-23 06:48:33"
"time": "2015-11-11 11:32:49"
},
{
"name": "phpunit/phpunit-mock-objects",

View file

@ -0,0 +1,66 @@
<?php
namespace Wallabag\CoreBundle\Command;
use Doctrine\ORM\NoResultException;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TagAllCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->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('<error>User %s not found.</error>', $input->getArgument('username')));
return 1;
}
$tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger');
$output->write(sprintf('Tagging entries for user « <info>%s</info> »... ', $user->getUserName()));
$entries = $tagger->tagAllForUser($user);
$em = $this->getDoctrine()->getManager();
foreach ($entries as $entry) {
$em->persist($entry);
}
$em->flush();
$output->writeln('<info>Done.</info>');
}
/**
* 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');
}
}

View file

@ -7,9 +7,11 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Wallabag\CoreBundle\Entity\Config;
use Wallabag\CoreBundle\Entity\TaggingRule;
use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
use Wallabag\CoreBundle\Form\Type\UserInformationType;
use Wallabag\CoreBundle\Form\Type\TaggingRuleType;
use Wallabag\CoreBundle\Form\Type\NewUserType;
use Wallabag\CoreBundle\Form\Type\RssType;
use Wallabag\CoreBundle\Tools\Utils;
@ -98,6 +100,24 @@ class ConfigController extends Controller
return $this->redirect($this->generateUrl('config'));
}
// handle tagging rule
$taggingRule = new TaggingRule();
$newTaggingRule = $this->createForm(new TaggingRuleType(), $taggingRule, array('action' => $this->generateUrl('config').'#set5'));
$newTaggingRule->handleRequest($request);
if ($newTaggingRule->isValid()) {
$taggingRule->setConfig($config);
$em->persist($taggingRule);
$em->flush();
$this->get('session')->getFlashBag()->add(
'notice',
'Tagging rules updated'
);
return $this->redirect($this->generateUrl('config'));
}
// handle adding new user
$newUser = $userManager->createUser();
// enable created user by default
@ -136,6 +156,7 @@ class ConfigController extends Controller
'pwd' => $pwdForm->createView(),
'user' => $userForm->createView(),
'new_user' => $newUserForm->createView(),
'new_tagging_rule' => $newTaggingRule->createView(),
),
'rss' => array(
'username' => $user->getUsername(),
@ -167,6 +188,33 @@ class ConfigController extends Controller
return $request->headers->get('referer') ? $this->redirect($request->headers->get('referer')) : $this->redirectToRoute('config');
}
/**
* Deletes a tagging rule and redirect to the config homepage.
*
* @param TaggingRule $rule
*
* @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule")
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function deleteTaggingRule(TaggingRule $rule)
{
if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) {
throw $this->createAccessDeniedException('You can not access this tagging ryle.');
}
$em = $this->getDoctrine()->getManager();
$em->remove($rule);
$em->flush();
$this->get('session')->getFlashBag()->add(
'notice',
'Tagging rule deleted'
);
return $this->redirect($this->generateUrl('config'));
}
/**
* Retrieve config for the current user.
* If no config were found, create a new one.

View file

@ -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');

View file

@ -2,6 +2,7 @@
namespace Wallabag\CoreBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
@ -76,12 +77,19 @@ class Config
*/
private $user;
/**
* @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\TaggingRule", mappedBy="config", cascade={"remove"})
* @ORM\OrderBy({"id" = "ASC"})
*/
private $taggingRules;
/*
* @param User $user
*/
public function __construct(\Wallabag\UserBundle\Entity\User $user)
{
$this->user = $user;
$this->taggingRules = new ArrayCollection();
}
/**
@ -237,4 +245,24 @@ class Config
{
return $this->rssLimit;
}
/**
* @param TaggingRule $rule
*
* @return Config
*/
public function addTaggingRule(TaggingRule $rule)
{
$this->taggingRules[] = $rule;
return $this;
}
/**
* @return ArrayCollection<TaggingRule>
*/
public function getTaggingRules()
{
return $this->taggingRules;
}
}

View file

@ -458,6 +458,10 @@ class Entry
*/
public function addTag(Tag $tag)
{
if ($this->tags->contains($tag)) {
return;
}
$this->tags[] = $tag;
$tag->addEntry($this);
}

View file

@ -0,0 +1,133 @@
<?php
namespace Wallabag\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use KPhoen\RulerZBundle\Validator\Constraints as RulerZAssert;
/**
* Tagging rule.
*
* @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\TaggingRuleRepository")
* @ORM\Table
* @ORM\Entity
*/
class TaggingRule
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @Assert\NotBlank()
* @RulerZAssert\ValidRule(
* allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"},
* allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"}
* )
* @ORM\Column(name="rule", type="string", nullable=false)
*/
private $rule;
/**
* @var array
*
* @Assert\NotBlank()
* @ORM\Column(name="tags", type="simple_array", nullable=false)
*/
private $tags = [];
/**
* @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", inversedBy="taggingRules")
*/
private $config;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set rule.
*
* @param string $rule
*
* @return TaggingRule
*/
public function setRule($rule)
{
$this->rule = $rule;
return $this;
}
/**
* Get rule.
*
* @return string
*/
public function getRule()
{
return $this->rule;
}
/**
* Set tags.
*
* @param array<string> $tags
*
* @return TaggingRule
*/
public function setTags(array $tags)
{
$this->tags = $tags;
return $this;
}
/**
* Get tags.
*
* @return array<string>
*/
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;
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace Wallabag\CoreBundle\Form\DataTransformer;
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;
}
/**
* Transforms a list to a string.
*
* @param array|null $list
*
* @return string
*/
public function transform($list)
{
if (null === $list) {
return '';
}
return implode($this->separator, $list);
}
/**
* Transforms a string to a list.
*
* @param string $string
*
* @return array|null
*/
public function reverseTransform($string)
{
if ($string === null) {
return null;
}
return array_values(array_filter(array_map('trim', explode($this->separator, $string))));
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Wallabag\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Wallabag\CoreBundle\Form\DataTransformer\StringToListTransformer;
class TaggingRuleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->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';
}
}

View file

@ -3,6 +3,7 @@
namespace Wallabag\CoreBundle\Helper;
use Graby\Graby;
use Psr\Log\LoggerInterface as Logger;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Tools\Utils;
@ -13,10 +14,14 @@ use Wallabag\CoreBundle\Tools\Utils;
class ContentProxy
{
protected $graby;
protected $tagger;
protected $logger;
public function __construct(Graby $graby)
public function __construct(Graby $graby, RuleBasedTagger $tagger, Logger $logger)
{
$this->graby = $graby;
$this->graby = $graby;
$this->tagger = $tagger;
$this->logger = $logger;
}
/**
@ -59,6 +64,15 @@ class ContentProxy
$entry->setPreviewPicture($content['open_graph']['og_image']);
}
try {
$this->tagger->tag($entry);
} catch (\Exception $e) {
$this->logger->error('Error while trying to automatically tag an entry.', array(
'entry_url' => $url,
'error_msg' => $e->getMessage(),
));
}
return $entry;
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace Wallabag\CoreBundle\Helper;
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;
class RuleBasedTagger
{
private $rulerz;
private $tagRepository;
private $entryRepository;
public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository)
{
$this->rulerz = $rulerz;
$this->tagRepository = $tagRepository;
$this->entryRepository = $entryRepository;
}
/**
* Add tags from rules defined by the user.
*
* @param Entry $entry Entry to tag.
*/
public function tag(Entry $entry)
{
$rules = $this->getRulesForUser($entry->getUser());
foreach ($rules as $rule) {
if (!$this->rulerz->satisfies($entry, $rule->getRule())) {
continue;
}
foreach ($rule->getTags() as $label) {
$tag = $this->getTag($entry->getUser(), $label);
$entry->addTag($tag);
}
}
}
/**
* Apply all the tagging rules defined by a user on its entries.
*
* @param User $user
*
* @return array<Entry> A list of modified entries.
*/
public function tagAllForUser(User $user)
{
$rules = $this->getRulesForUser($user);
$entries = array();
foreach ($rules as $rule) {
$qb = $this->entryRepository->getBuilderForAllByUser($user->getId());
$entries = $this->rulerz->filter($qb, $rule->getRule());
foreach ($entries as $entry) {
foreach ($rule->getTags() as $label) {
$tag = $this->getTag($user, $label);
$entry->addTag($tag);
$entries[] = $entry;
}
}
}
return $entries;
}
/**
* Fetch a tag for a user.
*
* @param User $user
* @param string $label The tag's label.
*
* @return Tag
*/
private function getTag(User $user, $label)
{
$tag = $this->tagRepository->findOneByLabelAndUserId($label, $user->getId());
if (!$tag) {
$tag = new Tag($user);
$tag->setLabel($label);
}
return $tag;
}
/**
* Retrieves the tagging rules for a given user.
*
* @param User $user
*
* @return array<TaggingRule>
*/
private function getRulesForUser(User $user)
{
return $user->getConfig()->getTaggingRules();
}
}

View file

@ -0,0 +1,25 @@
<?php
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)
{
if ($pattern[0] === "'") {
$pattern = sprintf("'%%%s%%'", substr($pattern, 1, -1));
}
return sprintf('UPPER(%s) LIKE UPPER(%s)', $subject, $pattern);
}
}

View file

@ -0,0 +1,21 @@
<?php
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)
{
return stripos($subject, $pattern) !== false;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Wallabag\CoreBundle\Repository;
use Doctrine\ORM\EntityRepository;
class TaggingRuleRepository extends EntityRepository
{
}

View file

@ -53,6 +53,27 @@ services:
class: Wallabag\CoreBundle\Helper\ContentProxy
arguments:
- @wallabag_core.graby
- @wallabag_core.rule_based_tagger
- @logger
wallabag_core.rule_based_tagger:
class: Wallabag\CoreBundle\Helper\RuleBasedTagger
arguments:
- @rulerz
- @wallabag_core.tag_repository
- @wallabag_core.entry_repository
wallabag_core.entry_repository:
class: Wallabag\CoreBundle\Repository\EntryRepository
factory: [ @doctrine.orm.default_entity_manager, getRepository ]
arguments:
- WallabagCoreBundle:Entry
wallabag_core.tag_repository:
class: Wallabag\CoreBundle\Repository\TagRepository
factory: [ @doctrine.orm.default_entity_manager, getRepository ]
arguments:
- WallabagCoreBundle:Tag
wallabag_core.registration_confirmed:
class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener
@ -70,3 +91,13 @@ services:
arguments:
- %wallabag_url%
- src/Wallabag/CoreBundle/Resources/views/themes/_global/public/img/appicon/apple-touch-icon-152.png
wallabag.operator.array.matches:
class: Wallabag\CoreBundle\Operator\PHP\Matches
tags:
- { name: rulerz.operator, executor: rulerz.executor.array, operator: matches }
wallabag.operator.doctrine.matches:
class: Wallabag\CoreBundle\Operator\Doctrine\Matches
tags:
- { name: rulerz.operator, executor: rulerz.executor.doctrine, operator: matches, inline: true }

View file

@ -145,6 +145,39 @@
{{ form_rest(form.pwd) }}
</form>
<h2>{% trans %}Tagging rules{% endtrans %}</h2>
<ul>
{% for tagging_rule in app.user.config.taggingRules %}
<li>
if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} »
<a href="{{ path('delete_tagging_rule', {id: tagging_rule.id}) }}" title="{% trans %}Delete{% endtrans %}" class="tool delete icon-trash icon"></a>
</li>
{% endfor %}
</ul>
<form action="{{ path('config') }}" method="post" {{ form_enctype(form.new_tagging_rule) }}>
{{ form_errors(form.new_tagging_rule) }}
<fieldset class="w500p inline">
<div class="row">
{{ form_label(form.new_tagging_rule.rule) }}
{{ form_errors(form.new_tagging_rule.rule) }}
{{ form_widget(form.new_tagging_rule.rule) }}
</div>
</fieldset>
<fieldset class="w500p inline">
<div class="row">
{{ form_label(form.new_tagging_rule.tags) }}
{{ form_errors(form.new_tagging_rule.tags) }}
{{ form_widget(form.new_tagging_rule.tags) }}
</div>
</fieldset>
{{ form_rest(form.new_tagging_rule) }}
</form>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<h2>{% trans %}Add a user{% endtrans %}</h2>

View file

@ -15,8 +15,9 @@
<li class="tab col s3"><a href="#set2">{% trans %}RSS{% endtrans %}</a></li>
<li class="tab col s3"><a href="#set3">{% trans %}User information{% endtrans %}</a></li>
<li class="tab col s3"><a href="#set4">{% trans %}Password{% endtrans %}</a></li>
<li class="tab col s3"><a href="#set5">{% trans %}Tagging rules{% endtrans %}</a></li>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<li class="tab col s3"><a href="#set5">{% trans %}Add a user{% endtrans %}</a></li>
<li class="tab col s3"><a href="#set6">{% trans %}Add a user{% endtrans %}</a></li>
{% endif %}
</ul>
</div>
@ -183,8 +184,155 @@
</form>
</div>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<div id="set5" class="col s12">
<div class="row">
<div class="input-field col s12">
<ul>
{% for tagging_rule in app.user.config.taggingRules %}
<li>
if « {{ tagging_rule.rule }} » then tag as « {{ tagging_rule.tags|join(', ') }} »
<a href="{{ path('delete_tagging_rule', {id: tagging_rule.id}) }}" title="{% trans %}Delete{% endtrans %}">
<i class="tool grey-text delete mdi-action-delete"></i>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{{ form_start(form.new_tagging_rule) }}
{{ form_errors(form.new_tagging_rule) }}
<div class="row">
<div class="input-field col s12">
{{ form_label(form.new_tagging_rule.rule) }}
{{ form_errors(form.new_tagging_rule.rule) }}
{{ form_widget(form.new_tagging_rule.rule) }}
</div>
</div>
<div class="row">
<div class="input-field col s12">
{{ form_label(form.new_tagging_rule.tags) }}
{{ form_errors(form.new_tagging_rule.tags) }}
{{ form_widget(form.new_tagging_rule.tags) }}
</div>
</div>
<div class="hidden">{{ form_rest(form.new_tagging_rule) }}</div>
<button class="btn waves-effect waves-light" type="submit" name="action">
{% trans %}Save{% endtrans %}
</button>
</form>
<div class="row">
<div class="input-field col s12">
<h4>{% trans %}FAQ{% endtrans %}</h4>
<h5>{% trans %}What does « tagging rules » mean?{% endtrans %}</h5>
<p class="help">
{% trans %}
They are rules used by Wallabag to automatically tag new entries.<br />
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 %}
</p>
<h5>{% trans %}How do I use them?{% endtrans %}</h5>
<p class="help">
{% trans %}
Let assume you want to tag new entries as « <i>short reading</i> » when the reading time is inferior to 3 minutes.<br />
In that case, you should put « readingTime &lt;= 3 » in the <i>Rule</i> field and « <i>short reading</i> » in the <i>Tags</i>
field.<br />
Several tags can added simultaneously by separating them by a comma: « <i>short reading, must read</i> »<br />
Complex rules can be written by using predefined operators: if « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » then tag as « <i>long reading, github </i> »
{% endtrans %}
</p>
<h5>{% trans %}Which variables and operators can I use to write rules?{% endtrans %}</h5>
<p class="help">
{% trans %}The following variables and operators can be used to create tagging rules:{% endtrans %}
<table>
<thead>
<tr>
<th>{% trans %}Variable{% endtrans %}</th>
<th>{% trans %}Meaning{% endtrans %}</th>
<th>{% trans %}Operator{% endtrans %}</th>
<th>{% trans %}Meaning{% endtrans %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>title</td>
<td>{% trans %}Title of the entry{% endtrans %}</td>
<td>&lt;=</td>
<td>{% trans %}Less than…{% endtrans %}</td>
</tr>
<tr>
<td>url</td>
<td>{% trans %}URL of the entry{% endtrans %}</td>
<td>&lt;</td>
<td>{% trans %}Strictly less than…{% endtrans %}</td>
</tr>
<tr>
<td>isArchived</td>
<td>{% trans %}Whether the entry is archived or not{% endtrans %}</td>
<td>=&gt;</td>
<td>{% trans %}Greater than…{% endtrans %}</td>
</tr>
<tr>
<td>isStared</td>
<td>{% trans %}Whether the entry is starred or not{% endtrans %}</td>
<td>&gt;</td>
<td>{% trans %}Strictly greater than…{% endtrans %}</td>
</tr>
<tr>
<td>content</td>
<td>{% trans %}The entry's content{% endtrans %}</td>
<td>=</td>
<td>{% trans %}Equal to…{% endtrans %}</td>
</tr>
<tr>
<td>language</td>
<td>{% trans %}The entry's language{% endtrans %}</td>
<td>!=</td>
<td>{% trans %}Not equal to…{% endtrans %}</td>
</tr>
<tr>
<td>mimetype</td>
<td>{% trans %}The entry's mime-type{% endtrans %}</td>
<td>OR</td>
<td>{% trans %}One rule or another{% endtrans %}</td>
</tr>
<tr>
<td>readingTime</td>
<td>{% trans %}The estimated entry's reading time, in minutes{% endtrans %}</td>
<td>AND</td>
<td>{% trans %}One rule and another{% endtrans %}</td>
</tr>
<tr>
<td>domainName</td>
<td>{% trans %}The domain name of the entry{% endtrans %}</td>
<td>matches</td>
<td>
{% trans %}
Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />
Example: <code>title matches "football"</code>
{% endtrans %}
</td>
</tr>
</tbody>
</table>
</p>
</div>
</div>
</div>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<div id="set6" class="col s12">
{{ form_start(form.new_user) }}
{{ form_errors(form.new_user) }}

View file

@ -479,4 +479,59 @@ class ConfigControllerTest extends WallabagCoreTestCase
$this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
$this->assertContains($expectedMessage, $alert[0]);
}
public function testTaggingRuleCreation()
{
$this->logInAs('admin');
$client = $this->getClient();
$crawler = $client->request('GET', '/config');
$this->assertTrue($client->getResponse()->isSuccessful());
$form = $crawler->filter('button[id=tagging_rule_save]')->form();
$data = array(
'tagging_rule[rule]' => 'readingTime <= 3',
'tagging_rule[tags]' => 'short reading',
);
$client->submit($form, $data);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
$this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text')));
$this->assertContains('Tagging rules updated', $alert[0]);
$deleteLink = $crawler->filter('.delete')->last()->link();
$crawler = $client->click($deleteLink);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
$this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text')));
$this->assertContains('Tagging rule deleted', $alert[0]);
}
public function dataForTaggingRuleFailed()
{
return array(
array(
array(
'rss_config[rule]' => 'unknownVar <= 3',
'rss_config[tags]' => 'cool tag',
),
'The variable « unknownVar » does not exist.',
),
array(
array(
'rss_config[rule]' => 'length(domainName) <= 42',
'rss_config[tags]' => 'cool tag',
),
'The operator « length » does not exist.',
),
);
}
}

View file

@ -102,6 +102,44 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->assertContains('Google', $alert[0]);
}
/**
* This test will require an internet connection.
*/
public function testPostNewThatWillBeTaggued()
{
$this->logInAs('admin');
$client = $this->getClient();
$crawler = $client->request('GET', '/new');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$form = $crawler->filter('button[type=submit]')->form();
$data = array(
'entry[url]' => $url = 'https://github.com/wallabag/wallabag',
);
$client->submit($form, $data);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
$em = $client->getContainer()
->get('doctrine.orm.entity_manager');
$entry = $em
->getRepository('WallabagCoreBundle:Entry')
->findOneByUrl($url);
$tags = $entry->getTags();
$this->assertCount(1, $tags);
$this->assertEquals('wallabag', $tags[0]->getLabel());
$em->remove($entry);
$em->flush();
}
public function testArchive()
{
$this->logInAs('admin');

View file

@ -0,0 +1,50 @@
<?php
namespace Wallabag\CoreBundle\Tests\Form\DataTransformer;
use Wallabag\CoreBundle\Form\DataTransformer\StringToListTransformer;
class StringToListTransformerTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider transformProvider
*/
public function testTransformWithValidData($inputData, $expectedResult)
{
$transformer = new StringToListTransformer();
$this->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') ),
);
}
}

View file

@ -2,6 +2,9 @@
namespace Wallabag\CoreBundle\Tests\Helper;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Psr\Log\NullLogger;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Helper\ContentProxy;
@ -10,6 +13,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
{
public function testWithEmptyContent()
{
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(array('fetchContent'))
->disableOriginalConstructor()
@ -25,7 +32,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
'language' => '',
));
$proxy = new ContentProxy($graby);
$proxy = new ContentProxy($graby, $tagger, $this->getLogger());
$entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0');
$this->assertEquals('http://0.0.0.0', $entry->getUrl());
@ -40,6 +47,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
public function testWithEmptyContentButOG()
{
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(array('fetchContent'))
->disableOriginalConstructor()
@ -59,7 +70,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
),
));
$proxy = new ContentProxy($graby);
$proxy = new ContentProxy($graby, $tagger, $this->getLogger());
$entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io');
$this->assertEquals('http://domain.io', $entry->getUrl());
@ -74,6 +85,10 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
public function testWithContent()
{
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(array('fetchContent'))
->disableOriginalConstructor()
@ -94,7 +109,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
),
));
$proxy = new ContentProxy($graby);
$proxy = new ContentProxy($graby, $tagger, $this->getLogger());
$entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0');
$this->assertEquals('http://1.1.1.1', $entry->getUrl());
@ -106,4 +121,17 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(4.0, $entry->getReadingTime());
$this->assertEquals('1.1.1.1', $entry->getDomainName());
}
private function getTaggerMock()
{
return $this->getMockBuilder('Wallabag\CoreBundle\Helper\RuleBasedTagger')
->setMethods(array('tag'))
->disableOriginalConstructor()
->getMock();
}
private function getLogger()
{
return new NullLogger();
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace Wallabag\CoreBundle\Tests\Helper;
use Wallabag\CoreBundle\Entity\Config;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
use Wallabag\CoreBundle\Entity\TaggingRule;
use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Helper\RuleBasedTagger;
class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase
{
private $rulerz;
private $tagRepository;
private $entryRepository;
private $tagger;
public function setUp()
{
$this->rulerz = $this->getRulerZMock();
$this->tagRepository = $this->getTagRepositoryMock();
$this->entryRepository = $this->getEntryRepositoryMock();
$this->tagger = new RuleBasedTagger($this->rulerz, $this->tagRepository, $this->entryRepository);
}
public function testTagWithNoRule()
{
$entry = new Entry($this->getUser());
$this->tagger->tag($entry);
$this->assertTrue($entry->getTags()->isEmpty());
}
public function testTagWithNoMatchingRule()
{
$taggingRule = $this->getTaggingRule('rule as string', array('foo', 'bar'));
$user = $this->getUser([$taggingRule]);
$entry = new Entry($user);
$this->rulerz
->expects($this->once())
->method('satisfies')
->with($entry, 'rule as string')
->willReturn(false);
$this->tagger->tag($entry);
$this->assertTrue($entry->getTags()->isEmpty());
}
public function testTagWithAMatchingRule()
{
$taggingRule = $this->getTaggingRule('rule as string', array('foo', 'bar'));
$user = $this->getUser([$taggingRule]);
$entry = new Entry($user);
$this->rulerz
->expects($this->once())
->method('satisfies')
->with($entry, 'rule as string')
->willReturn(true);
$this->tagger->tag($entry);
$this->assertFalse($entry->getTags()->isEmpty());
$tags = $entry->getTags();
$this->assertSame('foo', $tags[0]->getLabel());
$this->assertSame($user, $tags[0]->getUser());
$this->assertSame('bar', $tags[1]->getLabel());
$this->assertSame($user, $tags[1]->getUser());
}
public function testTagWithAMixOfMatchingRules()
{
$taggingRule = $this->getTaggingRule('bla bla', array('hey'));
$otherTaggingRule = $this->getTaggingRule('rule as string', array('foo'));
$user = $this->getUser([$taggingRule, $otherTaggingRule]);
$entry = new Entry($user);
$this->rulerz
->method('satisfies')
->will($this->onConsecutiveCalls(false, true));
$this->tagger->tag($entry);
$this->assertFalse($entry->getTags()->isEmpty());
$tags = $entry->getTags();
$this->assertSame('foo', $tags[0]->getLabel());
$this->assertSame($user, $tags[0]->getUser());
}
public function testWhenTheTagExists()
{
$taggingRule = $this->getTaggingRule('rule as string', array('foo'));
$user = $this->getUser([$taggingRule]);
$entry = new Entry($user);
$tag = new Tag($user);
$this->rulerz
->expects($this->once())
->method('satisfies')
->with($entry, 'rule as string')
->willReturn(true);
$this->tagRepository
->expects($this->once())
->method('findOneByLabelAndUserId')
->willReturn($tag);
$this->tagger->tag($entry);
$this->assertFalse($entry->getTags()->isEmpty());
$tags = $entry->getTags();
$this->assertSame($tag, $tags[0]);
}
private function getUser(array $taggingRules = [])
{
$user = new User();
$config = new Config($user);
$user->setConfig($config);
foreach ($taggingRules as $rule) {
$config->addTaggingRule($rule);
}
return $user;
}
private function getTaggingRule($rule, array $tags)
{
$taggingRule = new TaggingRule();
$taggingRule->setRule($rule);
$taggingRule->setTags($tags);
return $taggingRule;
}
private function getRulerZMock()
{
return $this->getMockBuilder('RulerZ\RulerZ')
->disableOriginalConstructor()
->getMock();
}
private function getTagRepositoryMock()
{
return $this->getMockBuilder('Wallabag\CoreBundle\Repository\TagRepository')
->disableOriginalConstructor()
->getMock();
}
private function getEntryRepositoryMock()
{
return $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
->disableOriginalConstructor()
->getMock();
}
}

View file

@ -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();
}
}