Slim router: handle add tag route

This commit is contained in:
ArthurHoaro 2020-05-12 12:44:48 +02:00
parent 8e47af2b36
commit 03340c18ea
20 changed files with 407 additions and 184 deletions

View file

@ -220,7 +220,7 @@ function hashtag_autolink($description, $indexUrl = '')
* \p{Mn} - any non marking space (accents, umlauts, etc)
*/
$regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui';
$replacement = '$1<a href="'. $indexUrl .'?addtag=$2" title="Hashtag $2">#$2</a>';
$replacement = '$1<a href="'. $indexUrl .'./add-tag/$2" title="Hashtag $2">#$2</a>';
return preg_replace($regex, $replacement, $description);
}

View file

@ -18,6 +18,7 @@
/**
* Extension of Slim container to document the injected objects.
*
* @property mixed[] $environment $_SERVER automatically injected by Slim
* @property ConfigManager $conf
* @property SessionManager $sessionManager
* @property LoginManager $loginManager

View file

@ -114,7 +114,7 @@ function ($match) use ($allowedProtocols, $indexUrl) {
/**
* Replace hashtag in Markdown links format
* E.g. `#hashtag` becomes `[#hashtag](?addtag=hashtag)`
* E.g. `#hashtag` becomes `[#hashtag](./add-tag/hashtag)`
* It includes the index URL if specified.
*
* @param string $description
@ -133,7 +133,7 @@ protected function formatHashTags($description)
* \p{Mn} - any non marking space (accents, umlauts, etc)
*/
$regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui';
$replacement = '$1[#$2]('. $indexUrl .'?addtag=$2)';
$replacement = '$1[#$2]('. $indexUrl .'./add-tag/$2)';
$descriptionLines = explode(PHP_EOL, $description);
$descriptionOut = '';

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class TagController
*
* Slim controller handle tags.
*
* @package Front\Controller
*/
class TagController extends ShaarliController
{
/**
* Add another tag in the current search through an HTTP redirection.
*
* @param array $args Should contain `newTag` key as tag to add to current search
*/
public function addTag(Request $request, Response $response, array $args): Response
{
$newTag = $args['newTag'] ?? null;
$referer = $this->container->environment['HTTP_REFERER'] ?? null;
// In case browser does not send HTTP_REFERER, we search a single tag
if (null === $referer) {
if (null !== $newTag) {
return $response->withRedirect('./?searchtags='. urlencode($newTag));
}
return $response->withRedirect('./');
}
$currentUrl = parse_url($this->container->environment['HTTP_REFERER']);
parse_str($currentUrl['query'] ?? '', $params);
if (null === $newTag) {
return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params));
}
// Prevent redirection loop
if (isset($params['addtag'])) {
unset($params['addtag']);
}
// Check if this tag is already in the search query and ignore it if it is.
// Each tag is always separated by a space
$currentTags = isset($params['searchtags']) ? explode(' ', $params['searchtags']) : [];
$addtag = true;
foreach ($currentTags as $value) {
if ($value === $newTag) {
$addtag = false;
break;
}
}
// Append the tag if necessary
if (true === $addtag) {
$currentTags[] = trim($newTag);
}
$params['searchtags'] = trim(implode(' ', $currentTags));
// We also remove page (keeping the same page has no sense, since the results are different)
unset($params['page']);
return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params));
}
}

207
composer.lock generated
View file

@ -508,16 +508,16 @@
},
{
"name": "psr/log",
"version": "1.1.2",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
@ -551,7 +551,7 @@
"psr",
"psr-3"
],
"time": "2019-11-01T11:05:21+00:00"
"time": "2020-03-23T09:12:05+00:00"
},
{
"name": "pubsubhubbub/publisher",
@ -936,24 +936,21 @@
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.0.0",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
"reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
"reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
"reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~6"
},
"type": "library",
"extra": {
"branch-alias": {
@ -984,7 +981,7 @@
"reflection",
"static analysis"
],
"time": "2018-08-07T13:53:10+00:00"
"time": "2020-04-27T09:25:28+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
@ -1087,24 +1084,24 @@
},
{
"name": "phpspec/prophecy",
"version": "1.10.1",
"version": "v1.10.3",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc"
"reference": "451c3cd1418cf640de218914901e51b064abb093"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc",
"reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
"reference": "451c3cd1418cf640de218914901e51b064abb093",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
"sebastian/comparator": "^1.2.3|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
"sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5 || ^3.2",
@ -1146,7 +1143,7 @@
"spy",
"stub"
],
"time": "2019-12-22T21:05:45+00:00"
"time": "2020-03-05T15:02:03+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1501,12 +1498,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "67ac6ea8f4a078c3c9b7aec5d7ae70f098c37389"
"reference": "ec1a75b10126327b351fdea7c2b9bfb94e2f6f35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/67ac6ea8f4a078c3c9b7aec5d7ae70f098c37389",
"reference": "67ac6ea8f4a078c3c9b7aec5d7ae70f098c37389",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ec1a75b10126327b351fdea7c2b9bfb94e2f6f35",
"reference": "ec1a75b10126327b351fdea7c2b9bfb94e2f6f35",
"shasum": ""
},
"conflict": {
@ -1518,11 +1515,17 @@
"api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6",
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
"aws/aws-sdk-php": ">=3,<3.2.1",
"bagisto/bagisto": "<0.1.5",
"barrelstrength/sprout-base-email": "<3.9",
"bolt/bolt": "<3.6.10",
"brightlocal/phpwhois": "<=4.2.5",
"buddypress/buddypress": "<5.1.2",
"bugsnag/bugsnag-laravel": ">=2,<2.0.2",
"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",
"cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
"cartalyst/sentry": "<=2.1.6",
"centreon/centreon": "<18.10.8|>=19,<19.4.5",
"cesnet/simplesamlphp-module-proxystatistics": "<3.1",
"codeigniter/framework": "<=3.0.6",
"composer/composer": "<=1-alpha.11",
"contao-components/mediaelement": ">=2.14.2,<2.21.1",
@ -1540,22 +1543,32 @@
"doctrine/mongodb-odm": ">=1,<1.0.2",
"doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
"doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1",
"dolibarr/dolibarr": "<=10.0.6",
"dompdf/dompdf": ">=0.6,<0.6.2",
"drupal/core": ">=7,<7.69|>=8,<8.7.11|>=8.8,<8.8.1",
"drupal/drupal": ">=7,<7.69|>=8,<8.7.11|>=8.8,<8.8.1",
"drupal/core": ">=7,<7.69|>=8,<8.7.12|>=8.8,<8.8.4",
"drupal/drupal": ">=7,<7.69|>=8,<8.7.12|>=8.8,<8.8.4",
"endroid/qr-code-bundle": "<3.4.2",
"enshrined/svg-sanitize": "<0.13.1",
"erusev/parsedown": "<1.7.2",
"ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.4",
"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",
"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",
"ezsystems/demobundle": ">=5.4,<5.4.6.1",
"ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1",
"ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1",
"ezsystems/ezplatform": ">=1.7,<1.7.9.1|>=1.13,<1.13.5.1|>=2.5,<2.5.4",
"ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6",
"ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2",
"ezsystems/ezplatform-user": ">=1,<1.0.1",
"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",
"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",
"ezsystems/repository-forms": ">=2.3,<2.3.2.1",
"ezyang/htmlpurifier": "<4.1.1",
"firebase/php-jwt": "<2",
"fooman/tcpdf": "<6.2.22",
"fossar/tcpdf-parser": "<6.2.22",
"friendsofsymfony/oauth2-php": "<1.3",
"friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
"friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
"fuel/core": "<1.8.1",
"getgrav/grav": "<1.7-beta.8",
"gree/jose": "<=2.2",
"gregwar/rst": "<1.0.3",
"guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1",
@ -1563,6 +1576,7 @@
"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",
"illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29",
"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",
"illuminate/view": ">=7,<7.1.2",
"ivankristianto/phpwhois": "<=4.3",
"james-heinrich/getid3": "<1.9.9",
"joomla/session": "<1.3.1",
@ -1570,15 +1584,19 @@
"kazist/phpwhois": "<=4.2.6",
"kreait/firebase-php": ">=3.2,<3.8.1",
"la-haute-societe/tcpdf": "<6.2.22",
"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",
"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",
"laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
"league/commonmark": "<0.18.3",
"librenms/librenms": "<1.53",
"magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3",
"magento/magento1ce": "<1.9.4.3",
"magento/magento1ee": ">=1,<1.14.4.3",
"magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2",
"monolog/monolog": ">=1.8,<1.12",
"namshi/jose": "<2.2",
"nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1",
"onelogin/php-saml": "<2.10.4",
"oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5",
"openid/php-openid": "<2.3",
"oro/crm": ">=1.7,<1.7.4",
"oro/platform": ">=1.7,<1.7.4",
@ -1587,49 +1605,67 @@
"paragonie/random_compat": "<2",
"paypal/merchant-sdk-php": "<3.12",
"pear/archive_tar": "<1.4.4",
"phpfastcache/phpfastcache": ">=5,<5.0.13",
"phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6",
"phpoffice/phpexcel": "<=1.8.1",
"phpoffice/phpspreadsheet": "<=1.5",
"phpmyadmin/phpmyadmin": "<4.9.2",
"phpoffice/phpexcel": "<1.8.2",
"phpoffice/phpspreadsheet": "<1.8",
"phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3",
"phpwhois/phpwhois": "<=4.2.5",
"phpxmlrpc/extras": "<0.6.1",
"pimcore/pimcore": "<6.3",
"prestashop/autoupgrade": ">=4,<4.10.1",
"prestashop/gamification": "<2.3.2",
"prestashop/ps_facetedsearch": "<3.4.1",
"privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2",
"propel/propel": ">=2-alpha.1,<=2-alpha.7",
"propel/propel1": ">=1,<=1.7.1",
"pusher/pusher-php-server": "<2.2.1",
"robrichards/xmlseclibs": ">=1,<3.0.4",
"robrichards/xmlseclibs": "<3.0.4",
"sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
"scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11",
"sensiolabs/connect": "<4.2.3",
"serluck/phpwhois": "<=4.2.6",
"shopware/shopware": "<5.3.7",
"silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11",
"silverstripe/admin": ">=1.0.3,<1.0.4|>=1.1,<1.1.1",
"silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2",
"silverstripe/cms": "<4.3.6|>=4.4,<4.4.4",
"silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1",
"silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
"silverstripe/framework": ">=3,<3.6.7|>=3.7,<3.7.3|>=4,<4.4",
"silverstripe/framework": "<4.4.5|>=4.5,<4.5.2",
"silverstripe/graphql": ">=2,<2.0.5|>=3,<3.1.2",
"silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1",
"silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4",
"silverstripe/subsites": ">=2,<2.1.1",
"silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1",
"silverstripe/userforms": "<3",
"simple-updates/phpwhois": "<=1",
"simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
"simplesamlphp/simplesamlphp": "<1.17.8",
"simplesamlphp/simplesamlphp": "<1.18.6",
"simplesamlphp/simplesamlphp-module-infocard": "<1.0.1",
"simplito/elliptic-php": "<1.0.6",
"slim/slim": "<2.6",
"smarty/smarty": "<3.1.33",
"socalnick/scn-social-auth": "<1.15.2",
"spoonity/tcpdf": "<6.2.22",
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
"ssddanbrown/bookstack": "<0.29.2",
"stormpath/sdk": ">=0,<9.9.99",
"studio-42/elfinder": "<2.1.48",
"studio-42/elfinder": "<2.1.49",
"swiftmailer/swiftmailer": ">=4,<5.4.5",
"sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2",
"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",
"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",
"sylius/sylius": ">=1,<1.1.18|>=1.2,<1.2.17|>=1.3,<1.3.12|>=1.4,<1.4.4",
"sylius/resource-bundle": "<1.3.13|>=1.4,<1.4.6|>=1.5,<1.5.1|>=1.6,<1.6.3",
"sylius/sylius": "<1.3.16|>=1.4,<1.4.12|>=1.5,<1.5.9|>=1.6,<1.6.5",
"symbiote/silverstripe-multivaluefield": ">=3,<3.0.99",
"symbiote/silverstripe-versionedfiles": "<=2.0.3",
"symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8",
"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",
"symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4",
"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",
"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",
"symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8",
"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",
"symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8",
"symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13",
"symfony/mime": ">=4.3,<4.3.8",
@ -1638,14 +1674,14 @@
"symfony/polyfill-php55": ">=1,<1.10",
"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",
"symfony/routing": ">=2,<2.0.19",
"symfony/security": ">=2,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7",
"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",
"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",
"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",
"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",
"symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
"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",
"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",
"symfony/serializer": ">=2,<2.0.11",
"symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8",
"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",
"symfony/translation": ">=2,<2.0.17",
"symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3",
"symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8",
@ -1664,8 +1700,11 @@
"typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
"typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1",
"ua-parser/uap-php": "<3.8",
"usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2",
"verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4",
"wallabag/tcpdf": "<6.2.22",
"willdurand/js-translation-bundle": "<2.1.1",
"yii2mod/yii2-cms": "<1.9.2",
"yiisoft/yii": ">=1.1.14,<1.1.15",
"yiisoft/yii2": "<2.0.15",
"yiisoft/yii2-bootstrap": "<2.0.4",
@ -1674,6 +1713,7 @@
"yiisoft/yii2-gii": "<2.0.4",
"yiisoft/yii2-jui": "<2.0.4",
"yiisoft/yii2-redis": "<2.0.8",
"yourls/yourls": "<1.7.4",
"zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3",
"zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2",
"zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2",
@ -1710,10 +1750,15 @@
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"role": "maintainer"
},
{
"name": "Ilya Tribusean",
"email": "slash3b@gmail.com",
"role": "maintainer"
}
],
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
"time": "2020-01-06T19:16:46+00:00"
"time": "2020-05-09T00:00:21+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -2326,16 +2371,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.5.3",
"version": "3.5.5",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb"
"reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"shasum": ""
},
"require": {
@ -2373,20 +2418,20 @@
"phpcs",
"standards"
],
"time": "2019-12-04T04:46:47+00:00"
"time": "2020-04-17T01:09:41+00:00"
},
{
"name": "symfony/console",
"version": "v4.4.2",
"version": "v4.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "82437719dab1e6bdd28726af14cb345c2ec816d0"
"reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/82437719dab1e6bdd28726af14cb345c2ec816d0",
"reference": "82437719dab1e6bdd28726af14cb345c2ec816d0",
"url": "https://api.github.com/repos/symfony/console/zipball/10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
"reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
"shasum": ""
},
"require": {
@ -2449,20 +2494,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2019-12-17T10:32:23+00:00"
"time": "2020-03-30T11:41:10+00:00"
},
{
"name": "symfony/finder",
"version": "v4.4.2",
"version": "v4.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "ce8743441da64c41e2a667b8eb66070444ed911e"
"reference": "5729f943f9854c5781984ed4907bbb817735776b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/ce8743441da64c41e2a667b8eb66070444ed911e",
"reference": "ce8743441da64c41e2a667b8eb66070444ed911e",
"url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b",
"reference": "5729f943f9854c5781984ed4907bbb817735776b",
"shasum": ""
},
"require": {
@ -2498,20 +2543,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2019-11-17T21:56:56+00:00"
"time": "2020-03-27T16:54:36+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.13.1",
"version": "v1.16.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
"reference": "1aab00e39cebaef4d8652497f46c15c1b7e45294"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1aab00e39cebaef4d8652497f46c15c1b7e45294",
"reference": "1aab00e39cebaef4d8652497f46c15c1b7e45294",
"shasum": ""
},
"require": {
@ -2523,7 +2568,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
"dev-master": "1.16-dev"
}
},
"autoload": {
@ -2556,20 +2601,20 @@
"polyfill",
"portable"
],
"time": "2019-11-27T13:56:44+00:00"
"time": "2020-05-08T16:50:20+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.13.1",
"version": "v1.16.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f"
"reference": "a54881ec0ab3b2005c406aed0023c062879031e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a54881ec0ab3b2005c406aed0023c062879031e7",
"reference": "a54881ec0ab3b2005c406aed0023c062879031e7",
"shasum": ""
},
"require": {
@ -2581,7 +2626,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
"dev-master": "1.16-dev"
}
},
"autoload": {
@ -2615,20 +2660,20 @@
"portable",
"shim"
],
"time": "2019-11-27T14:18:11+00:00"
"time": "2020-05-08T16:50:20+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.13.1",
"version": "v1.16.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "4b0e2222c55a25b4541305a053013d5647d3a25f"
"reference": "7e95fe59d12169fcf4041487e4bf34fca37ee0ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/4b0e2222c55a25b4541305a053013d5647d3a25f",
"reference": "4b0e2222c55a25b4541305a053013d5647d3a25f",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/7e95fe59d12169fcf4041487e4bf34fca37ee0ed",
"reference": "7e95fe59d12169fcf4041487e4bf34fca37ee0ed",
"shasum": ""
},
"require": {
@ -2637,7 +2682,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
"dev-master": "1.16-dev"
}
},
"autoload": {
@ -2673,7 +2718,7 @@
"portable",
"shim"
],
"time": "2019-11-27T16:25:15+00:00"
"time": "2020-05-02T14:56:09+00:00"
},
{
"name": "symfony/service-contracts",
@ -2815,16 +2860,16 @@
},
{
"name": "webmozart/assert",
"version": "1.6.0",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925"
"reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925",
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925",
"url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
"reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
"shasum": ""
},
"require": {
@ -2832,7 +2877,7 @@
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"vimeo/psalm": "<3.6.0"
"vimeo/psalm": "<3.9.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
@ -2859,7 +2904,7 @@
"check",
"validate"
],
"time": "2019-11-24T13:36:37+00:00"
"time": "2020-04-18T12:12:48+00:00"
}
],
"aliases": [],

View file

@ -1916,6 +1916,7 @@ function install($conf, $sessionManager, $loginManager)
$this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login');
$this->get('/logout', '\Shaarli\Front\Controller\LogoutController:index')->setName('logout');
$this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall');
$this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\TagController:addTag')->setName('add-tag');
})->add('\Shaarli\Front\ShaarliMiddleware');
$response = $app->run(true);

View file

@ -3,8 +3,6 @@
namespace Shaarli\Bookmark;
use PHPUnit\Framework\TestCase;
use ReferenceLinkDB;
use Shaarli\Config\ConfigManager;
require_once 'tests/utils/CurlUtils.php';
@ -491,7 +489,7 @@ public function testIsNotNote()
*/
private function getHashtagLink($hashtag, $index = '')
{
$hashtagLink = '<a href="' . $index . '?addtag=$1" title="Hashtag $1">#$1</a>';
$hashtagLink = '<a href="' . $index . './add-tag/$1" title="Hashtag $1">#$1</a>';
return str_replace('$1', $hashtag, $hashtagLink);
}
}

View file

@ -306,6 +306,6 @@ public function testBuildDataServerSubdir()
$link = $data['links'][array_keys($data['links'])[2]];
$this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']);
$this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']);
$this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']);
$this->assertContains('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
}
}

View file

@ -123,7 +123,7 @@ public function testFormatDescription()
$description[0] = 'This a &lt;strong&gt;description&lt;/strong&gt;<br />';
$url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
$description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />';
$description[2] = 'Also, there is an <a href="?addtag=hashtag" '.
$description[2] = 'Also, there is an <a href="./add-tag/hashtag" '.
'title="Hashtag hashtag">#hashtag</a> added<br />';
$description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; '.
'SPACES &nbsp; &nbsp;! &nbsp; <br />';
@ -148,7 +148,7 @@ public function testFormatNoteWithIndexUrl()
$this->assertEquals($root . $short, $link['url']);
$this->assertEquals($root . $short, $link['real_url']);
$this->assertEquals(
'Text <a href="'. $root .'?addtag=hashtag" title="Hashtag hashtag">'.
'Text <a href="'. $root .'./add-tag/hashtag" title="Hashtag hashtag">'.
'#hashtag</a> more text',
$link['description']
);

View file

@ -125,7 +125,7 @@ public function testFormatDescription()
$description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'. PHP_EOL;
$url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
$description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL;
$description .= 'Also, there is an <a href="?addtag=hashtag">#hashtag</a> added<br />'. PHP_EOL;
$description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />'. PHP_EOL;
$description .= 'A N D KEEP SPACES ! ';
$description .= '</p></div>';
@ -146,7 +146,7 @@ public function testFormatNoteWithIndexUrl()
$this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
$description = '<div class="markdown"><p>';
$description .= 'Text <a href="'. $root .'?addtag=hashtag">#hashtag</a> more text';
$description .= 'Text <a href="'. $root .'./add-tag/hashtag">#hashtag</a> more text';
$description .= '</p></div>';
$link = $this->formatter->format($bookmark);

View file

@ -0,0 +1,191 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller;
use PHPUnit\Framework\TestCase;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
use Shaarli\Container\ShaarliContainer;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\PageBuilder;
use Shaarli\Security\LoginManager;
use Slim\Http\Request;
use Slim\Http\Response;
class TagControllerTest extends TestCase
{
/** @var ShaarliContainer */
protected $container;
/** @var TagController */
protected $controller;
public function setUp(): void
{
$this->container = $this->createMock(ShaarliContainer::class);
$this->controller = new TagController($this->container);
}
public function testAddTagWithReferer(): void
{
$this->createValidContainerMockSet();
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
$request = $this->createMock(Request::class);
$response = new Response();
$tags = ['newTag' => 'abc'];
$result = $this->controller->addTag($request, $response, $tags);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
}
public function testAddTagWithRefererAndExistingSearch(): void
{
$this->createValidContainerMockSet();
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
$request = $this->createMock(Request::class);
$response = new Response();
$tags = ['newTag' => 'abc'];
$result = $this->controller->addTag($request, $response, $tags);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
}
public function testAddTagWithoutRefererAndExistingSearch(): void
{
$this->createValidContainerMockSet();
$request = $this->createMock(Request::class);
$response = new Response();
$tags = ['newTag' => 'abc'];
$result = $this->controller->addTag($request, $response, $tags);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['./?searchtags=abc'], $result->getHeader('location'));
}
public function testAddTagRemoveLegacyQueryParam(): void
{
$this->createValidContainerMockSet();
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc'];
$request = $this->createMock(Request::class);
$response = new Response();
$tags = ['newTag' => 'abc'];
$result = $this->controller->addTag($request, $response, $tags);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
}
public function testAddTagResetPagination(): void
{
$this->createValidContainerMockSet();
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12'];
$request = $this->createMock(Request::class);
$response = new Response();
$tags = ['newTag' => 'abc'];
$result = $this->controller->addTag($request, $response, $tags);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
}
public function testAddTagWithRefererAndEmptySearch(): void
{
$this->createValidContainerMockSet();
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags='];
$request = $this->createMock(Request::class);
$response = new Response();
$tags = ['newTag' => 'abc'];
$result = $this->controller->addTag($request, $response, $tags);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
}
public function testAddTagWithoutNewTagWithReferer(): void
{
$this->createValidContainerMockSet();
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->addTag($request, $response, []);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
}
public function testAddTagWithoutNewTagWithoutReferer(): void
{
$this->createValidContainerMockSet();
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->addTag($request, $response, []);
static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode());
static::assertSame(['./'], $result->getHeader('location'));
}
protected function createValidContainerMockSet(): void
{
// User logged out
$loginManager = $this->createMock(LoginManager::class);
$loginManager->method('isLoggedIn')->willReturn(false);
$loginManager->method('canLogin')->willReturn(true);
$this->container->loginManager = $loginManager;
// Config
$conf = $this->createMock(ConfigManager::class);
$conf->method('get')->willReturnCallback(function (string $parameter, $default) {
return $default;
});
$this->container->conf = $conf;
// PageBuilder
$pageBuilder = $this->createMock(PageBuilder::class);
$pageBuilder
->method('render')
->willReturnCallback(function (string $template): string {
return $template;
})
;
$this->container->pageBuilder = $pageBuilder;
$pluginManager = $this->createMock(PluginManager::class);
$this->container->pluginManager = $pluginManager;
$bookmarkService = $this->createMock(BookmarkServiceInterface::class);
$this->container->bookmarkService = $bookmarkService;
}
}

View file

@ -1,10 +0,0 @@
[#lol](?addtag=lol)
#test
`#test2`
```
bla #bli blo
#bla
```

View file

@ -1,10 +0,0 @@
#lol
#test
`#test2`
```
bla #bli blo
#bla
```

View file

@ -1,33 +0,0 @@
<div class="markdown"><ul>
<li>test:
<ul>
<li><a href="http://link.tld">zero</a></li>
<li><a href="http://link.tld">two</a></li>
<li><a href="http://link.tld">three</a></li>
</ul></li>
</ul>
<ol>
<li><a href="http://link.tld">zero</a>
<ol start="2">
<li><a href="http://link.tld">two</a></li>
<li><a href="http://link.tld">three</a></li>
<li><a href="http://link.tld">four</a></li>
<li>foo <a href="?addtag=foobar">#foobar</a></li>
</ol></li>
</ol>
<p><a href="?addtag=foobar">#foobar</a> foo <code>lol #foo</code> <a href="?addtag=bar">#bar</a></p>
<p>fsdfs <a href="http://link.tld">http://link.tld</a> <a href="?addtag=foobar">#foobar</a> <code>http://link.tld</code></p>
<pre><code>http://link.tld #foobar
next #foo</code></pre>
<p>Block:</p>
<pre><code>lorem ipsum #foobar http://link.tld
#foobar http://link.tld</code></pre>
<p><a href="?123456">link</a><br />
<img src="/img/train.png" alt="link" /><br />
<a href="http://test.tld/path/?query=value#hash">link</a><br />
<a href="http://test.tld/path/?query=value#hash">link</a><br />
<a href="https://test.tld/path/?query=value#hash">link</a><br />
<a href="ftp://test.tld/path/?query=value#hash">link</a><br />
<a href="magnet:test.tld/path/?query=value#hash">link</a><br />
<a href="http://alert(&#039;xss&#039;)">link</a><br />
<a href="http://test.tld/path/?query=value#hash">link</a></p></div>

View file

@ -1,34 +0,0 @@
* test:
* [zero](http://link.tld)
+ [two](http://link.tld)
- [three](http://link.tld)
1. [zero](http://link.tld)
2. [two](http://link.tld)
3. [three](http://link.tld)
4. [four](http://link.tld)
5. foo #foobar
#foobar foo `lol #foo` #bar
fsdfs http://link.tld #foobar `http://link.tld`
http://link.tld #foobar
next #foo
Block:
```
lorem ipsum #foobar http://link.tld
#foobar http://link.tld
```
[link](?123456)
![link](/img/train.png)
[link](test.tld/path/?query=value#hash)
[link](http://test.tld/path/?query=value#hash)
[link](https://test.tld/path/?query=value#hash)
[link](ftp://test.tld/path/?query=value#hash)
[link](magnet:test.tld/path/?query=value#hash)
[link](javascript:alert('xss'))
[link](other://test.tld/path/?query=value#hash)

View file

@ -181,7 +181,7 @@ <h2>
{$tag_counter=count($value.taglist)}
{loop="value.taglist"}
<span class="label label-tag" title="{$strAddTag}">
<a href="?addtag={$value|urlencode}">{$value}</a>
<a href="./add-tag/{$value|urlencode}">{$value}</a>
</span>
{if="$tag_counter - 1 != $counter"}&middot;{/if}
{/loop}

View file

@ -49,7 +49,7 @@ <h2 class="window-title">{'Tag cloud'|t} - {$countTags} {'tags'|t}</h2>
<div id="cloudtag" class="cloudtag-container">
{loop="tags"}
<a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" style="font-size:{$value.size}em;">{$key}</a
><a href="?addtag={$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value.count}</a>
><a href="./add-tag/{$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value.count}</a>
{loop="$value.tag_plugin"}
{$value}
{/loop}

View file

@ -56,7 +56,7 @@ <h2 class="window-title">{'Tag list'|t} - {$countTags} {'tags'|t}</h2>
</a>
{/if}
<a href="?addtag={$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a>
<a href="./add-tag/{$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a>
<a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" class="tag-link">{$key}</a>
{loop="$value.tag_plugin"}

View file

@ -133,7 +133,7 @@
<a href="{$value.real_url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
{if="$value.tags"}
<div class="linktaglist">
{loop="$value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop}
{loop="$value.taglist"}<span class="linktag" title="Add tag"><a href="./add-tag/{$value|urlencode}">{$value}</a></span> {/loop}
</div>
{/if}

View file

@ -12,7 +12,7 @@
<div id="cloudtag">
{loop="$tags"}
<a href="?addtag={$key|urlencode}" class="count">{$value.count}</a><a
<a href="./add-tag/{$key|urlencode}" class="count">{$value.count}</a><a
href="?searchtags={$key|urlencode}" style="font-size:{$value.size}em;">{$key}</a>
{loop="$value.tag_plugin"}
{$value}