aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNicolas LÅ“uillet <nicolas@loeuillet.org>2015-03-06 21:48:33 +0100
committerNicolas LÅ“uillet <nicolas@loeuillet.org>2015-03-06 21:48:33 +0100
commitf37d1427a1b75f9d7a2e273b2e9fb0e895a769ab (patch)
treee6ea5fb4786e843b970b14d5a96da8c32548a315
parent73b774438395d0ed74d0fd863194d2ebfb68d9ce (diff)
parent6e22bd737b2e543c85487864a9a25b0f2d5cc3f2 (diff)
downloadwallabag-f37d1427a1b75f9d7a2e273b2e9fb0e895a769ab.tar.gz
wallabag-f37d1427a1b75f9d7a2e273b2e9fb0e895a769ab.tar.zst
wallabag-f37d1427a1b75f9d7a2e273b2e9fb0e895a769ab.zip
Merge pull request #1110 from wallabag/v2-api-hypermedia
[WIP] API : hypermedia & tags
-rw-r--r--app/AppKernel.php3
-rw-r--r--composer.json4
-rw-r--r--composer.lock236
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php5
-rw-r--r--src/Wallabag/CoreBundle/Controller/WallabagRestController.php170
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php26
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php59
-rw-r--r--src/Wallabag/CoreBundle/Entity/Tag.php40
-rw-r--r--src/Wallabag/CoreBundle/Entity/TagsEntries.php93
-rw-r--r--src/Wallabag/CoreBundle/Entity/User.php34
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php38
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php9
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php9
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php81
14 files changed, 589 insertions, 218 deletions
diff --git a/app/AppKernel.php b/app/AppKernel.php
index d134de3c..601b52c3 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -22,7 +22,8 @@ class AppKernel extends Kernel
22 new Nelmio\ApiDocBundle\NelmioApiDocBundle(), 22 new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
23 new Nelmio\CorsBundle\NelmioCorsBundle(), 23 new Nelmio\CorsBundle\NelmioCorsBundle(),
24 new Liip\ThemeBundle\LiipThemeBundle(), 24 new Liip\ThemeBundle\LiipThemeBundle(),
25 new Wallabag\CoreBundle\WallabagCoreBundle() 25 new Wallabag\CoreBundle\WallabagCoreBundle(),
26 new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle()
26 ); 27 );
27 28
28 if (in_array($this->getEnvironment(), array('dev', 'test'))) { 29 if (in_array($this->getEnvironment(), array('dev', 'test'))) {
diff --git a/composer.json b/composer.json
index 0b162c02..590e2629 100644
--- a/composer.json
+++ b/composer.json
@@ -74,6 +74,7 @@
74 "robmorgan/phinx": "~0.4", 74 "robmorgan/phinx": "~0.4",
75 "tecnick.com/tcpdf": "~6.2", 75 "tecnick.com/tcpdf": "~6.2",
76 "simplepie/simplepie": "~1.3.1", 76 "simplepie/simplepie": "~1.3.1",
77 "willdurand/hateoas-bundle": "1.0.*@dev",
77 "htmlawed/htmlawed": "dev-master", 78 "htmlawed/htmlawed": "dev-master",
78 "liip/theme-bundle": "1.1.3", 79 "liip/theme-bundle": "1.1.3",
79 "wallabag/PHP-Flash-Messages": "dev-master", 80 "wallabag/PHP-Flash-Messages": "dev-master",
@@ -82,7 +83,8 @@
82 "wallabag/PHPePub": "dev-master", 83 "wallabag/PHPePub": "dev-master",
83 "wallabag/php-readability": "dev-master", 84 "wallabag/php-readability": "dev-master",
84 "wallabag/phpMobi": "dev-master", 85 "wallabag/phpMobi": "dev-master",
85 "wallabag/Fivefilters_Libraries": "dev-master" 86 "wallabag/Fivefilters_Libraries": "dev-master",
87 "pagerfanta/pagerfanta": "~1.0@dev"
86 }, 88 },
87 "require-dev": { 89 "require-dev": {
88 "doctrine/doctrine-fixtures-bundle": "dev-master", 90 "doctrine/doctrine-fixtures-bundle": "dev-master",
diff --git a/composer.lock b/composer.lock
index a6102a81..66f4738b 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
4 "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 4 "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 "This file is @generated automatically" 5 "This file is @generated automatically"
6 ], 6 ],
7 "hash": "fd56c671d70f498ccc1996450479fbdc", 7 "hash": "43d869c37ae73d7b74d3f77c4028bf9c",
8 "packages": [ 8 "packages": [
9 { 9 {
10 "name": "doctrine/annotations", 10 "name": "doctrine/annotations",
@@ -1386,16 +1386,16 @@
1386 }, 1386 },
1387 { 1387 {
1388 "name": "michelf/php-markdown", 1388 "name": "michelf/php-markdown",
1389 "version": "1.4.1", 1389 "version": "1.5.0",
1390 "source": { 1390 "source": {
1391 "type": "git", 1391 "type": "git",
1392 "url": "https://github.com/michelf/php-markdown.git", 1392 "url": "https://github.com/michelf/php-markdown.git",
1393 "reference": "de9a19c7bf352d41cc99ed86c3c0ef17e87394b6" 1393 "reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9"
1394 }, 1394 },
1395 "dist": { 1395 "dist": {
1396 "type": "zip", 1396 "type": "zip",
1397 "url": "https://api.github.com/repos/michelf/php-markdown/zipball/de9a19c7bf352d41cc99ed86c3c0ef17e87394b6", 1397 "url": "https://api.github.com/repos/michelf/php-markdown/zipball/e1aabe18173231ebcefc90e615565742fc1c7fd9",
1398 "reference": "de9a19c7bf352d41cc99ed86c3c0ef17e87394b6", 1398 "reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9",
1399 "shasum": "" 1399 "shasum": ""
1400 }, 1400 },
1401 "require": { 1401 "require": {
@@ -1418,35 +1418,35 @@
1418 ], 1418 ],
1419 "authors": [ 1419 "authors": [
1420 { 1420 {
1421 "name": "John Gruber",
1422 "homepage": "http://daringfireball.net/"
1423 },
1424 {
1421 "name": "Michel Fortin", 1425 "name": "Michel Fortin",
1422 "email": "michel.fortin@michelf.ca", 1426 "email": "michel.fortin@michelf.ca",
1423 "homepage": "http://michelf.ca/", 1427 "homepage": "https://michelf.ca/",
1424 "role": "Developer" 1428 "role": "Developer"
1425 },
1426 {
1427 "name": "John Gruber",
1428 "homepage": "http://daringfireball.net/"
1429 } 1429 }
1430 ], 1430 ],
1431 "description": "PHP Markdown", 1431 "description": "PHP Markdown",
1432 "homepage": "http://michelf.ca/projects/php-markdown/", 1432 "homepage": "https://michelf.ca/projects/php-markdown/",
1433 "keywords": [ 1433 "keywords": [
1434 "markdown" 1434 "markdown"
1435 ], 1435 ],
1436 "time": "2014-05-05 02:43:50" 1436 "time": "2015-03-01 12:03:08"
1437 }, 1437 },
1438 { 1438 {
1439 "name": "monolog/monolog", 1439 "name": "monolog/monolog",
1440 "version": "1.12.0", 1440 "version": "1.13.0",
1441 "source": { 1441 "source": {
1442 "type": "git", 1442 "type": "git",
1443 "url": "https://github.com/Seldaek/monolog.git", 1443 "url": "https://github.com/Seldaek/monolog.git",
1444 "reference": "1fbe8c2641f2b163addf49cc5e18f144bec6b19f" 1444 "reference": "c41c218e239b50446fd883acb1ecfd4b770caeae"
1445 }, 1445 },
1446 "dist": { 1446 "dist": {
1447 "type": "zip", 1447 "type": "zip",
1448 "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1fbe8c2641f2b163addf49cc5e18f144bec6b19f", 1448 "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c41c218e239b50446fd883acb1ecfd4b770caeae",
1449 "reference": "1fbe8c2641f2b163addf49cc5e18f144bec6b19f", 1449 "reference": "c41c218e239b50446fd883acb1ecfd4b770caeae",
1450 "shasum": "" 1450 "shasum": ""
1451 }, 1451 },
1452 "require": { 1452 "require": {
@@ -1463,6 +1463,7 @@
1463 "phpunit/phpunit": "~4.0", 1463 "phpunit/phpunit": "~4.0",
1464 "raven/raven": "~0.5", 1464 "raven/raven": "~0.5",
1465 "ruflin/elastica": "0.90.*", 1465 "ruflin/elastica": "0.90.*",
1466 "swiftmailer/swiftmailer": "~5.3",
1466 "videlalvaro/php-amqplib": "~2.4" 1467 "videlalvaro/php-amqplib": "~2.4"
1467 }, 1468 },
1468 "suggest": { 1469 "suggest": {
@@ -1479,7 +1480,7 @@
1479 "type": "library", 1480 "type": "library",
1480 "extra": { 1481 "extra": {
1481 "branch-alias": { 1482 "branch-alias": {
1482 "dev-master": "1.12.x-dev" 1483 "dev-master": "1.13.x-dev"
1483 } 1484 }
1484 }, 1485 },
1485 "autoload": { 1486 "autoload": {
@@ -1505,7 +1506,7 @@
1505 "logging", 1506 "logging",
1506 "psr-3" 1507 "psr-3"
1507 ], 1508 ],
1508 "time": "2014-12-29 21:29:35" 1509 "time": "2015-03-05 01:12:12"
1509 }, 1510 },
1510 { 1511 {
1511 "name": "nelmio/api-doc-bundle", 1512 "name": "nelmio/api-doc-bundle",
@@ -1638,6 +1639,73 @@
1638 "time": "2014-12-10 17:26:49" 1639 "time": "2014-12-10 17:26:49"
1639 }, 1640 },
1640 { 1641 {
1642 "name": "pagerfanta/pagerfanta",
1643 "version": "v1.0.3",
1644 "source": {
1645 "type": "git",
1646 "url": "https://github.com/whiteoctober/Pagerfanta.git",
1647 "reference": "a874d3612d954dcbbb49e5ffe178890918fb76fb"
1648 },
1649 "dist": {
1650 "type": "zip",
1651 "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/a874d3612d954dcbbb49e5ffe178890918fb76fb",
1652 "reference": "a874d3612d954dcbbb49e5ffe178890918fb76fb",
1653 "shasum": ""
1654 },
1655 "require": {
1656 "php": ">=5.3.0"
1657 },
1658 "require-dev": {
1659 "doctrine/orm": "~2.3",
1660 "doctrine/phpcr-odm": "1.*",
1661 "jackalope/jackalope-doctrine-dbal": "1.*",
1662 "jmikola/geojson": "~1.0",
1663 "mandango/mandango": "~1.0@dev",
1664 "mandango/mondator": "~1.0@dev",
1665 "phpunit/phpunit": "~4",
1666 "propel/propel1": "~1.6",
1667 "ruflin/elastica": "~1.3",
1668 "solarium/solarium": "~3.1"
1669 },
1670 "suggest": {
1671 "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.",
1672 "doctrine/orm": "To use the DoctrineORMAdapter.",
1673 "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0",
1674 "mandango/mandango": "To use the MandangoAdapter.",
1675 "propel/propel1": "To use the PropelAdapter",
1676 "solarium/solarium": "To use the SolariumAdapter."
1677 },
1678 "type": "library",
1679 "extra": {
1680 "branch-alias": {
1681 "dev-master": "1.0.x-dev"
1682 }
1683 },
1684 "autoload": {
1685 "psr-0": {
1686 "Pagerfanta\\": "src/"
1687 }
1688 },
1689 "notification-url": "https://packagist.org/downloads/",
1690 "license": [
1691 "MIT"
1692 ],
1693 "authors": [
1694 {
1695 "name": "Pablo Díez",
1696 "email": "pablodip@gmail.com"
1697 }
1698 ],
1699 "description": "Pagination for PHP 5.3",
1700 "keywords": [
1701 "page",
1702 "pagination",
1703 "paginator",
1704 "paging"
1705 ],
1706 "time": "2014-10-06 10:57:25"
1707 },
1708 {
1641 "name": "phpcollection/phpcollection", 1709 "name": "phpcollection/phpcollection",
1642 "version": "0.4.0", 1710 "version": "0.4.0",
1643 "source": { 1711 "source": {
@@ -2645,7 +2713,7 @@
2645 "description": "Libraries from @fivefilters.", 2713 "description": "Libraries from @fivefilters.",
2646 "homepage": "https://github.com/wallabag/Fivefilters_Libraries", 2714 "homepage": "https://github.com/wallabag/Fivefilters_Libraries",
2647 "support": { 2715 "support": {
2648 "source": "https://github.com/wallabag/Fivefilters_Libraries/tree/1.0.0", 2716 "source": "https://github.com/wallabag/Fivefilters_Libraries/tree/master",
2649 "issues": "https://github.com/wallabag/Fivefilters_Libraries/issues" 2717 "issues": "https://github.com/wallabag/Fivefilters_Libraries/issues"
2650 }, 2718 },
2651 "time": "2015-01-19 20:19:28" 2719 "time": "2015-01-19 20:19:28"
@@ -2690,7 +2758,7 @@
2690 "description": "PHP Classes for dynamically generating EPub files.", 2758 "description": "PHP Classes for dynamically generating EPub files.",
2691 "homepage": "https://github.com/wallabag/PHPePub", 2759 "homepage": "https://github.com/wallabag/PHPePub",
2692 "support": { 2760 "support": {
2693 "source": "https://github.com/wallabag/PHPePub/tree/2.1.0" 2761 "source": "https://github.com/wallabag/PHPePub/tree/master"
2694 }, 2762 },
2695 "time": "2015-01-19 11:44:19" 2763 "time": "2015-01-19 11:44:19"
2696 }, 2764 },
@@ -2727,7 +2795,7 @@
2727 "description": "A simple and smart (or stupid) php5 snippets repository", 2795 "description": "A simple and smart (or stupid) php5 snippets repository",
2728 "homepage": "https://github.com/wallabag/kriss_php5", 2796 "homepage": "https://github.com/wallabag/kriss_php5",
2729 "support": { 2797 "support": {
2730 "source": "https://github.com/wallabag/kriss_php5/tree/1.0.0" 2798 "source": "https://github.com/wallabag/kriss_php5/tree/master"
2731 }, 2799 },
2732 "time": "2015-01-18 21:21:43" 2800 "time": "2015-01-18 21:21:43"
2733 }, 2801 },
@@ -2764,7 +2832,7 @@
2764 "description": "Paginate record sets, not tied in directly to a database.", 2832 "description": "Paginate record sets, not tied in directly to a database.",
2765 "homepage": "https://github.com/wallabag/pagination", 2833 "homepage": "https://github.com/wallabag/pagination",
2766 "support": { 2834 "support": {
2767 "source": "https://github.com/wallabag/pagination/tree/1.0.0" 2835 "source": "https://github.com/wallabag/pagination/tree/master"
2768 }, 2836 },
2769 "time": "2015-01-19 09:24:39" 2837 "time": "2015-01-19 09:24:39"
2770 }, 2838 },
@@ -2810,7 +2878,7 @@
2810 "sessions" 2878 "sessions"
2811 ], 2879 ],
2812 "support": { 2880 "support": {
2813 "source": "https://github.com/wallabag/PHP-Flash-Messages/tree/1.0.0" 2881 "source": "https://github.com/wallabag/PHP-Flash-Messages/tree/master"
2814 }, 2882 },
2815 "time": "2015-01-18 19:51:55" 2883 "time": "2015-01-18 19:51:55"
2816 }, 2884 },
@@ -2864,7 +2932,7 @@
2864 "html" 2932 "html"
2865 ], 2933 ],
2866 "support": { 2934 "support": {
2867 "source": "https://github.com/wallabag/php-readability/tree/1.0.0", 2935 "source": "https://github.com/wallabag/php-readability/tree/master",
2868 "issues": "https://github.com/wallabag/php-readability/issues" 2936 "issues": "https://github.com/wallabag/php-readability/issues"
2869 }, 2937 },
2870 "time": "2015-01-19 12:25:38" 2938 "time": "2015-01-19 12:25:38"
@@ -2902,11 +2970,127 @@
2902 "description": "An experimental Mobipocket file creator in PHP.", 2970 "description": "An experimental Mobipocket file creator in PHP.",
2903 "homepage": "https://github.com/wallabag/phpMobi", 2971 "homepage": "https://github.com/wallabag/phpMobi",
2904 "support": { 2972 "support": {
2905 "source": "https://github.com/wallabag/phpMobi/tree/1.0.0" 2973 "source": "https://github.com/wallabag/phpMobi/tree/master"
2906 }, 2974 },
2907 "time": "2015-01-19 12:43:17" 2975 "time": "2015-01-19 12:43:17"
2908 }, 2976 },
2909 { 2977 {
2978 "name": "willdurand/hateoas",
2979 "version": "v2.4.0",
2980 "source": {
2981 "type": "git",
2982 "url": "https://github.com/willdurand/Hateoas.git",
2983 "reference": "89fe19ad9ce25f15323d76ac22272282ae8a9f14"
2984 },
2985 "dist": {
2986 "type": "zip",
2987 "url": "https://api.github.com/repos/willdurand/Hateoas/zipball/89fe19ad9ce25f15323d76ac22272282ae8a9f14",
2988 "reference": "89fe19ad9ce25f15323d76ac22272282ae8a9f14",
2989 "shasum": ""
2990 },
2991 "require": {
2992 "doctrine/annotations": "~1.0",
2993 "doctrine/common": "~2.0",
2994 "jms/metadata": "~1.1",
2995 "jms/serializer": "~0.13@dev",
2996 "symfony/expression-language": "~2.4"
2997 },
2998 "require-dev": {
2999 "atoum/atoum": "*@dev",
3000 "hautelook/frankenstein": "~0.1",
3001 "pagerfanta/pagerfanta": "~1.0",
3002 "phpunit/phpunit": "~3.7",
3003 "symfony/dependency-injection": "~2.0",
3004 "symfony/routing": "~2.0",
3005 "symfony/yaml": "~2.0",
3006 "twig/twig": "~1.12"
3007 },
3008 "suggest": {
3009 "symfony/routing": "To use the SymfonyRouteFactory.",
3010 "symfony/yaml": "To use yaml based configuration.",
3011 "twig/twig": "To use the Twig extensions."
3012 },
3013 "type": "library",
3014 "extra": {
3015 "branch-alias": {
3016 "dev-master": "2.4-dev"
3017 }
3018 },
3019 "autoload": {
3020 "psr-0": {
3021 "Hateoas": "src/"
3022 }
3023 },
3024 "notification-url": "https://packagist.org/downloads/",
3025 "license": [
3026 "MIT"
3027 ],
3028 "authors": [
3029 {
3030 "name": "Adrien Brault",
3031 "email": "adrien.brault@gmail.com"
3032 },
3033 {
3034 "name": "William Durand",
3035 "email": "william.durand1@gmail.com"
3036 }
3037 ],
3038 "description": "A PHP library to support implementing representations for HATEOAS REST web services",
3039 "time": "2015-02-24 15:28:33"
3040 },
3041 {
3042 "name": "willdurand/hateoas-bundle",
3043 "version": "dev-master",
3044 "target-dir": "Bazinga/Bundle/HateoasBundle",
3045 "source": {
3046 "type": "git",
3047 "url": "https://github.com/willdurand/BazingaHateoasBundle.git",
3048 "reference": "3c86e8080e8a229365a0ce91818da6fe6562376b"
3049 },
3050 "dist": {
3051 "type": "zip",
3052 "url": "https://api.github.com/repos/willdurand/BazingaHateoasBundle/zipball/3c86e8080e8a229365a0ce91818da6fe6562376b",
3053 "reference": "3c86e8080e8a229365a0ce91818da6fe6562376b",
3054 "shasum": ""
3055 },
3056 "require": {
3057 "jms/serializer-bundle": "~0.13",
3058 "symfony/framework-bundle": "~2.2",
3059 "willdurand/hateoas": "~2.0"
3060 },
3061 "require-dev": {
3062 "symfony/expression-language": "~2.4",
3063 "twig/twig": "~1.12"
3064 },
3065 "type": "symfony-bundle",
3066 "extra": {
3067 "branch-alias": {
3068 "dev-master": "1.0.x-dev"
3069 }
3070 },
3071 "autoload": {
3072 "psr-0": {
3073 "Bazinga\\Bundle\\HateoasBundle": ""
3074 }
3075 },
3076 "notification-url": "https://packagist.org/downloads/",
3077 "license": [
3078 "MIT"
3079 ],
3080 "authors": [
3081 {
3082 "name": "William Durand",
3083 "email": "william.durand1@gmail.com"
3084 }
3085 ],
3086 "description": "Integration of Hateoas into Symfony2.",
3087 "keywords": [
3088 "HATEOAS",
3089 "rest"
3090 ],
3091 "time": "2015-02-19 16:27:51"
3092 },
3093 {
2910 "name": "willdurand/jsonp-callback-validator", 3094 "name": "willdurand/jsonp-callback-validator",
2911 "version": "v1.1.0", 3095 "version": "v1.1.0",
2912 "source": { 3096 "source": {
@@ -4068,6 +4252,7 @@
4068 "minimum-stability": "dev", 4252 "minimum-stability": "dev",
4069 "stability-flags": { 4253 "stability-flags": {
4070 "nelmio/cors-bundle": 20, 4254 "nelmio/cors-bundle": 20,
4255 "willdurand/hateoas-bundle": 20,
4071 "htmlawed/htmlawed": 20, 4256 "htmlawed/htmlawed": 20,
4072 "wallabag/php-flash-messages": 20, 4257 "wallabag/php-flash-messages": 20,
4073 "wallabag/kriss_php5": 20, 4258 "wallabag/kriss_php5": 20,
@@ -4076,6 +4261,7 @@
4076 "wallabag/php-readability": 20, 4261 "wallabag/php-readability": 20,
4077 "wallabag/phpmobi": 20, 4262 "wallabag/phpmobi": 20,
4078 "wallabag/fivefilters_libraries": 20, 4263 "wallabag/fivefilters_libraries": 20,
4264 "pagerfanta/pagerfanta": 20,
4079 "doctrine/doctrine-fixtures-bundle": 20 4265 "doctrine/doctrine-fixtures-bundle": 20
4080 }, 4266 },
4081 "prefer-stable": true, 4267 "prefer-stable": true,
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 81ab7788..8a8f3cd7 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -192,8 +192,9 @@ class EntryController extends Controller
192 { 192 {
193 $this->checkUserAction($entry); 193 $this->checkUserAction($entry);
194 194
195 $entry->setDeleted(1); 195 $em = $this->getDoctrine()->getManager();
196 $this->getDoctrine()->getManager()->flush(); 196 $em->remove($entry);
197 $em->flush();
197 198
198 $this->get('session')->getFlashBag()->add( 199 $this->get('session')->getFlashBag()->add(
199 'notice', 200 'notice',
diff --git a/src/Wallabag/CoreBundle/Controller/WallabagRestController.php b/src/Wallabag/CoreBundle/Controller/WallabagRestController.php
index e9cd8c93..14f42c48 100644
--- a/src/Wallabag/CoreBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/CoreBundle/Controller/WallabagRestController.php
@@ -5,14 +5,41 @@ namespace Wallabag\CoreBundle\Controller;
5use Nelmio\ApiDocBundle\Annotation\ApiDoc; 5use Nelmio\ApiDocBundle\Annotation\ApiDoc;
6use Symfony\Bundle\FrameworkBundle\Controller\Controller; 6use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7use Symfony\Component\HttpFoundation\Request; 7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 8use Symfony\Component\HttpFoundation\Response;
9use Wallabag\CoreBundle\Entity\Entry; 9use Wallabag\CoreBundle\Entity\Entry;
10use Wallabag\CoreBundle\Entity\Tag; 10use Wallabag\CoreBundle\Entity\Tag;
11use Wallabag\CoreBundle\Service\Extractor; 11use Wallabag\CoreBundle\Service\Extractor;
12use Hateoas\Configuration\Route;
13use Hateoas\Representation\Factory\PagerfantaFactory;
12 14
13class WallabagRestController extends Controller 15class WallabagRestController extends Controller
14{ 16{
15 /** 17 /**
18 * @param Entry $entry
19 * @param string $tags
20 */
21 private function assignTagsToEntry(Entry $entry, $tags)
22 {
23 foreach (explode(',', $tags) as $label) {
24 $label = trim($label);
25 $tagEntity = $this
26 ->getDoctrine()
27 ->getRepository('WallabagCoreBundle:Tag')
28 ->findOneByLabel($label);
29
30 if (is_null($tagEntity)) {
31 $tagEntity = new Tag($this->getUser());
32 $tagEntity->setLabel($label);
33 }
34
35 // only add the tag on the entry if the relation doesn't exist
36 if (!$entry->getTags()->contains($tagEntity)) {
37 $entry->addTag($tagEntity);
38 }
39 }
40 }
41
42 /**
16 * Retrieve salt for a giver user. 43 * Retrieve salt for a giver user.
17 * 44 *
18 * @ApiDoc( 45 * @ApiDoc(
@@ -42,7 +69,6 @@ class WallabagRestController extends Controller
42 * parameters={ 69 * parameters={
43 * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false, all entries by default", "description"="filter by archived status."}, 70 * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false, all entries by default", "description"="filter by archived status."},
44 * {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false, all entries by default", "description"="filter by starred status."}, 71 * {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false, all entries by default", "description"="filter by starred status."},
45 * {"name"="delete", "dataType"="boolean", "required"=false, "format"="true or false, default '0'", "description"="filter by deleted status."},
46 * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, 72 * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."},
47 * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, 73 * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."},
48 * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, 74 * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."},
@@ -56,23 +82,33 @@ class WallabagRestController extends Controller
56 { 82 {
57 $isArchived = $request->query->get('archive'); 83 $isArchived = $request->query->get('archive');
58 $isStarred = $request->query->get('star'); 84 $isStarred = $request->query->get('star');
59 $isDeleted = $request->query->get('delete', 0);
60 $sort = $request->query->get('sort', 'created'); 85 $sort = $request->query->get('sort', 'created');
61 $order = $request->query->get('order', 'desc'); 86 $order = $request->query->get('order', 'desc');
62 $page = $request->query->get('page', 1); 87 $page = (int) $request->query->get('page', 1);
63 $perPage = $request->query->get('perPage', 30); 88 $perPage = (int) $request->query->get('perPage', 30);
64 $tags = $request->query->get('tags', array()); 89 $tags = $request->query->get('tags', array());
65 90
66 $entries = $this 91 $pager = $this
67 ->getDoctrine() 92 ->getDoctrine()
68 ->getRepository('WallabagCoreBundle:Entry') 93 ->getRepository('WallabagCoreBundle:Entry')
69 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $isDeleted, $sort, $order); 94 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order);
70 95
71 if (!($entries)) { 96 if (0 === $pager->getNbResults()) {
72 throw $this->createNotFoundException(); 97 throw $this->createNotFoundException();
73 } 98 }
74 99
75 return $entries; 100 $pager->setCurrentPage($page);
101 $pager->setMaxPerPage($perPage);
102
103 $pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
104 $paginatedCollection = $pagerfantaFactory->createRepresentation(
105 $pager,
106 new Route('api_get_entries', [], $absolute = true)
107 );
108
109 $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
110
111 return new Response($json, 200, array('application/json'));
76 } 112 }
77 113
78 /** 114 /**
@@ -87,7 +123,13 @@ class WallabagRestController extends Controller
87 */ 123 */
88 public function getEntryAction(Entry $entry) 124 public function getEntryAction(Entry $entry)
89 { 125 {
90 return $entry; 126 if ($entry->getUser()->getId() != $this->getUser()->getId()) {
127 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
128 }
129
130 $json = $this->get('serializer')->serialize($entry, 'json');
131
132 return new Response($json, 200, array('application/json'));
91 } 133 }
92 134
93 /** 135 /**
@@ -104,7 +146,6 @@ class WallabagRestController extends Controller
104 */ 146 */
105 public function postEntriesAction(Request $request) 147 public function postEntriesAction(Request $request)
106 { 148 {
107 //TODO gérer si on passe les tags
108 $url = $request->request->get('url'); 149 $url = $request->request->get('url');
109 150
110 $content = Extractor::extract($url); 151 $content = Extractor::extract($url);
@@ -112,11 +153,19 @@ class WallabagRestController extends Controller
112 $entry->setUrl($url); 153 $entry->setUrl($url);
113 $entry->setTitle($request->request->get('title') ?: $content->getTitle()); 154 $entry->setTitle($request->request->get('title') ?: $content->getTitle());
114 $entry->setContent($content->getBody()); 155 $entry->setContent($content->getBody());
156
157 $tags = $request->request->get('tags', '');
158 if (!empty($tags)) {
159 $this->assignTagsToEntry($entry, $tags);
160 }
161
115 $em = $this->getDoctrine()->getManager(); 162 $em = $this->getDoctrine()->getManager();
116 $em->persist($entry); 163 $em->persist($entry);
117 $em->flush(); 164 $em->flush();
118 165
119 return $entry; 166 $json = $this->get('serializer')->serialize($entry, 'json');
167
168 return new Response($json, 200, array('application/json'));
120 } 169 }
121 170
122 /** 171 /**
@@ -131,17 +180,18 @@ class WallabagRestController extends Controller
131 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, 180 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
132 * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false", "description"="archived the entry."}, 181 * {"name"="archive", "dataType"="boolean", "required"=false, "format"="true or false", "description"="archived the entry."},
133 * {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false", "description"="starred the entry."}, 182 * {"name"="star", "dataType"="boolean", "required"=false, "format"="true or false", "description"="starred the entry."},
134 * {"name"="delete", "dataType"="boolean", "required"=false, "format"="true or false", "description"="flag as deleted. Default false. In case that you don't want to *really* remove it.."}, 183 * }
135 * }
136 * ) 184 * )
137 * @return Entry 185 * @return Entry
138 */ 186 */
139 public function patchEntriesAction(Entry $entry, Request $request) 187 public function patchEntriesAction(Entry $entry, Request $request)
140 { 188 {
189 if ($entry->getUser()->getId() != $this->getUser()->getId()) {
190 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
191 }
192
141 $title = $request->request->get("title"); 193 $title = $request->request->get("title");
142 $tags = $request->request->get("tags", array());
143 $isArchived = $request->request->get("archive"); 194 $isArchived = $request->request->get("archive");
144 $isDeleted = $request->request->get("delete");
145 $isStarred = $request->request->get("star"); 195 $isStarred = $request->request->get("star");
146 196
147 if (!is_null($title)) { 197 if (!is_null($title)) {
@@ -152,18 +202,21 @@ class WallabagRestController extends Controller
152 $entry->setArchived($isArchived); 202 $entry->setArchived($isArchived);
153 } 203 }
154 204
155 if (!is_null($isDeleted)) {
156 $entry->setDeleted($isDeleted);
157 }
158
159 if (!is_null($isStarred)) { 205 if (!is_null($isStarred)) {
160 $entry->setStarred($isStarred); 206 $entry->setStarred($isStarred);
161 } 207 }
162 208
209 $tags = $request->request->get('tags', '');
210 if (!empty($tags)) {
211 $this->assignTagsToEntry($entry, $tags);
212 }
213
163 $em = $this->getDoctrine()->getManager(); 214 $em = $this->getDoctrine()->getManager();
164 $em->flush(); 215 $em->flush();
165 216
166 return $entry; 217 $json = $this->get('serializer')->serialize($entry, 'json');
218
219 return new Response($json, 200, array('application/json'));
167 } 220 }
168 221
169 /** 222 /**
@@ -178,15 +231,17 @@ class WallabagRestController extends Controller
178 */ 231 */
179 public function deleteEntriesAction(Entry $entry) 232 public function deleteEntriesAction(Entry $entry)
180 { 233 {
181 if ($entry->isDeleted()) { 234 if ($entry->getUser()->getId() != $this->getUser()->getId()) {
182 throw new NotFoundHttpException('This entry is already deleted'); 235 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
183 } 236 }
184 237
185 $em = $this->getDoctrine()->getManager(); 238 $em = $this->getDoctrine()->getManager();
186 $entry->setDeleted(1); 239 $em->remove($entry);
187 $em->flush(); 240 $em->flush();
188 241
189 return $entry; 242 $json = $this->get('serializer')->serialize($entry, 'json');
243
244 return new Response($json, 200, array('application/json'));
190 } 245 }
191 246
192 /** 247 /**
@@ -200,6 +255,13 @@ class WallabagRestController extends Controller
200 */ 255 */
201 public function getEntriesTagsAction(Entry $entry) 256 public function getEntriesTagsAction(Entry $entry)
202 { 257 {
258 if ($entry->getUser()->getId() != $this->getUser()->getId()) {
259 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
260 }
261
262 $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
263
264 return new Response($json, 200, array('application/json'));
203 } 265 }
204 266
205 /** 267 /**
@@ -214,8 +276,24 @@ class WallabagRestController extends Controller
214 * } 276 * }
215 * ) 277 * )
216 */ 278 */
217 public function postEntriesTagsAction(Entry $entry) 279 public function postEntriesTagsAction(Request $request, Entry $entry)
218 { 280 {
281 if ($entry->getUser()->getId() != $this->getUser()->getId()) {
282 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
283 }
284
285 $tags = $request->request->get('tags', '');
286 if (!empty($tags)) {
287 $this->assignTagsToEntry($entry, $tags);
288 }
289
290 $em = $this->getDoctrine()->getManager();
291 $em->persist($entry);
292 $em->flush();
293
294 $json = $this->get('serializer')->serialize($entry, 'json');
295
296 return new Response($json, 200, array('application/json'));
219 } 297 }
220 298
221 /** 299 /**
@@ -230,29 +308,30 @@ class WallabagRestController extends Controller
230 */ 308 */
231 public function deleteEntriesTagsAction(Entry $entry, Tag $tag) 309 public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
232 { 310 {
311 if ($entry->getUser()->getId() != $this->getUser()->getId()) {
312 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
313 }
314
315 $entry->removeTag($tag);
316 $em = $this->getDoctrine()->getManager();
317 $em->persist($entry);
318 $em->flush();
319
320 $json = $this->get('serializer')->serialize($entry, 'json');
321
322 return new Response($json, 200, array('application/json'));
233 } 323 }
234 324
235 /** 325 /**
236 * Retrieve all tags 326 * Retrieve all tags
237 * 327 *
238 * @ApiDoc( 328 * @ApiDoc()
239 * )
240 */ 329 */
241 public function getTagsAction() 330 public function getTagsAction()
242 { 331 {
243 } 332 $json = $this->get('serializer')->serialize($this->getUser()->getTags(), 'json');
244 333
245 /** 334 return new Response($json, 200, array('application/json'));
246 * Retrieve a single tag
247 *
248 * @ApiDoc(
249 * requirements={
250 * {"name"="tag", "dataType"="string", "requirement"="\w+", "description"="The tag"}
251 * }
252 * )
253 */
254 public function getTagAction(Tag $tag)
255 {
256 } 335 }
257 336
258 /** 337 /**
@@ -266,5 +345,16 @@ class WallabagRestController extends Controller
266 */ 345 */
267 public function deleteTagAction(Tag $tag) 346 public function deleteTagAction(Tag $tag)
268 { 347 {
348 if ($tag->getUser()->getId() != $this->getUser()->getId()) {
349 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$tag->getUser()->getId().', logged user id: '.$this->getUser()->getId());
350 }
351
352 $em = $this->getDoctrine()->getManager();
353 $em->remove($tag);
354 $em->flush();
355
356 $json = $this->get('serializer')->serialize($tag, 'json');
357
358 return new Response($json, 200, array('application/json'));
269 } 359 }
270} 360}
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php
index 3be323ed..ce12ec5d 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php
@@ -6,6 +6,7 @@ use Doctrine\Common\DataFixtures\AbstractFixture;
6use Doctrine\Common\DataFixtures\OrderedFixtureInterface; 6use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
7use Doctrine\Common\Persistence\ObjectManager; 7use Doctrine\Common\Persistence\ObjectManager;
8use Wallabag\CoreBundle\Entity\Entry; 8use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Entity\Tag;
9 10
10class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface 11class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
11{ 12{
@@ -37,10 +38,35 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
37 $entry3->setTitle('test title entry3'); 38 $entry3->setTitle('test title entry3');
38 $entry3->setContent('This is my content /o/'); 39 $entry3->setContent('This is my content /o/');
39 40
41 $tag1 = new Tag($this->getReference('bob-user'));
42 $tag1->setLabel("foo");
43 $tag2 = new Tag($this->getReference('bob-user'));
44 $tag2->setLabel("bar");
45
46 $entry3->addTag($tag1);
47 $entry3->addTag($tag2);
48
40 $manager->persist($entry3); 49 $manager->persist($entry3);
41 50
42 $this->addReference('entry3', $entry3); 51 $this->addReference('entry3', $entry3);
43 52
53 $entry4 = new Entry($this->getReference('admin-user'));
54 $entry4->setUrl('http://0.0.0.0');
55 $entry4->setTitle('test title entry4');
56 $entry4->setContent('This is my content /o/');
57
58 $tag1 = new Tag($this->getReference('admin-user'));
59 $tag1->setLabel("foo");
60 $tag2 = new Tag($this->getReference('admin-user'));
61 $tag2->setLabel("bar");
62
63 $entry4->addTag($tag1);
64 $entry4->addTag($tag2);
65
66 $manager->persist($entry4);
67
68 $this->addReference('entry4', $entry4);
69
44 $manager->flush(); 70 $manager->flush();
45 } 71 }
46 72
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index 937213b4..75aeae84 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -2,19 +2,24 @@
2 2
3namespace Wallabag\CoreBundle\Entity; 3namespace Wallabag\CoreBundle\Entity;
4 4
5use Doctrine\Common\Collections\ArrayCollection;
5use Doctrine\ORM\Mapping as ORM; 6use Doctrine\ORM\Mapping as ORM;
6use Symfony\Component\Validator\Constraints as Assert; 7use Symfony\Component\Validator\Constraints as Assert;
8use Hateoas\Configuration\Annotation as Hateoas;
9use JMS\Serializer\Annotation\XmlRoot;
7 10
8/** 11/**
9 * Entry 12 * Entry
10 * 13 *
14 * @XmlRoot("entry")
11 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository") 15 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository")
12 * @ORM\Table(name="entry") 16 * @ORM\Table(name="entry")
13 * @ORM\HasLifecycleCallbacks() 17 * @ORM\HasLifecycleCallbacks()
14 * 18 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())")
15 */ 19 */
16class Entry 20class Entry
17{ 21{
22 /** @Serializer\XmlAttribute */
18 /** 23 /**
19 * @var integer 24 * @var integer
20 * 25 *
@@ -54,13 +59,6 @@ class Entry
54 private $isStarred = false; 59 private $isStarred = false;
55 60
56 /** 61 /**
57 * @var boolean
58 *
59 * @ORM\Column(name="is_deleted", type="boolean")
60 */
61 private $isDeleted = false;
62
63 /**
64 * @var string 62 * @var string
65 * 63 *
66 * @ORM\Column(name="content", type="text", nullable=true) 64 * @ORM\Column(name="content", type="text", nullable=true)
@@ -121,12 +119,19 @@ class Entry
121 */ 119 */
122 private $user; 120 private $user;
123 121
122 /**
123 * @ORM\ManyToMany(targetEntity="Tag", inversedBy="entries", cascade={"persist"})
124 * @ORM\JoinTable(name="entry_tags")
125 */
126 private $tags;
127
124 /* 128 /*
125 * @param User $user 129 * @param User $user
126 */ 130 */
127 public function __construct(User $user) 131 public function __construct(User $user)
128 { 132 {
129 $this->user = $user; 133 $this->user = $user;
134 $this->tags = new ArrayCollection();
130 } 135 }
131 136
132 /** 137 /**
@@ -279,22 +284,6 @@ class Entry
279 /** 284 /**
280 * @return string 285 * @return string
281 */ 286 */
282 public function isDeleted()
283 {
284 return $this->isDeleted;
285 }
286
287 /**
288 * @param string $isDeleted
289 */
290 public function setDeleted($isDeleted)
291 {
292 $this->isDeleted = $isDeleted;
293 }
294
295 /**
296 * @return string
297 */
298 public function getCreatedAt() 287 public function getCreatedAt()
299 { 288 {
300 return $this->createdAt; 289 return $this->createdAt;
@@ -400,4 +389,26 @@ class Entry
400 { 389 {
401 $this->isPublic = $isPublic; 390 $this->isPublic = $isPublic;
402 } 391 }
392
393 /**
394 * @return ArrayCollection<Tag>
395 */
396 public function getTags()
397 {
398 return $this->tags;
399 }
400
401 /**
402 * @param Tag $tag
403 */
404 public function addTag(Tag $tag)
405 {
406 $this->tags[] = $tag;
407 $tag->addEntry($this);
408 }
409
410 public function removeTag(Tag $tag)
411 {
412 $this->tags->removeElement($tag);
413 }
403} 414}
diff --git a/src/Wallabag/CoreBundle/Entity/Tag.php b/src/Wallabag/CoreBundle/Entity/Tag.php
index 31017563..9ae5867c 100644
--- a/src/Wallabag/CoreBundle/Entity/Tag.php
+++ b/src/Wallabag/CoreBundle/Entity/Tag.php
@@ -3,18 +3,25 @@
3namespace Wallabag\CoreBundle\Entity; 3namespace Wallabag\CoreBundle\Entity;
4 4
5use Doctrine\ORM\Mapping as ORM; 5use Doctrine\ORM\Mapping as ORM;
6use JMS\Serializer\Annotation\XmlRoot;
7use JMS\Serializer\Annotation\ExclusionPolicy;
8use JMS\Serializer\Annotation\Expose;
9use Doctrine\Common\Collections\ArrayCollection;
6 10
7/** 11/**
8 * Tag 12 * Tag
9 * 13 *
14 * @XmlRoot("tag")
10 * @ORM\Table(name="tag") 15 * @ORM\Table(name="tag")
11 * @ORM\Entity 16 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\TagRepository")
17 * @ExclusionPolicy("all")
12 */ 18 */
13class Tag 19class Tag
14{ 20{
15 /** 21 /**
16 * @var integer 22 * @var integer
17 * 23 *
24 * @Expose
18 * @ORM\Column(name="id", type="integer") 25 * @ORM\Column(name="id", type="integer")
19 * @ORM\Id 26 * @ORM\Id
20 * @ORM\GeneratedValue(strategy="AUTO") 27 * @ORM\GeneratedValue(strategy="AUTO")
@@ -24,11 +31,27 @@ class Tag
24 /** 31 /**
25 * @var string 32 * @var string
26 * 33 *
34 * @Expose
27 * @ORM\Column(name="label", type="text") 35 * @ORM\Column(name="label", type="text")
28 */ 36 */
29 private $label; 37 private $label;
30 38
31 /** 39 /**
40 * @ORM\ManyToMany(targetEntity="Entry", mappedBy="tags", cascade={"persist"})
41 */
42 private $entries;
43
44 /**
45 * @ORM\ManyToOne(targetEntity="User", inversedBy="tags")
46 */
47 private $user;
48
49 public function __construct(User $user)
50 {
51 $this->user = $user;
52 $this->entries = new ArrayCollection();
53 }
54 /**
32 * Get id 55 * Get id
33 * 56 *
34 * @return integer 57 * @return integer
@@ -58,6 +81,19 @@ class Tag
58 */ 81 */
59 public function getLabel() 82 public function getLabel()
60 { 83 {
61 return $this->value; 84 return $this->label;
85 }
86
87 public function addEntry(Entry $entry)
88 {
89 $this->entries[] = $entry;
90 }
91
92 /**
93 * @return User
94 */
95 public function getUser()
96 {
97 return $this->user;
62 } 98 }
63} 99}
diff --git a/src/Wallabag/CoreBundle/Entity/TagsEntries.php b/src/Wallabag/CoreBundle/Entity/TagsEntries.php
deleted file mode 100644
index 22826387..00000000
--- a/src/Wallabag/CoreBundle/Entity/TagsEntries.php
+++ /dev/null
@@ -1,93 +0,0 @@
1<?php
2
3namespace Wallabag\CoreBundle\Entity;
4
5use Doctrine\ORM\Mapping as ORM;
6
7/**
8 * TagsEntries
9 *
10 * @ORM\Table(name="tags_entries")
11 * @ORM\Entity
12 */
13class TagsEntries
14{
15 /**
16 * @var integer
17 *
18 * @ORM\Column(name="id", type="integer")
19 * @ORM\Id
20 * @ORM\GeneratedValue(strategy="AUTO")
21 */
22 private $id;
23
24 /**
25 * @var integer
26 *
27 * @ORM\Column(name="entry_id", type="integer")
28 */
29 private $entryId;
30
31 /**
32 * @var integer
33 *
34 * @ORM\Column(name="tag_id", type="integer")
35 */
36 private $tagId;
37
38 /**
39 * Get id
40 *
41 * @return integer
42 */
43 public function getId()
44 {
45 return $this->id;
46 }
47
48 /**
49 * Set entryId
50 *
51 * @param integer $entryId
52 * @return TagsEntries
53 */
54 public function setEntryId($entryId)
55 {
56 $this->entryId = $entryId;
57
58 return $this;
59 }
60
61 /**
62 * Get entryId
63 *
64 * @return integer
65 */
66 public function getEntryId()
67 {
68 return $this->entryId;
69 }
70
71 /**
72 * Set tagId
73 *
74 * @param integer $tagId
75 * @return TagsEntries
76 */
77 public function setTagId($tagId)
78 {
79 $this->tagId = $tagId;
80
81 return $this;
82 }
83
84 /**
85 * Get tagId
86 *
87 * @return integer
88 */
89 public function getTagId()
90 {
91 return $this->tagId;
92 }
93}
diff --git a/src/Wallabag/CoreBundle/Entity/User.php b/src/Wallabag/CoreBundle/Entity/User.php
index ed5cfe53..f05c8760 100644
--- a/src/Wallabag/CoreBundle/Entity/User.php
+++ b/src/Wallabag/CoreBundle/Entity/User.php
@@ -7,6 +7,8 @@ use Doctrine\ORM\Mapping as ORM;
7use Symfony\Component\Security\Core\User\UserInterface; 7use Symfony\Component\Security\Core\User\UserInterface;
8use Symfony\Component\Security\Core\User\AdvancedUserInterface; 8use Symfony\Component\Security\Core\User\AdvancedUserInterface;
9use Symfony\Component\Validator\Constraints as Assert; 9use Symfony\Component\Validator\Constraints as Assert;
10use JMS\Serializer\Annotation\ExclusionPolicy;
11use JMS\Serializer\Annotation\Expose;
10 12
11/** 13/**
12 * User 14 * User
@@ -14,12 +16,14 @@ use Symfony\Component\Validator\Constraints as Assert;
14 * @ORM\Table(name="user") 16 * @ORM\Table(name="user")
15 * @ORM\Entity 17 * @ORM\Entity
16 * @ORM\HasLifecycleCallbacks() 18 * @ORM\HasLifecycleCallbacks()
19 * @ExclusionPolicy("all")
17 */ 20 */
18class User implements AdvancedUserInterface, \Serializable 21class User implements AdvancedUserInterface, \Serializable
19{ 22{
20 /** 23 /**
21 * @var integer 24 * @var integer
22 * 25 *
26 * @Expose
23 * @ORM\Column(name="id", type="integer") 27 * @ORM\Column(name="id", type="integer")
24 * @ORM\Id 28 * @ORM\Id
25 * @ORM\GeneratedValue(strategy="AUTO") 29 * @ORM\GeneratedValue(strategy="AUTO")
@@ -97,10 +101,17 @@ class User implements AdvancedUserInterface, \Serializable
97 */ 101 */
98 private $config; 102 private $config;
99 103
104 /**
105 * @ORM\OneToMany(targetEntity="Tag", mappedBy="user", cascade={"remove"})
106 */
107 private $tags;
108
100 public function __construct() 109 public function __construct()
101 { 110 {
102 $this->salt = md5(uniqid(null, true)); 111 $this->isActive = true;
103 $this->entries = new ArrayCollection(); 112 $this->salt = md5(uniqid(null, true));
113 $this->entries = new ArrayCollection();
114 $this->tags = new ArrayCollection();
104 } 115 }
105 116
106 /** 117 /**
@@ -275,6 +286,25 @@ class User implements AdvancedUserInterface, \Serializable
275 } 286 }
276 287
277 /** 288 /**
289 * @param Entry $entry
290 *
291 * @return User
292 */
293 public function addTag(Tag $tag)
294 {
295 $this->tags[] = $tag;
296
297 return $this;
298 }
299
300 /**
301 * @return ArrayCollection<Tag>
302 */
303 public function getTags()
304 {
305 return $this->tags;
306 }
307 /**
278 * @inheritDoc 308 * @inheritDoc
279 */ 309 */
280 public function eraseCredentials() 310 public function eraseCredentials()
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index bedc90d2..53e8e2ba 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -4,6 +4,8 @@ namespace Wallabag\CoreBundle\Repository;
4 4
5use Doctrine\ORM\EntityRepository; 5use Doctrine\ORM\EntityRepository;
6use Doctrine\ORM\Tools\Pagination\Paginator; 6use Doctrine\ORM\Tools\Pagination\Paginator;
7use Pagerfanta\Adapter\DoctrineORMAdapter;
8use Pagerfanta\Pagerfanta;
7 9
8class EntryRepository extends EntityRepository 10class EntryRepository extends EntityRepository
9{ 11{
@@ -24,7 +26,6 @@ class EntryRepository extends EntityRepository
24 ->leftJoin('e.user', 'u') 26 ->leftJoin('e.user', 'u')
25 ->where('e.isArchived = false') 27 ->where('e.isArchived = false')
26 ->andWhere('u.id =:userId')->setParameter('userId', $userId) 28 ->andWhere('u.id =:userId')->setParameter('userId', $userId)
27 ->andWhere('e.isDeleted=false')
28 ->orderBy('e.createdAt', 'desc') 29 ->orderBy('e.createdAt', 'desc')
29 ->getQuery(); 30 ->getQuery();
30 31
@@ -51,7 +52,6 @@ class EntryRepository extends EntityRepository
51 ->leftJoin('e.user', 'u') 52 ->leftJoin('e.user', 'u')
52 ->where('e.isArchived = true') 53 ->where('e.isArchived = true')
53 ->andWhere('u.id =:userId')->setParameter('userId', $userId) 54 ->andWhere('u.id =:userId')->setParameter('userId', $userId)
54 ->andWhere('e.isDeleted=false')
55 ->orderBy('e.createdAt', 'desc') 55 ->orderBy('e.createdAt', 'desc')
56 ->getQuery(); 56 ->getQuery();
57 57
@@ -78,7 +78,6 @@ class EntryRepository extends EntityRepository
78 ->leftJoin('e.user', 'u') 78 ->leftJoin('e.user', 'u')
79 ->where('e.isStarred = true') 79 ->where('e.isStarred = true')
80 ->andWhere('u.id =:userId')->setParameter('userId', $userId) 80 ->andWhere('u.id =:userId')->setParameter('userId', $userId)
81 ->andWhere('e.isDeleted = false')
82 ->orderBy('e.createdAt', 'desc') 81 ->orderBy('e.createdAt', 'desc')
83 ->getQuery(); 82 ->getQuery();
84 83
@@ -93,17 +92,15 @@ class EntryRepository extends EntityRepository
93 * @param int $userId 92 * @param int $userId
94 * @param bool $isArchived 93 * @param bool $isArchived
95 * @param bool $isStarred 94 * @param bool $isStarred
96 * @param bool $isDeleted
97 * @param string $sort 95 * @param string $sort
98 * @param string $order 96 * @param string $order
99 * 97 *
100 * @return array 98 * @return array
101 */ 99 */
102 public function findEntries($userId, $isArchived = null, $isStarred = null, $isDeleted = null, $sort = 'created', $order = 'ASC') 100 public function findEntries($userId, $isArchived = null, $isStarred = null, $sort = 'created', $order = 'ASC')
103 { 101 {
104 $qb = $this->createQueryBuilder('e') 102 $qb = $this->createQueryBuilder('e')
105 ->leftJoin('e.user', 'u') 103 ->where('e.user =:userId')->setParameter('userId', $userId);
106 ->where('u.id =:userId')->setParameter('userId', $userId);
107 104
108 if (null !== $isArchived) { 105 if (null !== $isArchived) {
109 $qb->andWhere('e.isArchived =:isArchived')->setParameter('isArchived', (bool) $isArchived); 106 $qb->andWhere('e.isArchived =:isArchived')->setParameter('isArchived', (bool) $isArchived);
@@ -113,18 +110,31 @@ class EntryRepository extends EntityRepository
113 $qb->andWhere('e.isStarred =:isStarred')->setParameter('isStarred', (bool) $isStarred); 110 $qb->andWhere('e.isStarred =:isStarred')->setParameter('isStarred', (bool) $isStarred);
114 } 111 }
115 112
116 if (null !== $isDeleted) {
117 $qb->andWhere('e.isDeleted =:isDeleted')->setParameter('isDeleted', (bool) $isDeleted);
118 }
119
120 if ('created' === $sort) { 113 if ('created' === $sort) {
121 $qb->orderBy('e.createdAt', $order); 114 $qb->orderBy('e.createdAt', $order);
122 } elseif ('updated' === $sort) { 115 } elseif ('updated' === $sort) {
123 $qb->orderBy('e.updatedAt', $order); 116 $qb->orderBy('e.updatedAt', $order);
124 } 117 }
125 118
126 return $qb 119 $pagerAdapter = new DoctrineORMAdapter($qb);
127 ->getQuery() 120
128 ->getResult(); 121 return new Pagerfanta($pagerAdapter);
122 }
123
124 /**
125 * Fetch an entry with a tag. Only used for tests.
126 *
127 * @return Entry
128 */
129 public function findOneWithTags($userId)
130 {
131 $qb = $this->createQueryBuilder('e')
132 ->innerJoin('e.tags', 't')
133 ->innerJoin('e.user', 'u')
134 ->addSelect('t', 'u')
135 ->where('e.user=:userId')->setParameter('userId', $userId)
136 ;
137
138 return $qb->getQuery()->getResult();
129 } 139 }
130} 140}
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php
new file mode 100644
index 00000000..52f319f1
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php
@@ -0,0 +1,9 @@
1<?php
2
3namespace Wallabag\CoreBundle\Repository;
4
5use Doctrine\ORM\EntityRepository;
6
7class TagRepository extends EntityRepository
8{
9}
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
index 2634141e..99a3a945 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
@@ -161,18 +161,15 @@ class EntryControllerTest extends WallabagTestCase
161 $content = $client->getContainer() 161 $content = $client->getContainer()
162 ->get('doctrine.orm.entity_manager') 162 ->get('doctrine.orm.entity_manager')
163 ->getRepository('WallabagCoreBundle:Entry') 163 ->getRepository('WallabagCoreBundle:Entry')
164 ->findOneByIsDeleted(false); 164 ->findOneById(1);
165 165
166 $client->request('GET', '/delete/'.$content->getId()); 166 $client->request('GET', '/delete/'.$content->getId());
167 167
168 $this->assertEquals(302, $client->getResponse()->getStatusCode()); 168 $this->assertEquals(302, $client->getResponse()->getStatusCode());
169 169
170 $res = $client->getContainer() 170 $client->request('GET', '/delete/'.$content->getId());
171 ->get('doctrine.orm.entity_manager')
172 ->getRepository('WallabagCoreBundle:Entry')
173 ->findOneById($content->getId());
174 171
175 $this->assertEquals($res->isDeleted(), true); 172 $this->assertEquals(404, $client->getResponse()->getStatusCode());
176 } 173 }
177 174
178 public function testViewOtherUserEntry() 175 public function testViewOtherUserEntry()
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
index fcfa8ccf..0eca7cde 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
@@ -44,10 +44,6 @@ class WallabagRestControllerTest extends WallabagTestCase
44 public function testWithBadHeaders() 44 public function testWithBadHeaders()
45 { 45 {
46 $client = $this->createClient(); 46 $client = $this->createClient();
47 $client->request('GET', '/api/salts/admin.json');
48 $salt = json_decode($client->getResponse()->getContent());
49
50 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
51 47
52 $entry = $client->getContainer() 48 $entry = $client->getContainer()
53 ->get('doctrine.orm.entity_manager') 49 ->get('doctrine.orm.entity_manager')
@@ -130,7 +126,7 @@ class WallabagRestControllerTest extends WallabagTestCase
130 $entry = $client->getContainer() 126 $entry = $client->getContainer()
131 ->get('doctrine.orm.entity_manager') 127 ->get('doctrine.orm.entity_manager')
132 ->getRepository('WallabagCoreBundle:Entry') 128 ->getRepository('WallabagCoreBundle:Entry')
133 ->findOneByIsDeleted(false); 129 ->findOneByUser(1);
134 130
135 if (!$entry) { 131 if (!$entry) {
136 $this->markTestSkipped('No content found in db.'); 132 $this->markTestSkipped('No content found in db.');
@@ -140,10 +136,79 @@ class WallabagRestControllerTest extends WallabagTestCase
140 136
141 $this->assertEquals(200, $client->getResponse()->getStatusCode()); 137 $this->assertEquals(200, $client->getResponse()->getStatusCode());
142 138
143 $res = $client->getContainer() 139 // We'll try to delete this entry again
140 $client->request('GET', '/api/salts/admin.json');
141 $salt = json_decode($client->getResponse()->getContent());
142
143 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
144
145 $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
146
147 $this->assertEquals(404, $client->getResponse()->getStatusCode());
148 }
149
150 public function testGetTagsEntry()
151 {
152 $client = $this->createClient();
153 $client->request('GET', '/api/salts/admin.json');
154 $salt = json_decode($client->getResponse()->getContent());
155 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
156
157 $entry = $client->getContainer()
158 ->get('doctrine.orm.entity_manager')
159 ->getRepository('WallabagCoreBundle:Entry')
160 ->findOneWithTags(1);
161
162 $entry = $entry[0];
163
164 if (!$entry) {
165 $this->markTestSkipped('No content found in db.');
166 }
167
168 $tags = array();
169 foreach ($entry->getTags() as $tag) {
170 $tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel());
171 }
172
173 $client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers);
174
175 $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent());
176 }
177
178 public function testPostTagsOnEntry()
179 {
180 $client = $this->createClient();
181 $client->request('GET', '/api/salts/admin.json');
182 $salt = json_decode($client->getResponse()->getContent());
183 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
184
185 $entry = $client->getContainer()
186 ->get('doctrine.orm.entity_manager')
187 ->getRepository('WallabagCoreBundle:Entry')
188 ->findOneByUser(1);
189
190 if (!$entry) {
191 $this->markTestSkipped('No content found in db.');
192 }
193
194 $newTags = 'tag1,tag2,tag3';
195
196 $client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers);
197
198 $this->assertEquals(200, $client->getResponse()->getStatusCode());
199
200 $entryDB = $client->getContainer()
144 ->get('doctrine.orm.entity_manager') 201 ->get('doctrine.orm.entity_manager')
145 ->getRepository('WallabagCoreBundle:Entry') 202 ->getRepository('WallabagCoreBundle:Entry')
146 ->findOneById($entry->getId()); 203 ->find($entry->getId());
147 $this->assertEquals($res->isDeleted(), true); 204
205 $tagsInDB = array();
206 foreach ($entryDB->getTags()->toArray() as $tag) {
207 $tagsInDB[$tag->getId()] = $tag->getLabel();
208 }
209
210 foreach (explode(',', $newTags) as $tag) {
211 $this->assertContains($tag, $tagsInDB);
212 }
148 } 213 }
149} 214}