aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/bookmark/LinkUtils.php2
-rw-r--r--application/container/ShaarliContainer.php1
-rw-r--r--application/formatter/BookmarkMarkdownFormatter.php4
-rw-r--r--application/front/controllers/TagController.php74
-rw-r--r--composer.lock207
-rw-r--r--index.php1
-rw-r--r--tests/bookmark/LinkUtilsTest.php4
-rw-r--r--tests/feed/FeedBuilderTest.php2
-rw-r--r--tests/formatter/BookmarkDefaultFormatterTest.php4
-rw-r--r--tests/formatter/BookmarkMarkdownFormatterTest.php4
-rw-r--r--tests/front/controller/TagControllerTest.php191
-rw-r--r--tests/plugins/resources/hashtags.md10
-rw-r--r--tests/plugins/resources/hashtags.raw10
-rw-r--r--tests/plugins/resources/markdown.html33
-rw-r--r--tests/plugins/resources/markdown.md34
-rw-r--r--tpl/default/linklist.html2
-rw-r--r--tpl/default/tag.cloud.html2
-rw-r--r--tpl/default/tag.list.html2
-rw-r--r--tpl/vintage/linklist.html2
-rw-r--r--tpl/vintage/tag.cloud.html2
20 files changed, 407 insertions, 184 deletions
diff --git a/application/bookmark/LinkUtils.php b/application/bookmark/LinkUtils.php
index 88379430..98d9038a 100644
--- a/application/bookmark/LinkUtils.php
+++ b/application/bookmark/LinkUtils.php
@@ -220,7 +220,7 @@ function hashtag_autolink($description, $indexUrl = '')
220 * \p{Mn} - any non marking space (accents, umlauts, etc) 220 * \p{Mn} - any non marking space (accents, umlauts, etc)
221 */ 221 */
222 $regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui'; 222 $regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui';
223 $replacement = '$1<a href="'. $indexUrl .'?addtag=$2" title="Hashtag $2">#$2</a>'; 223 $replacement = '$1<a href="'. $indexUrl .'./add-tag/$2" title="Hashtag $2">#$2</a>';
224 return preg_replace($regex, $replacement, $description); 224 return preg_replace($regex, $replacement, $description);
225} 225}
226 226
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php
index af62e574..3995f669 100644
--- a/application/container/ShaarliContainer.php
+++ b/application/container/ShaarliContainer.php
@@ -18,6 +18,7 @@ use Slim\Container;
18/** 18/**
19 * Extension of Slim container to document the injected objects. 19 * Extension of Slim container to document the injected objects.
20 * 20 *
21 * @property mixed[] $environment $_SERVER automatically injected by Slim
21 * @property ConfigManager $conf 22 * @property ConfigManager $conf
22 * @property SessionManager $sessionManager 23 * @property SessionManager $sessionManager
23 * @property LoginManager $loginManager 24 * @property LoginManager $loginManager
diff --git a/application/formatter/BookmarkMarkdownFormatter.php b/application/formatter/BookmarkMarkdownFormatter.php
index 077e5312..5d244d4c 100644
--- a/application/formatter/BookmarkMarkdownFormatter.php
+++ b/application/formatter/BookmarkMarkdownFormatter.php
@@ -114,7 +114,7 @@ class BookmarkMarkdownFormatter extends BookmarkDefaultFormatter
114 114
115 /** 115 /**
116 * Replace hashtag in Markdown links format 116 * Replace hashtag in Markdown links format
117 * E.g. `#hashtag` becomes `[#hashtag](?addtag=hashtag)` 117 * E.g. `#hashtag` becomes `[#hashtag](./add-tag/hashtag)`
118 * It includes the index URL if specified. 118 * It includes the index URL if specified.
119 * 119 *
120 * @param string $description 120 * @param string $description
@@ -133,7 +133,7 @@ class BookmarkMarkdownFormatter extends BookmarkDefaultFormatter
133 * \p{Mn} - any non marking space (accents, umlauts, etc) 133 * \p{Mn} - any non marking space (accents, umlauts, etc)
134 */ 134 */
135 $regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui'; 135 $regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui';
136 $replacement = '$1[#$2]('. $indexUrl .'?addtag=$2)'; 136 $replacement = '$1[#$2]('. $indexUrl .'./add-tag/$2)';
137 137
138 $descriptionLines = explode(PHP_EOL, $description); 138 $descriptionLines = explode(PHP_EOL, $description);
139 $descriptionOut = ''; 139 $descriptionOut = '';
diff --git a/application/front/controllers/TagController.php b/application/front/controllers/TagController.php
new file mode 100644
index 00000000..598275b0
--- /dev/null
+++ b/application/front/controllers/TagController.php
@@ -0,0 +1,74 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use Slim\Http\Request;
8use Slim\Http\Response;
9
10/**
11 * Class TagController
12 *
13 * Slim controller handle tags.
14 *
15 * @package Front\Controller
16 */
17class TagController extends ShaarliController
18{
19 /**
20 * Add another tag in the current search through an HTTP redirection.
21 *
22 * @param array $args Should contain `newTag` key as tag to add to current search
23 */
24 public function addTag(Request $request, Response $response, array $args): Response
25 {
26 $newTag = $args['newTag'] ?? null;
27 $referer = $this->container->environment['HTTP_REFERER'] ?? null;
28
29 // In case browser does not send HTTP_REFERER, we search a single tag
30 if (null === $referer) {
31 if (null !== $newTag) {
32 return $response->withRedirect('./?searchtags='. urlencode($newTag));
33 }
34
35 return $response->withRedirect('./');
36 }
37
38 $currentUrl = parse_url($this->container->environment['HTTP_REFERER']);
39 parse_str($currentUrl['query'] ?? '', $params);
40
41 if (null === $newTag) {
42 return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params));
43 }
44
45 // Prevent redirection loop
46 if (isset($params['addtag'])) {
47 unset($params['addtag']);
48 }
49
50 // Check if this tag is already in the search query and ignore it if it is.
51 // Each tag is always separated by a space
52 $currentTags = isset($params['searchtags']) ? explode(' ', $params['searchtags']) : [];
53
54 $addtag = true;
55 foreach ($currentTags as $value) {
56 if ($value === $newTag) {
57 $addtag = false;
58 break;
59 }
60 }
61
62 // Append the tag if necessary
63 if (true === $addtag) {
64 $currentTags[] = trim($newTag);
65 }
66
67 $params['searchtags'] = trim(implode(' ', $currentTags));
68
69 // We also remove page (keeping the same page has no sense, since the results are different)
70 unset($params['page']);
71
72 return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params));
73 }
74}
diff --git a/composer.lock b/composer.lock
index b3373a32..fccc741c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -508,16 +508,16 @@
508 }, 508 },
509 { 509 {
510 "name": "psr/log", 510 "name": "psr/log",
511 "version": "1.1.2", 511 "version": "1.1.3",
512 "source": { 512 "source": {
513 "type": "git", 513 "type": "git",
514 "url": "https://github.com/php-fig/log.git", 514 "url": "https://github.com/php-fig/log.git",
515 "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" 515 "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
516 }, 516 },
517 "dist": { 517 "dist": {
518 "type": "zip", 518 "type": "zip",
519 "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", 519 "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
520 "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", 520 "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
521 "shasum": "" 521 "shasum": ""
522 }, 522 },
523 "require": { 523 "require": {
@@ -551,7 +551,7 @@
551 "psr", 551 "psr",
552 "psr-3" 552 "psr-3"
553 ], 553 ],
554 "time": "2019-11-01T11:05:21+00:00" 554 "time": "2020-03-23T09:12:05+00:00"
555 }, 555 },
556 { 556 {
557 "name": "pubsubhubbub/publisher", 557 "name": "pubsubhubbub/publisher",
@@ -936,24 +936,21 @@
936 }, 936 },
937 { 937 {
938 "name": "phpdocumentor/reflection-common", 938 "name": "phpdocumentor/reflection-common",
939 "version": "2.0.0", 939 "version": "2.1.0",
940 "source": { 940 "source": {
941 "type": "git", 941 "type": "git",
942 "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 942 "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
943 "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" 943 "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b"
944 }, 944 },
945 "dist": { 945 "dist": {
946 "type": "zip", 946 "type": "zip",
947 "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", 947 "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
948 "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", 948 "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
949 "shasum": "" 949 "shasum": ""
950 }, 950 },
951 "require": { 951 "require": {
952 "php": ">=7.1" 952 "php": ">=7.1"
953 }, 953 },
954 "require-dev": {
955 "phpunit/phpunit": "~6"
956 },
957 "type": "library", 954 "type": "library",
958 "extra": { 955 "extra": {
959 "branch-alias": { 956 "branch-alias": {
@@ -984,7 +981,7 @@
984 "reflection", 981 "reflection",
985 "static analysis" 982 "static analysis"
986 ], 983 ],
987 "time": "2018-08-07T13:53:10+00:00" 984 "time": "2020-04-27T09:25:28+00:00"
988 }, 985 },
989 { 986 {
990 "name": "phpdocumentor/reflection-docblock", 987 "name": "phpdocumentor/reflection-docblock",
@@ -1087,24 +1084,24 @@
1087 }, 1084 },
1088 { 1085 {
1089 "name": "phpspec/prophecy", 1086 "name": "phpspec/prophecy",
1090 "version": "1.10.1", 1087 "version": "v1.10.3",
1091 "source": { 1088 "source": {
1092 "type": "git", 1089 "type": "git",
1093 "url": "https://github.com/phpspec/prophecy.git", 1090 "url": "https://github.com/phpspec/prophecy.git",
1094 "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" 1091 "reference": "451c3cd1418cf640de218914901e51b064abb093"
1095 }, 1092 },
1096 "dist": { 1093 "dist": {
1097 "type": "zip", 1094 "type": "zip",
1098 "url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc", 1095 "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
1099 "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc", 1096 "reference": "451c3cd1418cf640de218914901e51b064abb093",
1100 "shasum": "" 1097 "shasum": ""
1101 }, 1098 },
1102 "require": { 1099 "require": {
1103 "doctrine/instantiator": "^1.0.2", 1100 "doctrine/instantiator": "^1.0.2",
1104 "php": "^5.3|^7.0", 1101 "php": "^5.3|^7.0",
1105 "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", 1102 "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
1106 "sebastian/comparator": "^1.2.3|^2.0|^3.0", 1103 "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
1107 "sebastian/recursion-context": "^1.0|^2.0|^3.0" 1104 "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
1108 }, 1105 },
1109 "require-dev": { 1106 "require-dev": {
1110 "phpspec/phpspec": "^2.5 || ^3.2", 1107 "phpspec/phpspec": "^2.5 || ^3.2",
@@ -1146,7 +1143,7 @@
1146 "spy", 1143 "spy",
1147 "stub" 1144 "stub"
1148 ], 1145 ],
1149 "time": "2019-12-22T21:05:45+00:00" 1146 "time": "2020-03-05T15:02:03+00:00"
1150 }, 1147 },
1151 { 1148 {
1152 "name": "phpunit/php-code-coverage", 1149 "name": "phpunit/php-code-coverage",
@@ -1501,12 +1498,12 @@
1501 "source": { 1498 "source": {
1502 "type": "git", 1499 "type": "git",
1503 "url": "https://github.com/Roave/SecurityAdvisories.git", 1500 "url": "https://github.com/Roave/SecurityAdvisories.git",
1504 "reference": "67ac6ea8f4a078c3c9b7aec5d7ae70f098c37389" 1501 "reference": "ec1a75b10126327b351fdea7c2b9bfb94e2f6f35"
1505 }, 1502 },
1506 "dist": { 1503 "dist": {
1507 "type": "zip", 1504 "type": "zip",
1508 "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/67ac6ea8f4a078c3c9b7aec5d7ae70f098c37389", 1505 "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ec1a75b10126327b351fdea7c2b9bfb94e2f6f35",
1509 "reference": "67ac6ea8f4a078c3c9b7aec5d7ae70f098c37389", 1506 "reference": "ec1a75b10126327b351fdea7c2b9bfb94e2f6f35",
1510 "shasum": "" 1507 "shasum": ""
1511 }, 1508 },
1512 "conflict": { 1509 "conflict": {
@@ -1518,11 +1515,17 @@
1518 "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", 1515 "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6",
1519 "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", 1516 "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
1520 "aws/aws-sdk-php": ">=3,<3.2.1", 1517 "aws/aws-sdk-php": ">=3,<3.2.1",
1518 "bagisto/bagisto": "<0.1.5",
1519 "barrelstrength/sprout-base-email": "<3.9",
1520 "bolt/bolt": "<3.6.10",
1521 "brightlocal/phpwhois": "<=4.2.5", 1521 "brightlocal/phpwhois": "<=4.2.5",
1522 "buddypress/buddypress": "<5.1.2",
1522 "bugsnag/bugsnag-laravel": ">=2,<2.0.2", 1523 "bugsnag/bugsnag-laravel": ">=2,<2.0.2",
1523 "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7", 1524 "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7",
1524 "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", 1525 "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
1525 "cartalyst/sentry": "<=2.1.6", 1526 "cartalyst/sentry": "<=2.1.6",
1527 "centreon/centreon": "<18.10.8|>=19,<19.4.5",
1528 "cesnet/simplesamlphp-module-proxystatistics": "<3.1",
1526 "codeigniter/framework": "<=3.0.6", 1529 "codeigniter/framework": "<=3.0.6",
1527 "composer/composer": "<=1-alpha.11", 1530 "composer/composer": "<=1-alpha.11",
1528 "contao-components/mediaelement": ">=2.14.2,<2.21.1", 1531 "contao-components/mediaelement": ">=2.14.2,<2.21.1",
@@ -1540,22 +1543,32 @@
1540 "doctrine/mongodb-odm": ">=1,<1.0.2", 1543 "doctrine/mongodb-odm": ">=1,<1.0.2",
1541 "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", 1544 "doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
1542 "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", 1545 "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1",
1546 "dolibarr/dolibarr": "<=10.0.6",
1543 "dompdf/dompdf": ">=0.6,<0.6.2", 1547 "dompdf/dompdf": ">=0.6,<0.6.2",
1544 "drupal/core": ">=7,<7.69|>=8,<8.7.11|>=8.8,<8.8.1", 1548 "drupal/core": ">=7,<7.69|>=8,<8.7.12|>=8.8,<8.8.4",
1545 "drupal/drupal": ">=7,<7.69|>=8,<8.7.11|>=8.8,<8.8.1", 1549 "drupal/drupal": ">=7,<7.69|>=8,<8.7.12|>=8.8,<8.8.4",
1546 "endroid/qr-code-bundle": "<3.4.2", 1550 "endroid/qr-code-bundle": "<3.4.2",
1551 "enshrined/svg-sanitize": "<0.13.1",
1547 "erusev/parsedown": "<1.7.2", 1552 "erusev/parsedown": "<1.7.2",
1548 "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.4", 1553 "ezsystems/demobundle": ">=5.4,<5.4.6.1",
1549 "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.13.1|>=6,<6.7.9.1|>=6.8,<6.13.5.1|>=7,<7.2.4.1|>=7.3,<7.3.2.1", 1554 "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1",
1550 "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.12.3|>=2011,<2017.12.4.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3", 1555 "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1",
1556 "ezsystems/ezplatform": ">=1.7,<1.7.9.1|>=1.13,<1.13.5.1|>=2.5,<2.5.4",
1557 "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6",
1558 "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2",
1559 "ezsystems/ezplatform-user": ">=1,<1.0.1",
1560 "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.1|>=6,<6.7.9.1|>=6.8,<6.13.6.2|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<7.5.6.2",
1561 "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.1|>=2011,<2017.12.7.2|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.4.2",
1551 "ezsystems/repository-forms": ">=2.3,<2.3.2.1", 1562 "ezsystems/repository-forms": ">=2.3,<2.3.2.1",
1552 "ezyang/htmlpurifier": "<4.1.1", 1563 "ezyang/htmlpurifier": "<4.1.1",
1553 "firebase/php-jwt": "<2", 1564 "firebase/php-jwt": "<2",
1554 "fooman/tcpdf": "<6.2.22", 1565 "fooman/tcpdf": "<6.2.22",
1555 "fossar/tcpdf-parser": "<6.2.22", 1566 "fossar/tcpdf-parser": "<6.2.22",
1567 "friendsofsymfony/oauth2-php": "<1.3",
1556 "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", 1568 "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
1557 "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", 1569 "friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
1558 "fuel/core": "<1.8.1", 1570 "fuel/core": "<1.8.1",
1571 "getgrav/grav": "<1.7-beta.8",
1559 "gree/jose": "<=2.2", 1572 "gree/jose": "<=2.2",
1560 "gregwar/rst": "<1.0.3", 1573 "gregwar/rst": "<1.0.3",
1561 "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", 1574 "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1",
@@ -1563,6 +1576,7 @@
1563 "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", 1576 "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
1564 "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", 1577 "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29",
1565 "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", 1578 "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15",
1579 "illuminate/view": ">=7,<7.1.2",
1566 "ivankristianto/phpwhois": "<=4.3", 1580 "ivankristianto/phpwhois": "<=4.3",
1567 "james-heinrich/getid3": "<1.9.9", 1581 "james-heinrich/getid3": "<1.9.9",
1568 "joomla/session": "<1.3.1", 1582 "joomla/session": "<1.3.1",
@@ -1570,15 +1584,19 @@
1570 "kazist/phpwhois": "<=4.2.6", 1584 "kazist/phpwhois": "<=4.2.6",
1571 "kreait/firebase-php": ">=3.2,<3.8.1", 1585 "kreait/firebase-php": ">=3.2,<3.8.1",
1572 "la-haute-societe/tcpdf": "<6.2.22", 1586 "la-haute-societe/tcpdf": "<6.2.22",
1573 "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", 1587 "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30|>=7,<7.1.2",
1574 "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", 1588 "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
1575 "league/commonmark": "<0.18.3", 1589 "league/commonmark": "<0.18.3",
1590 "librenms/librenms": "<1.53",
1591 "magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3",
1576 "magento/magento1ce": "<1.9.4.3", 1592 "magento/magento1ce": "<1.9.4.3",
1577 "magento/magento1ee": ">=1,<1.14.4.3", 1593 "magento/magento1ee": ">=1,<1.14.4.3",
1578 "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", 1594 "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2",
1579 "monolog/monolog": ">=1.8,<1.12", 1595 "monolog/monolog": ">=1.8,<1.12",
1580 "namshi/jose": "<2.2", 1596 "namshi/jose": "<2.2",
1597 "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1",
1581 "onelogin/php-saml": "<2.10.4", 1598 "onelogin/php-saml": "<2.10.4",
1599 "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5",
1582 "openid/php-openid": "<2.3", 1600 "openid/php-openid": "<2.3",
1583 "oro/crm": ">=1.7,<1.7.4", 1601 "oro/crm": ">=1.7,<1.7.4",
1584 "oro/platform": ">=1.7,<1.7.4", 1602 "oro/platform": ">=1.7,<1.7.4",
@@ -1587,49 +1605,67 @@
1587 "paragonie/random_compat": "<2", 1605 "paragonie/random_compat": "<2",
1588 "paypal/merchant-sdk-php": "<3.12", 1606 "paypal/merchant-sdk-php": "<3.12",
1589 "pear/archive_tar": "<1.4.4", 1607 "pear/archive_tar": "<1.4.4",
1608 "phpfastcache/phpfastcache": ">=5,<5.0.13",
1590 "phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6", 1609 "phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6",
1591 "phpoffice/phpexcel": "<=1.8.1", 1610 "phpmyadmin/phpmyadmin": "<4.9.2",
1592 "phpoffice/phpspreadsheet": "<=1.5", 1611 "phpoffice/phpexcel": "<1.8.2",
1612 "phpoffice/phpspreadsheet": "<1.8",
1593 "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", 1613 "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3",
1594 "phpwhois/phpwhois": "<=4.2.5", 1614 "phpwhois/phpwhois": "<=4.2.5",
1595 "phpxmlrpc/extras": "<0.6.1", 1615 "phpxmlrpc/extras": "<0.6.1",
1616 "pimcore/pimcore": "<6.3",
1617 "prestashop/autoupgrade": ">=4,<4.10.1",
1618 "prestashop/gamification": "<2.3.2",
1619 "prestashop/ps_facetedsearch": "<3.4.1",
1620 "privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2",
1596 "propel/propel": ">=2-alpha.1,<=2-alpha.7", 1621 "propel/propel": ">=2-alpha.1,<=2-alpha.7",
1597 "propel/propel1": ">=1,<=1.7.1", 1622 "propel/propel1": ">=1,<=1.7.1",
1598 "pusher/pusher-php-server": "<2.2.1", 1623 "pusher/pusher-php-server": "<2.2.1",
1599 "robrichards/xmlseclibs": ">=1,<3.0.4", 1624 "robrichards/xmlseclibs": "<3.0.4",
1600 "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", 1625 "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
1601 "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", 1626 "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11",
1602 "sensiolabs/connect": "<4.2.3", 1627 "sensiolabs/connect": "<4.2.3",
1603 "serluck/phpwhois": "<=4.2.6", 1628 "serluck/phpwhois": "<=4.2.6",
1604 "shopware/shopware": "<5.3.7", 1629 "shopware/shopware": "<5.3.7",
1605 "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", 1630 "silverstripe/admin": ">=1.0.3,<1.0.4|>=1.1,<1.1.1",
1631 "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2",
1632 "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4",
1633 "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1",
1606 "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", 1634 "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
1607 "silverstripe/framework": ">=3,<3.6.7|>=3.7,<3.7.3|>=4,<4.4", 1635 "silverstripe/framework": "<4.4.5|>=4.5,<4.5.2",
1608 "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.1.2", 1636 "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.1.2",
1609 "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", 1637 "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1",
1610 "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", 1638 "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4",
1639 "silverstripe/subsites": ">=2,<2.1.1",
1640 "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1",
1611 "silverstripe/userforms": "<3", 1641 "silverstripe/userforms": "<3",
1612 "simple-updates/phpwhois": "<=1", 1642 "simple-updates/phpwhois": "<=1",
1613 "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", 1643 "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
1614 "simplesamlphp/simplesamlphp": "<1.17.8", 1644 "simplesamlphp/simplesamlphp": "<1.18.6",
1615 "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", 1645 "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1",
1646 "simplito/elliptic-php": "<1.0.6",
1616 "slim/slim": "<2.6", 1647 "slim/slim": "<2.6",
1617 "smarty/smarty": "<3.1.33", 1648 "smarty/smarty": "<3.1.33",
1618 "socalnick/scn-social-auth": "<1.15.2", 1649 "socalnick/scn-social-auth": "<1.15.2",
1619 "spoonity/tcpdf": "<6.2.22", 1650 "spoonity/tcpdf": "<6.2.22",
1620 "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", 1651 "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
1652 "ssddanbrown/bookstack": "<0.29.2",
1621 "stormpath/sdk": ">=0,<9.9.99", 1653 "stormpath/sdk": ">=0,<9.9.99",
1622 "studio-42/elfinder": "<2.1.48", 1654 "studio-42/elfinder": "<2.1.49",
1623 "swiftmailer/swiftmailer": ">=4,<5.4.5", 1655 "swiftmailer/swiftmailer": ">=4,<5.4.5",
1624 "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", 1656 "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2",
1625 "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", 1657 "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1",
1626 "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", 1658 "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1",
1627 "sylius/sylius": ">=1,<1.1.18|>=1.2,<1.2.17|>=1.3,<1.3.12|>=1.4,<1.4.4", 1659 "sylius/resource-bundle": "<1.3.13|>=1.4,<1.4.6|>=1.5,<1.5.1|>=1.6,<1.6.3",
1660 "sylius/sylius": "<1.3.16|>=1.4,<1.4.12|>=1.5,<1.5.9|>=1.6,<1.6.5",
1661 "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99",
1662 "symbiote/silverstripe-versionedfiles": "<=2.0.3",
1628 "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", 1663 "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8",
1629 "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", 1664 "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7",
1665 "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4",
1630 "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", 1666 "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
1631 "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", 1667 "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7",
1632 "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", 1668 "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7",
1633 "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", 1669 "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8",
1634 "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", 1670 "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13",
1635 "symfony/mime": ">=4.3,<4.3.8", 1671 "symfony/mime": ">=4.3,<4.3.8",
@@ -1638,14 +1674,14 @@
1638 "symfony/polyfill-php55": ">=1,<1.10", 1674 "symfony/polyfill-php55": ">=1,<1.10",
1639 "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", 1675 "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7",
1640 "symfony/routing": ">=2,<2.0.19", 1676 "symfony/routing": ">=2,<2.0.19",
1641 "symfony/security": ">=2,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", 1677 "symfony/security": ">=2,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=4.4,<4.4.7|>=5,<5.0.7",
1642 "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", 1678 "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
1643 "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", 1679 "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7",
1644 "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", 1680 "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
1645 "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", 1681 "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
1646 "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8", 1682 "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7",
1647 "symfony/serializer": ">=2,<2.0.11", 1683 "symfony/serializer": ">=2,<2.0.11",
1648 "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", 1684 "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7",
1649 "symfony/translation": ">=2,<2.0.17", 1685 "symfony/translation": ">=2,<2.0.17",
1650 "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", 1686 "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3",
1651 "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", 1687 "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8",
@@ -1664,8 +1700,11 @@
1664 "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", 1700 "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
1665 "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", 1701 "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1",
1666 "ua-parser/uap-php": "<3.8", 1702 "ua-parser/uap-php": "<3.8",
1703 "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2",
1704 "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4",
1667 "wallabag/tcpdf": "<6.2.22", 1705 "wallabag/tcpdf": "<6.2.22",
1668 "willdurand/js-translation-bundle": "<2.1.1", 1706 "willdurand/js-translation-bundle": "<2.1.1",
1707 "yii2mod/yii2-cms": "<1.9.2",
1669 "yiisoft/yii": ">=1.1.14,<1.1.15", 1708 "yiisoft/yii": ">=1.1.14,<1.1.15",
1670 "yiisoft/yii2": "<2.0.15", 1709 "yiisoft/yii2": "<2.0.15",
1671 "yiisoft/yii2-bootstrap": "<2.0.4", 1710 "yiisoft/yii2-bootstrap": "<2.0.4",
@@ -1674,6 +1713,7 @@
1674 "yiisoft/yii2-gii": "<2.0.4", 1713 "yiisoft/yii2-gii": "<2.0.4",
1675 "yiisoft/yii2-jui": "<2.0.4", 1714 "yiisoft/yii2-jui": "<2.0.4",
1676 "yiisoft/yii2-redis": "<2.0.8", 1715 "yiisoft/yii2-redis": "<2.0.8",
1716 "yourls/yourls": "<1.7.4",
1677 "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", 1717 "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3",
1678 "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", 1718 "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2",
1679 "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", 1719 "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2",
@@ -1710,10 +1750,15 @@
1710 "name": "Marco Pivetta", 1750 "name": "Marco Pivetta",
1711 "email": "ocramius@gmail.com", 1751 "email": "ocramius@gmail.com",
1712 "role": "maintainer" 1752 "role": "maintainer"
1753 },
1754 {
1755 "name": "Ilya Tribusean",
1756 "email": "slash3b@gmail.com",
1757 "role": "maintainer"
1713 } 1758 }
1714 ], 1759 ],
1715 "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", 1760 "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
1716 "time": "2020-01-06T19:16:46+00:00" 1761 "time": "2020-05-09T00:00:21+00:00"
1717 }, 1762 },
1718 { 1763 {
1719 "name": "sebastian/code-unit-reverse-lookup", 1764 "name": "sebastian/code-unit-reverse-lookup",
@@ -2326,16 +2371,16 @@
2326 }, 2371 },
2327 { 2372 {
2328 "name": "squizlabs/php_codesniffer", 2373 "name": "squizlabs/php_codesniffer",
2329 "version": "3.5.3", 2374 "version": "3.5.5",
2330 "source": { 2375 "source": {
2331 "type": "git", 2376 "type": "git",
2332 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 2377 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
2333 "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb" 2378 "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6"
2334 }, 2379 },
2335 "dist": { 2380 "dist": {
2336 "type": "zip", 2381 "type": "zip",
2337 "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", 2382 "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
2338 "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", 2383 "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
2339 "shasum": "" 2384 "shasum": ""
2340 }, 2385 },
2341 "require": { 2386 "require": {
@@ -2373,20 +2418,20 @@
2373 "phpcs", 2418 "phpcs",
2374 "standards" 2419 "standards"
2375 ], 2420 ],
2376 "time": "2019-12-04T04:46:47+00:00" 2421 "time": "2020-04-17T01:09:41+00:00"
2377 }, 2422 },
2378 { 2423 {
2379 "name": "symfony/console", 2424 "name": "symfony/console",
2380 "version": "v4.4.2", 2425 "version": "v4.4.8",
2381 "source": { 2426 "source": {
2382 "type": "git", 2427 "type": "git",
2383 "url": "https://github.com/symfony/console.git", 2428 "url": "https://github.com/symfony/console.git",
2384 "reference": "82437719dab1e6bdd28726af14cb345c2ec816d0" 2429 "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7"
2385 }, 2430 },
2386 "dist": { 2431 "dist": {
2387 "type": "zip", 2432 "type": "zip",
2388 "url": "https://api.github.com/repos/symfony/console/zipball/82437719dab1e6bdd28726af14cb345c2ec816d0", 2433 "url": "https://api.github.com/repos/symfony/console/zipball/10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
2389 "reference": "82437719dab1e6bdd28726af14cb345c2ec816d0", 2434 "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
2390 "shasum": "" 2435 "shasum": ""
2391 }, 2436 },
2392 "require": { 2437 "require": {
@@ -2449,20 +2494,20 @@
2449 ], 2494 ],
2450 "description": "Symfony Console Component", 2495 "description": "Symfony Console Component",
2451 "homepage": "https://symfony.com", 2496 "homepage": "https://symfony.com",
2452 "time": "2019-12-17T10:32:23+00:00" 2497 "time": "2020-03-30T11:41:10+00:00"
2453 }, 2498 },
2454 { 2499 {
2455 "name": "symfony/finder", 2500 "name": "symfony/finder",
2456 "version": "v4.4.2", 2501 "version": "v4.4.8",
2457 "source": { 2502 "source": {
2458 "type": "git", 2503 "type": "git",
2459 "url": "https://github.com/symfony/finder.git", 2504 "url": "https://github.com/symfony/finder.git",
2460 "reference": "ce8743441da64c41e2a667b8eb66070444ed911e" 2505 "reference": "5729f943f9854c5781984ed4907bbb817735776b"
2461 }, 2506 },
2462 "dist": { 2507 "dist": {
2463 "type": "zip", 2508 "type": "zip",
2464 "url": "https://api.github.com/repos/symfony/finder/zipball/ce8743441da64c41e2a667b8eb66070444ed911e", 2509 "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b",
2465 "reference": "ce8743441da64c41e2a667b8eb66070444ed911e", 2510 "reference": "5729f943f9854c5781984ed4907bbb817735776b",
2466 "shasum": "" 2511 "shasum": ""
2467 }, 2512 },
2468 "require": { 2513 "require": {
@@ -2498,20 +2543,20 @@
2498 ], 2543 ],
2499 "description": "Symfony Finder Component", 2544 "description": "Symfony Finder Component",
2500 "homepage": "https://symfony.com", 2545 "homepage": "https://symfony.com",
2501 "time": "2019-11-17T21:56:56+00:00" 2546 "time": "2020-03-27T16:54:36+00:00"
2502 }, 2547 },
2503 { 2548 {
2504 "name": "symfony/polyfill-ctype", 2549 "name": "symfony/polyfill-ctype",
2505 "version": "v1.13.1", 2550 "version": "v1.16.0",
2506 "source": { 2551 "source": {
2507 "type": "git", 2552 "type": "git",
2508 "url": "https://github.com/symfony/polyfill-ctype.git", 2553 "url": "https://github.com/symfony/polyfill-ctype.git",
2509 "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" 2554 "reference": "1aab00e39cebaef4d8652497f46c15c1b7e45294"
2510 }, 2555 },
2511 "dist": { 2556 "dist": {
2512 "type": "zip", 2557 "type": "zip",
2513 "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", 2558 "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1aab00e39cebaef4d8652497f46c15c1b7e45294",
2514 "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", 2559 "reference": "1aab00e39cebaef4d8652497f46c15c1b7e45294",
2515 "shasum": "" 2560 "shasum": ""
2516 }, 2561 },
2517 "require": { 2562 "require": {
@@ -2523,7 +2568,7 @@
2523 "type": "library", 2568 "type": "library",
2524 "extra": { 2569 "extra": {
2525 "branch-alias": { 2570 "branch-alias": {
2526 "dev-master": "1.13-dev" 2571 "dev-master": "1.16-dev"
2527 } 2572 }
2528 }, 2573 },
2529 "autoload": { 2574 "autoload": {
@@ -2556,20 +2601,20 @@
2556 "polyfill", 2601 "polyfill",
2557 "portable" 2602 "portable"
2558 ], 2603 ],
2559 "time": "2019-11-27T13:56:44+00:00" 2604 "time": "2020-05-08T16:50:20+00:00"
2560 }, 2605 },
2561 { 2606 {
2562 "name": "symfony/polyfill-mbstring", 2607 "name": "symfony/polyfill-mbstring",
2563 "version": "v1.13.1", 2608 "version": "v1.16.0",
2564 "source": { 2609 "source": {
2565 "type": "git", 2610 "type": "git",
2566 "url": "https://github.com/symfony/polyfill-mbstring.git", 2611 "url": "https://github.com/symfony/polyfill-mbstring.git",
2567 "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f" 2612 "reference": "a54881ec0ab3b2005c406aed0023c062879031e7"
2568 }, 2613 },
2569 "dist": { 2614 "dist": {
2570 "type": "zip", 2615 "type": "zip",
2571 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f", 2616 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a54881ec0ab3b2005c406aed0023c062879031e7",
2572 "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f", 2617 "reference": "a54881ec0ab3b2005c406aed0023c062879031e7",
2573 "shasum": "" 2618 "shasum": ""
2574 }, 2619 },
2575 "require": { 2620 "require": {
@@ -2581,7 +2626,7 @@
2581 "type": "library", 2626 "type": "library",
2582 "extra": { 2627 "extra": {
2583 "branch-alias": { 2628 "branch-alias": {
2584 "dev-master": "1.13-dev" 2629 "dev-master": "1.16-dev"
2585 } 2630 }
2586 }, 2631 },
2587 "autoload": { 2632 "autoload": {
@@ -2615,20 +2660,20 @@
2615 "portable", 2660 "portable",
2616 "shim" 2661 "shim"
2617 ], 2662 ],
2618 "time": "2019-11-27T14:18:11+00:00" 2663 "time": "2020-05-08T16:50:20+00:00"
2619 }, 2664 },
2620 { 2665 {
2621 "name": "symfony/polyfill-php73", 2666 "name": "symfony/polyfill-php73",
2622 "version": "v1.13.1", 2667 "version": "v1.16.0",
2623 "source": { 2668 "source": {
2624 "type": "git", 2669 "type": "git",
2625 "url": "https://github.com/symfony/polyfill-php73.git", 2670 "url": "https://github.com/symfony/polyfill-php73.git",
2626 "reference": "4b0e2222c55a25b4541305a053013d5647d3a25f" 2671 "reference": "7e95fe59d12169fcf4041487e4bf34fca37ee0ed"
2627 }, 2672 },
2628 "dist": { 2673 "dist": {
2629 "type": "zip", 2674 "type": "zip",
2630 "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/4b0e2222c55a25b4541305a053013d5647d3a25f", 2675 "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/7e95fe59d12169fcf4041487e4bf34fca37ee0ed",
2631 "reference": "4b0e2222c55a25b4541305a053013d5647d3a25f", 2676 "reference": "7e95fe59d12169fcf4041487e4bf34fca37ee0ed",
2632 "shasum": "" 2677 "shasum": ""
2633 }, 2678 },
2634 "require": { 2679 "require": {
@@ -2637,7 +2682,7 @@
2637 "type": "library", 2682 "type": "library",
2638 "extra": { 2683 "extra": {
2639 "branch-alias": { 2684 "branch-alias": {
2640 "dev-master": "1.13-dev" 2685 "dev-master": "1.16-dev"
2641 } 2686 }
2642 }, 2687 },
2643 "autoload": { 2688 "autoload": {
@@ -2673,7 +2718,7 @@
2673 "portable", 2718 "portable",
2674 "shim" 2719 "shim"
2675 ], 2720 ],
2676 "time": "2019-11-27T16:25:15+00:00" 2721 "time": "2020-05-02T14:56:09+00:00"
2677 }, 2722 },
2678 { 2723 {
2679 "name": "symfony/service-contracts", 2724 "name": "symfony/service-contracts",
@@ -2815,16 +2860,16 @@
2815 }, 2860 },
2816 { 2861 {
2817 "name": "webmozart/assert", 2862 "name": "webmozart/assert",
2818 "version": "1.6.0", 2863 "version": "1.8.0",
2819 "source": { 2864 "source": {
2820 "type": "git", 2865 "type": "git",
2821 "url": "https://github.com/webmozart/assert.git", 2866 "url": "https://github.com/webmozart/assert.git",
2822 "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" 2867 "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6"
2823 }, 2868 },
2824 "dist": { 2869 "dist": {
2825 "type": "zip", 2870 "type": "zip",
2826 "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", 2871 "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
2827 "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", 2872 "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
2828 "shasum": "" 2873 "shasum": ""
2829 }, 2874 },
2830 "require": { 2875 "require": {
@@ -2832,7 +2877,7 @@
2832 "symfony/polyfill-ctype": "^1.8" 2877 "symfony/polyfill-ctype": "^1.8"
2833 }, 2878 },
2834 "conflict": { 2879 "conflict": {
2835 "vimeo/psalm": "<3.6.0" 2880 "vimeo/psalm": "<3.9.1"
2836 }, 2881 },
2837 "require-dev": { 2882 "require-dev": {
2838 "phpunit/phpunit": "^4.8.36 || ^7.5.13" 2883 "phpunit/phpunit": "^4.8.36 || ^7.5.13"
@@ -2859,7 +2904,7 @@
2859 "check", 2904 "check",
2860 "validate" 2905 "validate"
2861 ], 2906 ],
2862 "time": "2019-11-24T13:36:37+00:00" 2907 "time": "2020-04-18T12:12:48+00:00"
2863 } 2908 }
2864 ], 2909 ],
2865 "aliases": [], 2910 "aliases": [],
diff --git a/index.php b/index.php
index 57e13d4e..a42c844a 100644
--- a/index.php
+++ b/index.php
@@ -1916,6 +1916,7 @@ $app->group('', function () {
1916 $this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login'); 1916 $this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login');
1917 $this->get('/logout', '\Shaarli\Front\Controller\LogoutController:index')->setName('logout'); 1917 $this->get('/logout', '\Shaarli\Front\Controller\LogoutController:index')->setName('logout');
1918 $this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall'); 1918 $this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall');
1919 $this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\TagController:addTag')->setName('add-tag');
1919})->add('\Shaarli\Front\ShaarliMiddleware'); 1920})->add('\Shaarli\Front\ShaarliMiddleware');
1920 1921
1921$response = $app->run(true); 1922$response = $app->run(true);
diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php
index 591976f2..7d4a7b89 100644
--- a/tests/bookmark/LinkUtilsTest.php
+++ b/tests/bookmark/LinkUtilsTest.php
@@ -3,8 +3,6 @@
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use PHPUnit\Framework\TestCase; 5use PHPUnit\Framework\TestCase;
6use ReferenceLinkDB;
7use Shaarli\Config\ConfigManager;
8 6
9require_once 'tests/utils/CurlUtils.php'; 7require_once 'tests/utils/CurlUtils.php';
10 8
@@ -491,7 +489,7 @@ class LinkUtilsTest extends TestCase
491 */ 489 */
492 private function getHashtagLink($hashtag, $index = '') 490 private function getHashtagLink($hashtag, $index = '')
493 { 491 {
494 $hashtagLink = '<a href="' . $index . '?addtag=$1" title="Hashtag $1">#$1</a>'; 492 $hashtagLink = '<a href="' . $index . './add-tag/$1" title="Hashtag $1">#$1</a>';
495 return str_replace('$1', $hashtag, $hashtagLink); 493 return str_replace('$1', $hashtag, $hashtagLink);
496 } 494 }
497} 495}
diff --git a/tests/feed/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php
index 54671891..535207aa 100644
--- a/tests/feed/FeedBuilderTest.php
+++ b/tests/feed/FeedBuilderTest.php
@@ -306,6 +306,6 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
306 $link = $data['links'][array_keys($data['links'])[2]]; 306 $link = $data['links'][array_keys($data['links'])[2]];
307 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']); 307 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']);
308 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']); 308 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']);
309 $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']); 309 $this->assertContains('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
310 } 310 }
311} 311}
diff --git a/tests/formatter/BookmarkDefaultFormatterTest.php b/tests/formatter/BookmarkDefaultFormatterTest.php
index 382a560e..cf48b00b 100644
--- a/tests/formatter/BookmarkDefaultFormatterTest.php
+++ b/tests/formatter/BookmarkDefaultFormatterTest.php
@@ -123,7 +123,7 @@ class BookmarkDefaultFormatterTest extends TestCase
123 $description[0] = 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'; 123 $description[0] = 'This a &lt;strong&gt;description&lt;/strong&gt;<br />';
124 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash'; 124 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
125 $description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />'; 125 $description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />';
126 $description[2] = 'Also, there is an <a href="?addtag=hashtag" '. 126 $description[2] = 'Also, there is an <a href="./add-tag/hashtag" '.
127 'title="Hashtag hashtag">#hashtag</a> added<br />'; 127 'title="Hashtag hashtag">#hashtag</a> added<br />';
128 $description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; '. 128 $description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; '.
129 'SPACES &nbsp; &nbsp;! &nbsp; <br />'; 129 'SPACES &nbsp; &nbsp;! &nbsp; <br />';
@@ -148,7 +148,7 @@ class BookmarkDefaultFormatterTest extends TestCase
148 $this->assertEquals($root . $short, $link['url']); 148 $this->assertEquals($root . $short, $link['url']);
149 $this->assertEquals($root . $short, $link['real_url']); 149 $this->assertEquals($root . $short, $link['real_url']);
150 $this->assertEquals( 150 $this->assertEquals(
151 'Text <a href="'. $root .'?addtag=hashtag" title="Hashtag hashtag">'. 151 'Text <a href="'. $root .'./add-tag/hashtag" title="Hashtag hashtag">'.
152 '#hashtag</a> more text', 152 '#hashtag</a> more text',
153 $link['description'] 153 $link['description']
154 ); 154 );
diff --git a/tests/formatter/BookmarkMarkdownFormatterTest.php b/tests/formatter/BookmarkMarkdownFormatterTest.php
index f1f12c04..3e72d1ee 100644
--- a/tests/formatter/BookmarkMarkdownFormatterTest.php
+++ b/tests/formatter/BookmarkMarkdownFormatterTest.php
@@ -125,7 +125,7 @@ class BookmarkMarkdownFormatterTest extends TestCase
125 $description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'. PHP_EOL; 125 $description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'. PHP_EOL;
126 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash'; 126 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
127 $description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL; 127 $description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL;
128 $description .= 'Also, there is an <a href="?addtag=hashtag">#hashtag</a> added<br />'. PHP_EOL; 128 $description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />'. PHP_EOL;
129 $description .= 'A N D KEEP SPACES ! '; 129 $description .= 'A N D KEEP SPACES ! ';
130 $description .= '</p></div>'; 130 $description .= '</p></div>';
131 131
@@ -146,7 +146,7 @@ class BookmarkMarkdownFormatterTest extends TestCase
146 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/'); 146 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
147 147
148 $description = '<div class="markdown"><p>'; 148 $description = '<div class="markdown"><p>';
149 $description .= 'Text <a href="'. $root .'?addtag=hashtag">#hashtag</a> more text'; 149 $description .= 'Text <a href="'. $root .'./add-tag/hashtag">#hashtag</a> more text';
150 $description .= '</p></div>'; 150 $description .= '</p></div>';
151 151
152 $link = $this->formatter->format($bookmark); 152 $link = $this->formatter->format($bookmark);
diff --git a/tests/front/controller/TagControllerTest.php b/tests/front/controller/TagControllerTest.php
new file mode 100644
index 00000000..bbac5652
--- /dev/null
+++ b/tests/front/controller/TagControllerTest.php
@@ -0,0 +1,191 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Container\ShaarliContainer;
11use Shaarli\Plugin\PluginManager;
12use Shaarli\Render\PageBuilder;
13use Shaarli\Security\LoginManager;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17class TagControllerTest extends TestCase
18{
19 /** @var ShaarliContainer */
20 protected $container;
21
22 /** @var TagController */
23 protected $controller;
24
25 public function setUp(): void
26 {
27 $this->container = $this->createMock(ShaarliContainer::class);
28 $this->controller = new TagController($this->container);
29 }
30
31 public function testAddTagWithReferer(): void
32 {
33 $this->createValidContainerMockSet();
34 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
35
36 $request = $this->createMock(Request::class);
37 $response = new Response();
38
39 $tags = ['newTag' => 'abc'];
40
41 $result = $this->controller->addTag($request, $response, $tags);
42
43 static::assertInstanceOf(Response::class, $result);
44 static::assertSame(302, $result->getStatusCode());
45 static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
46 }
47
48 public function testAddTagWithRefererAndExistingSearch(): void
49 {
50 $this->createValidContainerMockSet();
51 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
52
53 $request = $this->createMock(Request::class);
54 $response = new Response();
55
56 $tags = ['newTag' => 'abc'];
57
58 $result = $this->controller->addTag($request, $response, $tags);
59
60 static::assertInstanceOf(Response::class, $result);
61 static::assertSame(302, $result->getStatusCode());
62 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
63 }
64
65 public function testAddTagWithoutRefererAndExistingSearch(): void
66 {
67 $this->createValidContainerMockSet();
68
69 $request = $this->createMock(Request::class);
70 $response = new Response();
71
72 $tags = ['newTag' => 'abc'];
73
74 $result = $this->controller->addTag($request, $response, $tags);
75
76 static::assertInstanceOf(Response::class, $result);
77 static::assertSame(302, $result->getStatusCode());
78 static::assertSame(['./?searchtags=abc'], $result->getHeader('location'));
79 }
80
81 public function testAddTagRemoveLegacyQueryParam(): void
82 {
83 $this->createValidContainerMockSet();
84 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc'];
85
86 $request = $this->createMock(Request::class);
87 $response = new Response();
88
89 $tags = ['newTag' => 'abc'];
90
91 $result = $this->controller->addTag($request, $response, $tags);
92
93 static::assertInstanceOf(Response::class, $result);
94 static::assertSame(302, $result->getStatusCode());
95 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
96 }
97
98 public function testAddTagResetPagination(): void
99 {
100 $this->createValidContainerMockSet();
101 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12'];
102
103 $request = $this->createMock(Request::class);
104 $response = new Response();
105
106 $tags = ['newTag' => 'abc'];
107
108 $result = $this->controller->addTag($request, $response, $tags);
109
110 static::assertInstanceOf(Response::class, $result);
111 static::assertSame(302, $result->getStatusCode());
112 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
113 }
114
115 public function testAddTagWithRefererAndEmptySearch(): void
116 {
117 $this->createValidContainerMockSet();
118 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags='];
119
120 $request = $this->createMock(Request::class);
121 $response = new Response();
122
123 $tags = ['newTag' => 'abc'];
124
125 $result = $this->controller->addTag($request, $response, $tags);
126
127 static::assertInstanceOf(Response::class, $result);
128 static::assertSame(302, $result->getStatusCode());
129 static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
130 }
131
132 public function testAddTagWithoutNewTagWithReferer(): void
133 {
134 $this->createValidContainerMockSet();
135 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
136
137 $request = $this->createMock(Request::class);
138 $response = new Response();
139
140 $result = $this->controller->addTag($request, $response, []);
141
142 static::assertInstanceOf(Response::class, $result);
143 static::assertSame(302, $result->getStatusCode());
144 static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
145 }
146
147 public function testAddTagWithoutNewTagWithoutReferer(): void
148 {
149 $this->createValidContainerMockSet();
150
151 $request = $this->createMock(Request::class);
152 $response = new Response();
153
154 $result = $this->controller->addTag($request, $response, []);
155
156 static::assertInstanceOf(Response::class, $result);
157 static::assertSame(302, $result->getStatusCode());
158 static::assertSame(['./'], $result->getHeader('location'));
159 }
160
161 protected function createValidContainerMockSet(): void
162 {
163 // User logged out
164 $loginManager = $this->createMock(LoginManager::class);
165 $loginManager->method('isLoggedIn')->willReturn(false);
166 $loginManager->method('canLogin')->willReturn(true);
167 $this->container->loginManager = $loginManager;
168
169 // Config
170 $conf = $this->createMock(ConfigManager::class);
171 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
172 return $default;
173 });
174 $this->container->conf = $conf;
175
176 // PageBuilder
177 $pageBuilder = $this->createMock(PageBuilder::class);
178 $pageBuilder
179 ->method('render')
180 ->willReturnCallback(function (string $template): string {
181 return $template;
182 })
183 ;
184 $this->container->pageBuilder = $pageBuilder;
185
186 $pluginManager = $this->createMock(PluginManager::class);
187 $this->container->pluginManager = $pluginManager;
188 $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
189 $this->container->bookmarkService = $bookmarkService;
190 }
191}
diff --git a/tests/plugins/resources/hashtags.md b/tests/plugins/resources/hashtags.md
deleted file mode 100644
index 46326de3..00000000
--- a/tests/plugins/resources/hashtags.md
+++ /dev/null
@@ -1,10 +0,0 @@
1[#lol](?addtag=lol)
2
3 #test
4
5`#test2`
6
7```
8bla #bli blo
9#bla
10```
diff --git a/tests/plugins/resources/hashtags.raw b/tests/plugins/resources/hashtags.raw
deleted file mode 100644
index 9d2dc98a..00000000
--- a/tests/plugins/resources/hashtags.raw
+++ /dev/null
@@ -1,10 +0,0 @@
1#lol
2
3 #test
4
5`#test2`
6
7```
8bla #bli blo
9#bla
10```
diff --git a/tests/plugins/resources/markdown.html b/tests/plugins/resources/markdown.html
deleted file mode 100644
index c3460bf7..00000000
--- a/tests/plugins/resources/markdown.html
+++ /dev/null
@@ -1,33 +0,0 @@
1<div class="markdown"><ul>
2<li>test:
3<ul>
4<li><a href="http://link.tld">zero</a></li>
5<li><a href="http://link.tld">two</a></li>
6<li><a href="http://link.tld">three</a></li>
7</ul></li>
8</ul>
9<ol>
10<li><a href="http://link.tld">zero</a>
11<ol start="2">
12<li><a href="http://link.tld">two</a></li>
13<li><a href="http://link.tld">three</a></li>
14<li><a href="http://link.tld">four</a></li>
15<li>foo <a href="?addtag=foobar">#foobar</a></li>
16</ol></li>
17</ol>
18<p><a href="?addtag=foobar">#foobar</a> foo <code>lol #foo</code> <a href="?addtag=bar">#bar</a></p>
19<p>fsdfs <a href="http://link.tld">http://link.tld</a> <a href="?addtag=foobar">#foobar</a> <code>http://link.tld</code></p>
20<pre><code>http://link.tld #foobar
21next #foo</code></pre>
22<p>Block:</p>
23<pre><code>lorem ipsum #foobar http://link.tld
24#foobar http://link.tld</code></pre>
25<p><a href="?123456">link</a><br />
26<img src="/img/train.png" alt="link" /><br />
27<a href="http://test.tld/path/?query=value#hash">link</a><br />
28<a href="http://test.tld/path/?query=value#hash">link</a><br />
29<a href="https://test.tld/path/?query=value#hash">link</a><br />
30<a href="ftp://test.tld/path/?query=value#hash">link</a><br />
31<a href="magnet:test.tld/path/?query=value#hash">link</a><br />
32<a href="http://alert(&#039;xss&#039;)">link</a><br />
33<a href="http://test.tld/path/?query=value#hash">link</a></p></div>
diff --git a/tests/plugins/resources/markdown.md b/tests/plugins/resources/markdown.md
deleted file mode 100644
index 9350a8c7..00000000
--- a/tests/plugins/resources/markdown.md
+++ /dev/null
@@ -1,34 +0,0 @@
1* test:
2 * [zero](http://link.tld)
3 + [two](http://link.tld)
4 - [three](http://link.tld)
5
61. [zero](http://link.tld)
7 2. [two](http://link.tld)
8 3. [three](http://link.tld)
9 4. [four](http://link.tld)
10 5. foo #foobar
11
12#foobar foo `lol #foo` #bar
13
14fsdfs http://link.tld #foobar `http://link.tld`
15
16 http://link.tld #foobar
17 next #foo
18
19Block:
20
21```
22lorem ipsum #foobar http://link.tld
23#foobar http://link.tld
24```
25
26[link](?123456)
27![link](/img/train.png)
28[link](test.tld/path/?query=value#hash)
29[link](http://test.tld/path/?query=value#hash)
30[link](https://test.tld/path/?query=value#hash)
31[link](ftp://test.tld/path/?query=value#hash)
32[link](magnet:test.tld/path/?query=value#hash)
33[link](javascript:alert('xss'))
34[link](other://test.tld/path/?query=value#hash)
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index 505b1614..d8a15823 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -181,7 +181,7 @@
181 {$tag_counter=count($value.taglist)} 181 {$tag_counter=count($value.taglist)}
182 {loop="value.taglist"} 182 {loop="value.taglist"}
183 <span class="label label-tag" title="{$strAddTag}"> 183 <span class="label label-tag" title="{$strAddTag}">
184 <a href="?addtag={$value|urlencode}">{$value}</a> 184 <a href="./add-tag/{$value|urlencode}">{$value}</a>
185 </span> 185 </span>
186 {if="$tag_counter - 1 != $counter"}&middot;{/if} 186 {if="$tag_counter - 1 != $counter"}&middot;{/if}
187 {/loop} 187 {/loop}
diff --git a/tpl/default/tag.cloud.html b/tpl/default/tag.cloud.html
index 7839fcca..80dcebf5 100644
--- a/tpl/default/tag.cloud.html
+++ b/tpl/default/tag.cloud.html
@@ -49,7 +49,7 @@
49 <div id="cloudtag" class="cloudtag-container"> 49 <div id="cloudtag" class="cloudtag-container">
50 {loop="tags"} 50 {loop="tags"}
51 <a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" style="font-size:{$value.size}em;">{$key}</a 51 <a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" style="font-size:{$value.size}em;">{$key}</a
52 ><a href="?addtag={$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value.count}</a> 52 ><a href="./add-tag/{$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value.count}</a>
53 {loop="$value.tag_plugin"} 53 {loop="$value.tag_plugin"}
54 {$value} 54 {$value}
55 {/loop} 55 {/loop}
diff --git a/tpl/default/tag.list.html b/tpl/default/tag.list.html
index dbc5ee3a..01b7a642 100644
--- a/tpl/default/tag.list.html
+++ b/tpl/default/tag.list.html
@@ -56,7 +56,7 @@
56 </a> 56 </a>
57 {/if} 57 {/if}
58 58
59 <a href="?addtag={$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a> 59 <a href="./add-tag/{$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a>
60 <a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" class="tag-link">{$key}</a> 60 <a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" class="tag-link">{$key}</a>
61 61
62 {loop="$value.tag_plugin"} 62 {loop="$value.tag_plugin"}
diff --git a/tpl/vintage/linklist.html b/tpl/vintage/linklist.html
index dcb14e90..8f1ded95 100644
--- a/tpl/vintage/linklist.html
+++ b/tpl/vintage/linklist.html
@@ -133,7 +133,7 @@
133 <a href="{$value.real_url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br> 133 <a href="{$value.real_url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
134 {if="$value.tags"} 134 {if="$value.tags"}
135 <div class="linktaglist"> 135 <div class="linktaglist">
136 {loop="$value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop} 136 {loop="$value.taglist"}<span class="linktag" title="Add tag"><a href="./add-tag/{$value|urlencode}">{$value}</a></span> {/loop}
137 </div> 137 </div>
138 {/if} 138 {/if}
139 139
diff --git a/tpl/vintage/tag.cloud.html b/tpl/vintage/tag.cloud.html
index d93bf4f9..23aa381e 100644
--- a/tpl/vintage/tag.cloud.html
+++ b/tpl/vintage/tag.cloud.html
@@ -12,7 +12,7 @@
12 12
13 <div id="cloudtag"> 13 <div id="cloudtag">
14 {loop="$tags"} 14 {loop="$tags"}
15 <a href="?addtag={$key|urlencode}" class="count">{$value.count}</a><a 15 <a href="./add-tag/{$key|urlencode}" class="count">{$value.count}</a><a
16 href="?searchtags={$key|urlencode}" style="font-size:{$value.size}em;">{$key}</a> 16 href="?searchtags={$key|urlencode}" style="font-size:{$value.size}em;">{$key}</a>
17 {loop="$value.tag_plugin"} 17 {loop="$value.tag_plugin"}
18 {$value} 18 {$value}