diff options
Diffstat (limited to 'application')
31 files changed, 282 insertions, 211 deletions
diff --git a/application/Utils.php b/application/Utils.php index 9c9eaaa2..bcfda65c 100644 --- a/application/Utils.php +++ b/application/Utils.php | |||
@@ -95,14 +95,14 @@ function escape($input) | |||
95 | return null; | 95 | return null; |
96 | } | 96 | } |
97 | 97 | ||
98 | if (is_bool($input)) { | 98 | if (is_bool($input) || is_int($input) || is_float($input) || $input instanceof DateTimeInterface) { |
99 | return $input; | 99 | return $input; |
100 | } | 100 | } |
101 | 101 | ||
102 | if (is_array($input)) { | 102 | if (is_array($input)) { |
103 | $out = array(); | 103 | $out = array(); |
104 | foreach ($input as $key => $value) { | 104 | foreach ($input as $key => $value) { |
105 | $out[$key] = escape($value); | 105 | $out[escape($key)] = escape($value); |
106 | } | 106 | } |
107 | return $out; | 107 | return $out; |
108 | } | 108 | } |
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php index 09ce6445..f5b53b01 100644 --- a/application/api/ApiMiddleware.php +++ b/application/api/ApiMiddleware.php | |||
@@ -107,7 +107,9 @@ class ApiMiddleware | |||
107 | */ | 107 | */ |
108 | protected function checkToken($request) | 108 | protected function checkToken($request) |
109 | { | 109 | { |
110 | if (! $request->hasHeader('Authorization')) { | 110 | if (!$request->hasHeader('Authorization') |
111 | && !isset($this->container->environment['REDIRECT_HTTP_AUTHORIZATION']) | ||
112 | ) { | ||
111 | throw new ApiAuthorizationException('JWT token not provided'); | 113 | throw new ApiAuthorizationException('JWT token not provided'); |
112 | } | 114 | } |
113 | 115 | ||
@@ -115,7 +117,11 @@ class ApiMiddleware | |||
115 | throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration'); | 117 | throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration'); |
116 | } | 118 | } |
117 | 119 | ||
118 | $authorization = $request->getHeaderLine('Authorization'); | 120 | if (isset($this->container->environment['REDIRECT_HTTP_AUTHORIZATION'])) { |
121 | $authorization = $this->container->environment['REDIRECT_HTTP_AUTHORIZATION']; | ||
122 | } else { | ||
123 | $authorization = $request->getHeaderLine('Authorization'); | ||
124 | } | ||
119 | 125 | ||
120 | if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) { | 126 | if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) { |
121 | throw new ApiAuthorizationException('Invalid JWT header'); | 127 | throw new ApiAuthorizationException('Invalid JWT header'); |
diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php index faebb8f5..4a6326f0 100644 --- a/application/api/ApiUtils.php +++ b/application/api/ApiUtils.php | |||
@@ -94,7 +94,7 @@ class ApiUtils | |||
94 | * | 94 | * |
95 | * @return Bookmark instance. | 95 | * @return Bookmark instance. |
96 | */ | 96 | */ |
97 | public static function buildLinkFromRequest($input, $defaultPrivate) | 97 | public static function buildBookmarkFromRequest($input, $defaultPrivate): Bookmark |
98 | { | 98 | { |
99 | $bookmark = new Bookmark(); | 99 | $bookmark = new Bookmark(); |
100 | $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; | 100 | $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; |
@@ -110,6 +110,15 @@ class ApiUtils | |||
110 | $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); | 110 | $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); |
111 | $bookmark->setPrivate($private); | 111 | $bookmark->setPrivate($private); |
112 | 112 | ||
113 | $created = \DateTime::createFromFormat(\DateTime::ATOM, $input['created'] ?? ''); | ||
114 | if ($created instanceof \DateTimeInterface) { | ||
115 | $bookmark->setCreated($created); | ||
116 | } | ||
117 | $updated = \DateTime::createFromFormat(\DateTime::ATOM, $input['updated'] ?? ''); | ||
118 | if ($updated instanceof \DateTimeInterface) { | ||
119 | $bookmark->setUpdated($updated); | ||
120 | } | ||
121 | |||
113 | return $bookmark; | 122 | return $bookmark; |
114 | } | 123 | } |
115 | 124 | ||
diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php index c4b3d0c3..88a845eb 100644 --- a/application/api/controllers/ApiController.php +++ b/application/api/controllers/ApiController.php | |||
@@ -4,6 +4,7 @@ namespace Shaarli\Api\Controllers; | |||
4 | 4 | ||
5 | use Shaarli\Bookmark\BookmarkServiceInterface; | 5 | use Shaarli\Bookmark\BookmarkServiceInterface; |
6 | use Shaarli\Config\ConfigManager; | 6 | use Shaarli\Config\ConfigManager; |
7 | use Shaarli\History; | ||
7 | use Slim\Container; | 8 | use Slim\Container; |
8 | 9 | ||
9 | /** | 10 | /** |
@@ -31,7 +32,7 @@ abstract class ApiController | |||
31 | protected $bookmarkService; | 32 | protected $bookmarkService; |
32 | 33 | ||
33 | /** | 34 | /** |
34 | * @var HistoryController | 35 | * @var History |
35 | */ | 36 | */ |
36 | protected $history; | 37 | protected $history; |
37 | 38 | ||
diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index 29247950..778097fd 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php | |||
@@ -116,7 +116,7 @@ class Links extends ApiController | |||
116 | public function postLink($request, $response) | 116 | public function postLink($request, $response) |
117 | { | 117 | { |
118 | $data = $request->getParsedBody(); | 118 | $data = $request->getParsedBody(); |
119 | $bookmark = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); | 119 | $bookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); |
120 | // duplicate by URL, return 409 Conflict | 120 | // duplicate by URL, return 409 Conflict |
121 | if (! empty($bookmark->getUrl()) | 121 | if (! empty($bookmark->getUrl()) |
122 | && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl())) | 122 | && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl())) |
@@ -155,7 +155,7 @@ class Links extends ApiController | |||
155 | $index = index_url($this->ci['environment']); | 155 | $index = index_url($this->ci['environment']); |
156 | $data = $request->getParsedBody(); | 156 | $data = $request->getParsedBody(); |
157 | 157 | ||
158 | $requestBookmark = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); | 158 | $requestBookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); |
159 | // duplicate URL on a different link, return 409 Conflict | 159 | // duplicate URL on a different link, return 409 Conflict |
160 | if (! empty($requestBookmark->getUrl()) | 160 | if (! empty($requestBookmark->getUrl()) |
161 | && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) | 161 | && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) |
diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php index e3a61146..c9ec2609 100644 --- a/application/bookmark/BookmarkFileService.php +++ b/application/bookmark/BookmarkFileService.php | |||
@@ -362,7 +362,9 @@ class BookmarkFileService implements BookmarkServiceInterface | |||
362 | */ | 362 | */ |
363 | public function filterDay($request) | 363 | public function filterDay($request) |
364 | { | 364 | { |
365 | return $this->bookmarkFilter->filter(BookmarkFilter::$FILTER_DAY, $request); | 365 | $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC; |
366 | |||
367 | return $this->bookmarkFilter->filter(BookmarkFilter::$FILTER_DAY, $request, false, $visibility); | ||
366 | } | 368 | } |
367 | 369 | ||
368 | /** | 370 | /** |
diff --git a/application/bookmark/BookmarkFilter.php b/application/bookmark/BookmarkFilter.php index 797a36b8..6636bbfe 100644 --- a/application/bookmark/BookmarkFilter.php +++ b/application/bookmark/BookmarkFilter.php | |||
@@ -115,7 +115,7 @@ class BookmarkFilter | |||
115 | return $this->filterTags($request, $casesensitive, $visibility); | 115 | return $this->filterTags($request, $casesensitive, $visibility); |
116 | } | 116 | } |
117 | case self::$FILTER_DAY: | 117 | case self::$FILTER_DAY: |
118 | return $this->filterDay($request); | 118 | return $this->filterDay($request, $visibility); |
119 | default: | 119 | default: |
120 | return $this->noFilter($visibility); | 120 | return $this->noFilter($visibility); |
121 | } | 121 | } |
@@ -425,21 +425,26 @@ class BookmarkFilter | |||
425 | * print_r($mydb->filterDay('20120125')); | 425 | * print_r($mydb->filterDay('20120125')); |
426 | * | 426 | * |
427 | * @param string $day day to filter. | 427 | * @param string $day day to filter. |
428 | * | 428 | * @param string $visibility return only all/private/public bookmarks. |
429 | |||
429 | * @return array all link matching given day. | 430 | * @return array all link matching given day. |
430 | * | 431 | * |
431 | * @throws Exception if date format is invalid. | 432 | * @throws Exception if date format is invalid. |
432 | */ | 433 | */ |
433 | public function filterDay($day) | 434 | public function filterDay($day, $visibility) |
434 | { | 435 | { |
435 | if (!checkDateFormat('Ymd', $day)) { | 436 | if (!checkDateFormat('Ymd', $day)) { |
436 | throw new Exception('Invalid date format'); | 437 | throw new Exception('Invalid date format'); |
437 | } | 438 | } |
438 | 439 | ||
439 | $filtered = []; | 440 | $filtered = []; |
440 | foreach ($this->bookmarks as $key => $l) { | 441 | foreach ($this->bookmarks as $key => $bookmark) { |
441 | if ($l->getCreated()->format('Ymd') == $day) { | 442 | if ($visibility === static::$PUBLIC && $bookmark->isPrivate()) { |
442 | $filtered[$key] = $l; | 443 | continue; |
444 | } | ||
445 | |||
446 | if ($bookmark->getCreated()->format('Ymd') == $day) { | ||
447 | $filtered[$key] = $bookmark; | ||
443 | } | 448 | } |
444 | } | 449 | } |
445 | 450 | ||
diff --git a/application/bookmark/BookmarkInitializer.php b/application/bookmark/BookmarkInitializer.php index cd2d1724..815047e3 100644 --- a/application/bookmark/BookmarkInitializer.php +++ b/application/bookmark/BookmarkInitializer.php | |||
@@ -34,25 +34,77 @@ class BookmarkInitializer | |||
34 | public function initialize() | 34 | public function initialize() |
35 | { | 35 | { |
36 | $bookmark = new Bookmark(); | 36 | $bookmark = new Bookmark(); |
37 | $bookmark->setTitle(t('My secret stuff... - Pastebin.com')); | 37 | $bookmark->setTitle('quicksilver (loop) on Vimeo ' . t('(private bookmark with thumbnail demo)')); |
38 | $bookmark->setUrl('http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8='); | 38 | $bookmark->setUrl('https://vimeo.com/153493904'); |
39 | $bookmark->setDescription(t('Shhhh! I\'m a private link only YOU can see. You can delete me too.')); | 39 | $bookmark->setDescription(t( |
40 | $bookmark->setTagsString('secretstuff'); | 40 | 'Shaarli will automatically pick up the thumbnail for links to a variety of websites. |
41 | |||
42 | Explore your new Shaarli instance by trying out controls and menus. | ||
43 | Visit the project on [Github](https://github.com/shaarli/Shaarli) or [the documentation](https://shaarli.readthedocs.io/en/master/) to learn more about Shaarli. | ||
44 | |||
45 | Now you can edit or delete the default shaares. | ||
46 | ' | ||
47 | )); | ||
48 | $bookmark->setTagsString('shaarli help thumbnail'); | ||
49 | $bookmark->setPrivate(true); | ||
50 | $this->bookmarkService->add($bookmark, false); | ||
51 | |||
52 | $bookmark = new Bookmark(); | ||
53 | $bookmark->setTitle(t('Note: Shaare descriptions')); | ||
54 | $bookmark->setDescription(t( | ||
55 | 'Adding a shaare without entering a URL creates a text-only "note" post such as this one. | ||
56 | This note is private, so you are the only one able to see it while logged in. | ||
57 | |||
58 | You can use this to keep notes, post articles, code snippets, and much more. | ||
59 | |||
60 | The Markdown formatting setting allows you to format your notes and bookmark description: | ||
61 | |||
62 | ### Title headings | ||
63 | |||
64 | #### Multiple headings levels | ||
65 | * bullet lists | ||
66 | * _italic_ text | ||
67 | * **bold** text | ||
68 | * ~~strike through~~ text | ||
69 | * `code` blocks | ||
70 | * images | ||
71 | * [links](https://en.wikipedia.org/wiki/Markdown) | ||
72 | |||
73 | Markdown also supports tables: | ||
74 | |||
75 | | Name | Type | Color | Qty | | ||
76 | | ------- | --------- | ------ | ----- | | ||
77 | | Orange | Fruit | Orange | 126 | | ||
78 | | Apple | Fruit | Any | 62 | | ||
79 | | Lemon | Fruit | Yellow | 30 | | ||
80 | | Carrot | Vegetable | Red | 14 | | ||
81 | ' | ||
82 | )); | ||
83 | $bookmark->setTagsString('shaarli help'); | ||
41 | $bookmark->setPrivate(true); | 84 | $bookmark->setPrivate(true); |
42 | $this->bookmarkService->add($bookmark, false); | 85 | $this->bookmarkService->add($bookmark, false); |
43 | 86 | ||
44 | $bookmark = new Bookmark(); | 87 | $bookmark = new Bookmark(); |
45 | $bookmark->setTitle(t('The personal, minimalist, super-fast, database free, bookmarking service')); | 88 | $bookmark->setTitle( |
46 | $bookmark->setUrl('https://shaarli.readthedocs.io', []); | 89 | 'Shaarli - ' . t('The personal, minimalist, super-fast, database free, bookmarking service') |
90 | ); | ||
47 | $bookmark->setDescription(t( | 91 | $bookmark->setDescription(t( |
48 | 'Welcome to Shaarli! This is your first public bookmark. ' | 92 | 'Welcome to Shaarli! |
49 | . 'To edit or delete me, you must first login. | 93 | |
94 | Shaarli allows you to bookmark your favorite pages, and share them with others or store them privately. | ||
95 | You can add a description to your bookmarks, such as this one, and tag them. | ||
96 | |||
97 | Create a new shaare by clicking the `+Shaare` button, or using any of the recommended tools (browser extension, mobile app, bookmarklet, REST API, etc.). | ||
50 | 98 | ||
51 | To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. | 99 | You can easily retrieve your links, even with thousands of them, using the internal search engine, or search through tags (e.g. this Shaare is tagged with `shaarli` and `help`). |
100 | Hashtags such as #shaarli #help are also supported. | ||
101 | You can also filter the available [RSS feed](/feed/atom) and picture wall by tag or plaintext search. | ||
52 | 102 | ||
53 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.' | 103 | We hope that you will enjoy using Shaarli, maintained with ❤️ by the community! |
104 | Feel free to open [an issue](https://github.com/shaarli/Shaarli/issues) if you have a suggestion or encounter an issue. | ||
105 | ' | ||
54 | )); | 106 | )); |
55 | $bookmark->setTagsString('opensource software'); | 107 | $bookmark->setTagsString('shaarli help'); |
56 | $this->bookmarkService->add($bookmark, false); | 108 | $this->bookmarkService->add($bookmark, false); |
57 | } | 109 | } |
58 | } | 110 | } |
diff --git a/application/bookmark/LinkUtils.php b/application/bookmark/LinkUtils.php index 03e1b82a..faf5dbfd 100644 --- a/application/bookmark/LinkUtils.php +++ b/application/bookmark/LinkUtils.php | |||
@@ -26,7 +26,7 @@ function html_extract_title($html) | |||
26 | */ | 26 | */ |
27 | function header_extract_charset($header) | 27 | function header_extract_charset($header) |
28 | { | 28 | { |
29 | preg_match('/charset="?([^; ]+)/i', $header, $match); | 29 | preg_match('/charset=["\']?([^; "\']+)/i', $header, $match); |
30 | if (! empty($match[1])) { | 30 | if (! empty($match[1])) { |
31 | return strtolower(trim($match[1])); | 31 | return strtolower(trim($match[1])); |
32 | } | 32 | } |
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php index 4509357c..c0c0dab9 100644 --- a/application/config/ConfigJson.php +++ b/application/config/ConfigJson.php | |||
@@ -46,7 +46,7 @@ class ConfigJson implements ConfigIO | |||
46 | // JSON_PRETTY_PRINT is available from PHP 5.4. | 46 | // JSON_PRETTY_PRINT is available from PHP 5.4. |
47 | $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; | 47 | $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; |
48 | $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); | 48 | $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); |
49 | if (!file_put_contents($filepath, $data)) { | 49 | if (empty($filepath) || !file_put_contents($filepath, $data)) { |
50 | throw new \Shaarli\Exceptions\IOException( | 50 | throw new \Shaarli\Exceptions\IOException( |
51 | $filepath, | 51 | $filepath, |
52 | t('Shaarli could not create the config file. '. | 52 | t('Shaarli could not create the config file. '. |
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php index 58067c99..55bb51b5 100644 --- a/application/container/ContainerBuilder.php +++ b/application/container/ContainerBuilder.php | |||
@@ -10,6 +10,7 @@ use Shaarli\Config\ConfigManager; | |||
10 | use Shaarli\Feed\FeedBuilder; | 10 | use Shaarli\Feed\FeedBuilder; |
11 | use Shaarli\Formatter\FormatterFactory; | 11 | use Shaarli\Formatter\FormatterFactory; |
12 | use Shaarli\Front\Controller\Visitor\ErrorController; | 12 | use Shaarli\Front\Controller\Visitor\ErrorController; |
13 | use Shaarli\Front\Controller\Visitor\ErrorNotFoundController; | ||
13 | use Shaarli\History; | 14 | use Shaarli\History; |
14 | use Shaarli\Http\HttpAccess; | 15 | use Shaarli\Http\HttpAccess; |
15 | use Shaarli\Netscape\NetscapeBookmarkUtils; | 16 | use Shaarli\Netscape\NetscapeBookmarkUtils; |
@@ -149,6 +150,9 @@ class ContainerBuilder | |||
149 | ); | 150 | ); |
150 | }; | 151 | }; |
151 | 152 | ||
153 | $container['notFoundHandler'] = function (ShaarliContainer $container): ErrorNotFoundController { | ||
154 | return new ErrorNotFoundController($container); | ||
155 | }; | ||
152 | $container['errorHandler'] = function (ShaarliContainer $container): ErrorController { | 156 | $container['errorHandler'] = function (ShaarliContainer $container): ErrorController { |
153 | return new ErrorController($container); | 157 | return new ErrorController($container); |
154 | }; | 158 | }; |
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php index 9a9a974a..66e669aa 100644 --- a/application/container/ShaarliContainer.php +++ b/application/container/ShaarliContainer.php | |||
@@ -24,21 +24,22 @@ use Slim\Container; | |||
24 | /** | 24 | /** |
25 | * Extension of Slim container to document the injected objects. | 25 | * Extension of Slim container to document the injected objects. |
26 | * | 26 | * |
27 | * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`) | 27 | * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`) |
28 | * @property BookmarkServiceInterface $bookmarkService | 28 | * @property BookmarkServiceInterface $bookmarkService |
29 | * @property CookieManager $cookieManager | 29 | * @property CookieManager $cookieManager |
30 | * @property ConfigManager $conf | 30 | * @property ConfigManager $conf |
31 | * @property mixed[] $environment $_SERVER automatically injected by Slim | 31 | * @property mixed[] $environment $_SERVER automatically injected by Slim |
32 | * @property callable $errorHandler Overrides default Slim exception display | 32 | * @property callable $errorHandler Overrides default Slim exception display |
33 | * @property FeedBuilder $feedBuilder | 33 | * @property FeedBuilder $feedBuilder |
34 | * @property FormatterFactory $formatterFactory | 34 | * @property FormatterFactory $formatterFactory |
35 | * @property History $history | 35 | * @property History $history |
36 | * @property HttpAccess $httpAccess | 36 | * @property HttpAccess $httpAccess |
37 | * @property LoginManager $loginManager | 37 | * @property LoginManager $loginManager |
38 | * @property NetscapeBookmarkUtils $netscapeBookmarkUtils | 38 | * @property NetscapeBookmarkUtils $netscapeBookmarkUtils |
39 | * @property callable $notFoundHandler Overrides default Slim exception display | ||
39 | * @property PageBuilder $pageBuilder | 40 | * @property PageBuilder $pageBuilder |
40 | * @property PageCacheManager $pageCacheManager | 41 | * @property PageCacheManager $pageCacheManager |
41 | * @property callable $phpErrorHandler Overrides default Slim PHP error display | 42 | * @property callable $phpErrorHandler Overrides default Slim PHP error display |
42 | * @property PluginManager $pluginManager | 43 | * @property PluginManager $pluginManager |
43 | * @property SessionManager $sessionManager | 44 | * @property SessionManager $sessionManager |
44 | * @property Thumbnailer $thumbnailer | 45 | * @property Thumbnailer $thumbnailer |
diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php index 3653c32f..f6def630 100644 --- a/application/feed/FeedBuilder.php +++ b/application/feed/FeedBuilder.php | |||
@@ -122,9 +122,9 @@ class FeedBuilder | |||
122 | $data['language'] = $this->getTypeLanguage($feedType); | 122 | $data['language'] = $this->getTypeLanguage($feedType); |
123 | $data['last_update'] = $this->getLatestDateFormatted($feedType); | 123 | $data['last_update'] = $this->getLatestDateFormatted($feedType); |
124 | $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; | 124 | $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; |
125 | // Remove leading slash from REQUEST_URI. | 125 | // Remove leading path from REQUEST_URI (already contained in $pageaddr). |
126 | $data['self_link'] = escape(server_url($this->serverInfo)) | 126 | $requestUri = preg_replace('#(.*?/)(feed.*)#', '$2', escape($this->serverInfo['REQUEST_URI'])); |
127 | . escape($this->serverInfo['REQUEST_URI']); | 127 | $data['self_link'] = $pageaddr . $requestUri; |
128 | $data['index_url'] = $pageaddr; | 128 | $data['index_url'] = $pageaddr; |
129 | $data['usepermalinks'] = $this->usePermalinks === true; | 129 | $data['usepermalinks'] = $this->usePermalinks === true; |
130 | $data['links'] = $linkDisplayed; | 130 | $data['links'] = $linkDisplayed; |
diff --git a/application/formatter/BookmarkFormatter.php b/application/formatter/BookmarkFormatter.php index 22ba7aae..0042dafe 100644 --- a/application/formatter/BookmarkFormatter.php +++ b/application/formatter/BookmarkFormatter.php | |||
@@ -58,7 +58,9 @@ abstract class BookmarkFormatter | |||
58 | $out['title'] = $this->formatTitle($bookmark); | 58 | $out['title'] = $this->formatTitle($bookmark); |
59 | $out['description'] = $this->formatDescription($bookmark); | 59 | $out['description'] = $this->formatDescription($bookmark); |
60 | $out['thumbnail'] = $this->formatThumbnail($bookmark); | 60 | $out['thumbnail'] = $this->formatThumbnail($bookmark); |
61 | $out['urlencoded_taglist'] = $this->formatUrlEncodedTagList($bookmark); | ||
61 | $out['taglist'] = $this->formatTagList($bookmark); | 62 | $out['taglist'] = $this->formatTagList($bookmark); |
63 | $out['urlencoded_tags'] = $this->formatUrlEncodedTagString($bookmark); | ||
62 | $out['tags'] = $this->formatTagString($bookmark); | 64 | $out['tags'] = $this->formatTagString($bookmark); |
63 | $out['sticky'] = $bookmark->isSticky(); | 65 | $out['sticky'] = $bookmark->isSticky(); |
64 | $out['private'] = $bookmark->isPrivate(); | 66 | $out['private'] = $bookmark->isPrivate(); |
@@ -182,6 +184,18 @@ abstract class BookmarkFormatter | |||
182 | } | 184 | } |
183 | 185 | ||
184 | /** | 186 | /** |
187 | * Format Url Encoded Tags | ||
188 | * | ||
189 | * @param Bookmark $bookmark instance | ||
190 | * | ||
191 | * @return array formatted Tags | ||
192 | */ | ||
193 | protected function formatUrlEncodedTagList($bookmark) | ||
194 | { | ||
195 | return array_map('urlencode', $this->filterTagList($bookmark->getTags())); | ||
196 | } | ||
197 | |||
198 | /** | ||
185 | * Format TagString | 199 | * Format TagString |
186 | * | 200 | * |
187 | * @param Bookmark $bookmark instance | 201 | * @param Bookmark $bookmark instance |
@@ -194,6 +208,18 @@ abstract class BookmarkFormatter | |||
194 | } | 208 | } |
195 | 209 | ||
196 | /** | 210 | /** |
211 | * Format TagString | ||
212 | * | ||
213 | * @param Bookmark $bookmark instance | ||
214 | * | ||
215 | * @return string formatted TagString | ||
216 | */ | ||
217 | protected function formatUrlEncodedTagString($bookmark) | ||
218 | { | ||
219 | return implode(' ', $this->formatUrlEncodedTagList($bookmark)); | ||
220 | } | ||
221 | |||
222 | /** | ||
197 | * Format Class | 223 | * Format Class |
198 | * Used to add specific CSS class for a link | 224 | * Used to add specific CSS class for a link |
199 | * | 225 | * |
diff --git a/application/formatter/BookmarkMarkdownExtraFormatter.php b/application/formatter/BookmarkMarkdownExtraFormatter.php new file mode 100644 index 00000000..0694b23f --- /dev/null +++ b/application/formatter/BookmarkMarkdownExtraFormatter.php | |||
@@ -0,0 +1,24 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli\Formatter; | ||
4 | |||
5 | use Shaarli\Config\ConfigManager; | ||
6 | |||
7 | /** | ||
8 | * Class BookmarkMarkdownExtraFormatter | ||
9 | * | ||
10 | * Format bookmark description into MarkdownExtra format. | ||
11 | * | ||
12 | * @see https://michelf.ca/projects/php-markdown/extra/ | ||
13 | * | ||
14 | * @package Shaarli\Formatter | ||
15 | */ | ||
16 | class BookmarkMarkdownExtraFormatter extends BookmarkMarkdownFormatter | ||
17 | { | ||
18 | public function __construct(ConfigManager $conf, bool $isLoggedIn) | ||
19 | { | ||
20 | parent::__construct($conf, $isLoggedIn); | ||
21 | |||
22 | $this->parsedown = new \ParsedownExtra(); | ||
23 | } | ||
24 | } | ||
diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index e675fcca..0ed7ad81 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php | |||
@@ -30,7 +30,7 @@ class ConfigureController extends ShaarliAdminController | |||
30 | 'theme_available', | 30 | 'theme_available', |
31 | ThemeUtils::getThemes($this->container->conf->get('resource.raintpl_tpl')) | 31 | ThemeUtils::getThemes($this->container->conf->get('resource.raintpl_tpl')) |
32 | ); | 32 | ); |
33 | $this->assignView('formatter_available', ['default', 'markdown']); | 33 | $this->assignView('formatter_available', ['default', 'markdown', 'markdownExtra']); |
34 | list($continents, $cities) = generateTimeZoneData( | 34 | list($continents, $cities) = generateTimeZoneData( |
35 | timezone_identifiers_list(), | 35 | timezone_identifiers_list(), |
36 | $this->container->conf->get('general.timezone') | 36 | $this->container->conf->get('general.timezone') |
diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index 33e1188e..bb083486 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php | |||
@@ -69,7 +69,7 @@ class ManageShaareController extends ShaarliAdminController | |||
69 | $retrieveDescription | 69 | $retrieveDescription |
70 | ) | 70 | ) |
71 | ); | 71 | ); |
72 | if (! empty($title) && strtolower($charset) !== 'utf-8') { | 72 | if (! empty($title) && strtolower($charset) !== 'utf-8' && mb_check_encoding($charset)) { |
73 | $title = mb_convert_encoding($title, 'utf-8', $charset); | 73 | $title = mb_convert_encoding($title, 'utf-8', $charset); |
74 | } | 74 | } |
75 | } | 75 | } |
@@ -78,13 +78,13 @@ class ManageShaareController extends ShaarliAdminController | |||
78 | $title = $this->container->conf->get('general.default_note_title', t('Note: ')); | 78 | $title = $this->container->conf->get('general.default_note_title', t('Note: ')); |
79 | } | 79 | } |
80 | 80 | ||
81 | $link = escape([ | 81 | $link = [ |
82 | 'title' => $title, | 82 | 'title' => $title, |
83 | 'url' => $url ?? '', | 83 | 'url' => $url ?? '', |
84 | 'description' => $description ?? '', | 84 | 'description' => $description ?? '', |
85 | 'tags' => $tags ?? '', | 85 | 'tags' => $tags ?? '', |
86 | 'private' => $private, | 86 | 'private' => $private, |
87 | ]); | 87 | ]; |
88 | } else { | 88 | } else { |
89 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | 89 | $formatter = $this->container->formatterFactory->getFormatter('raw'); |
90 | $link = $formatter->format($bookmark); | 90 | $link = $formatter->format($bookmark); |
@@ -127,7 +127,7 @@ class ManageShaareController extends ShaarliAdminController | |||
127 | $this->checkToken($request); | 127 | $this->checkToken($request); |
128 | 128 | ||
129 | // lf_id should only be present if the link exists. | 129 | // lf_id should only be present if the link exists. |
130 | $id = $request->getParam('lf_id') ? intval(escape($request->getParam('lf_id'))) : null; | 130 | $id = $request->getParam('lf_id') !== null ? intval(escape($request->getParam('lf_id'))) : null; |
131 | if (null !== $id && true === $this->container->bookmarkService->exists($id)) { | 131 | if (null !== $id && true === $this->container->bookmarkService->exists($id)) { |
132 | // Edit | 132 | // Edit |
133 | $bookmark = $this->container->bookmarkService->get($id); | 133 | $bookmark = $this->container->bookmarkService->get($id); |
@@ -169,7 +169,7 @@ class ManageShaareController extends ShaarliAdminController | |||
169 | return $this->redirectFromReferer( | 169 | return $this->redirectFromReferer( |
170 | $request, | 170 | $request, |
171 | $response, | 171 | $response, |
172 | ['add-shaare', 'shaare'], ['addlink', 'post', 'edit_link'], | 172 | ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'], |
173 | $bookmark->getShortUrl() | 173 | $bookmark->getShortUrl() |
174 | ); | 174 | ); |
175 | } | 175 | } |
@@ -345,14 +345,14 @@ class ManageShaareController extends ShaarliAdminController | |||
345 | $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; | 345 | $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; |
346 | } | 346 | } |
347 | 347 | ||
348 | $data = [ | 348 | $data = escape([ |
349 | 'link' => $link, | 349 | 'link' => $link, |
350 | 'link_is_new' => $isNew, | 350 | 'link_is_new' => $isNew, |
351 | 'http_referer' => escape($this->container->environment['HTTP_REFERER'] ?? ''), | 351 | 'http_referer' => $this->container->environment['HTTP_REFERER'] ?? '', |
352 | 'source' => $request->getParam('source') ?? '', | 352 | 'source' => $request->getParam('source') ?? '', |
353 | 'tags' => $tags, | 353 | 'tags' => $tags, |
354 | 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), | 354 | 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), |
355 | ]; | 355 | ]); |
356 | 356 | ||
357 | $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); | 357 | $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); |
358 | 358 | ||
diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php index 0380ef1f..2065c3e2 100644 --- a/application/front/controller/admin/ManageTagController.php +++ b/application/front/controller/admin/ManageTagController.php | |||
@@ -41,8 +41,8 @@ class ManageTagController extends ShaarliAdminController | |||
41 | 41 | ||
42 | $isDelete = null !== $request->getParam('deletetag') && null === $request->getParam('renametag'); | 42 | $isDelete = null !== $request->getParam('deletetag') && null === $request->getParam('renametag'); |
43 | 43 | ||
44 | $fromTag = escape(trim($request->getParam('fromtag') ?? '')); | 44 | $fromTag = trim($request->getParam('fromtag') ?? ''); |
45 | $toTag = escape(trim($request->getParam('totag') ?? '')); | 45 | $toTag = trim($request->getParam('totag') ?? ''); |
46 | 46 | ||
47 | if (0 === strlen($fromTag) || false === $isDelete && 0 === strlen($toTag)) { | 47 | if (0 === strlen($fromTag) || false === $isDelete && 0 === strlen($toTag)) { |
48 | $this->saveWarningMessage(t('Invalid tags provided.')); | 48 | $this->saveWarningMessage(t('Invalid tags provided.')); |
diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php index 0e09116e..8e059681 100644 --- a/application/front/controller/admin/PluginsController.php +++ b/application/front/controller/admin/PluginsController.php | |||
@@ -62,6 +62,7 @@ class PluginsController extends ShaarliAdminController | |||
62 | 62 | ||
63 | if (isset($parameters['parameters_form'])) { | 63 | if (isset($parameters['parameters_form'])) { |
64 | unset($parameters['parameters_form']); | 64 | unset($parameters['parameters_form']); |
65 | unset($parameters['token']); | ||
65 | foreach ($parameters as $param => $value) { | 66 | foreach ($parameters as $param => $value) { |
66 | $this->container->conf->set('plugins.'. $param, escape($value)); | 67 | $this->container->conf->set('plugins.'. $param, escape($value)); |
67 | } | 68 | } |
diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php index 3b5939bb..c26c9cbe 100644 --- a/application/front/controller/admin/ShaarliAdminController.php +++ b/application/front/controller/admin/ShaarliAdminController.php | |||
@@ -4,9 +4,7 @@ declare(strict_types=1); | |||
4 | 4 | ||
5 | namespace Shaarli\Front\Controller\Admin; | 5 | namespace Shaarli\Front\Controller\Admin; |
6 | 6 | ||
7 | use Shaarli\Container\ShaarliContainer; | ||
8 | use Shaarli\Front\Controller\Visitor\ShaarliVisitorController; | 7 | use Shaarli\Front\Controller\Visitor\ShaarliVisitorController; |
9 | use Shaarli\Front\Exception\UnauthorizedException; | ||
10 | use Shaarli\Front\Exception\WrongTokenException; | 8 | use Shaarli\Front\Exception\WrongTokenException; |
11 | use Shaarli\Security\SessionManager; | 9 | use Shaarli\Security\SessionManager; |
12 | use Slim\Http\Request; | 10 | use Slim\Http\Request; |
diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index 2988bee6..18368751 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php | |||
@@ -34,7 +34,7 @@ class BookmarkListController extends ShaarliVisitorController | |||
34 | $formatter = $this->container->formatterFactory->getFormatter(); | 34 | $formatter = $this->container->formatterFactory->getFormatter(); |
35 | $formatter->addContextData('base_path', $this->container->basePath); | 35 | $formatter->addContextData('base_path', $this->container->basePath); |
36 | 36 | ||
37 | $searchTags = escape(normalize_spaces($request->getParam('searchtags') ?? '')); | 37 | $searchTags = normalize_spaces($request->getParam('searchtags') ?? ''); |
38 | $searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? ''));; | 38 | $searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? ''));; |
39 | 39 | ||
40 | // Filter bookmarks according search parameters. | 40 | // Filter bookmarks according search parameters. |
@@ -104,8 +104,9 @@ class BookmarkListController extends ShaarliVisitorController | |||
104 | 'page_current' => $page, | 104 | 'page_current' => $page, |
105 | 'page_max' => $pageCount, | 105 | 'page_max' => $pageCount, |
106 | 'result_count' => count($linksToDisplay), | 106 | 'result_count' => count($linksToDisplay), |
107 | 'search_term' => $searchTerm, | 107 | 'search_term' => escape($searchTerm), |
108 | 'search_tags' => $searchTags, | 108 | 'search_tags' => escape($searchTags), |
109 | 'search_tags_url' => array_map('urlencode', explode(' ', $searchTags)), | ||
109 | 'visibility' => $visibility, | 110 | 'visibility' => $visibility, |
110 | 'links' => $linkDisp, | 111 | 'links' => $linkDisp, |
111 | ] | 112 | ] |
diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 54a4778f..07617cf1 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php | |||
@@ -132,7 +132,7 @@ class DailyController extends ShaarliVisitorController | |||
132 | 'date' => $dayDatetime, | 132 | 'date' => $dayDatetime, |
133 | 'date_rss' => $dayDatetime->format(DateTime::RSS), | 133 | 'date_rss' => $dayDatetime->format(DateTime::RSS), |
134 | 'date_human' => format_date($dayDatetime, false, true), | 134 | 'date_human' => format_date($dayDatetime, false, true), |
135 | 'absolute_url' => $indexUrl . '/daily?day=' . $day, | 135 | 'absolute_url' => $indexUrl . 'daily?day=' . $day, |
136 | 'links' => [], | 136 | 'links' => [], |
137 | ]; | 137 | ]; |
138 | 138 | ||
diff --git a/application/front/controller/visitor/ErrorNotFoundController.php b/application/front/controller/visitor/ErrorNotFoundController.php new file mode 100644 index 00000000..758dd83b --- /dev/null +++ b/application/front/controller/visitor/ErrorNotFoundController.php | |||
@@ -0,0 +1,29 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller\Visitor; | ||
6 | |||
7 | use Slim\Http\Request; | ||
8 | use Slim\Http\Response; | ||
9 | |||
10 | /** | ||
11 | * Controller used to render the 404 error page. | ||
12 | */ | ||
13 | class ErrorNotFoundController extends ShaarliVisitorController | ||
14 | { | ||
15 | public function __invoke(Request $request, Response $response): Response | ||
16 | { | ||
17 | // Request from the API | ||
18 | if (false !== strpos($request->getRequestTarget(), '/api/v1')) { | ||
19 | return $response->withStatus(404); | ||
20 | } | ||
21 | |||
22 | // This is required because the middleware is ignored if the route is not found. | ||
23 | $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/'); | ||
24 | |||
25 | $this->assignView('error_message', t('Requested page could not be found.')); | ||
26 | |||
27 | return $response->withStatus(404)->write($this->render('404')); | ||
28 | } | ||
29 | } | ||
diff --git a/application/front/controller/visitor/FeedController.php b/application/front/controller/visitor/FeedController.php index da2848c2..8d8b546a 100644 --- a/application/front/controller/visitor/FeedController.php +++ b/application/front/controller/visitor/FeedController.php | |||
@@ -46,10 +46,10 @@ class FeedController extends ShaarliVisitorController | |||
46 | 46 | ||
47 | $data = $this->container->feedBuilder->buildData($feedType, $request->getParams()); | 47 | $data = $this->container->feedBuilder->buildData($feedType, $request->getParams()); |
48 | 48 | ||
49 | $this->executePageHooks('render_feed', $data, $feedType); | 49 | $this->executePageHooks('render_feed', $data, 'feed.' . $feedType); |
50 | $this->assignAllView($data); | 50 | $this->assignAllView($data); |
51 | 51 | ||
52 | $content = $this->render('feed.'. $feedType); | 52 | $content = $this->render('feed.' . $feedType); |
53 | 53 | ||
54 | $cache->cache($content); | 54 | $cache->cache($content); |
55 | 55 | ||
diff --git a/application/front/controller/visitor/ShaarliVisitorController.php b/application/front/controller/visitor/ShaarliVisitorController.php index f17c8ed3..55c075a2 100644 --- a/application/front/controller/visitor/ShaarliVisitorController.php +++ b/application/front/controller/visitor/ShaarliVisitorController.php | |||
@@ -78,16 +78,14 @@ abstract class ShaarliVisitorController | |||
78 | 'footer', | 78 | 'footer', |
79 | ]; | 79 | ]; |
80 | 80 | ||
81 | $parameters = $this->buildPluginParameters($template); | ||
82 | |||
81 | foreach ($common_hooks as $name) { | 83 | foreach ($common_hooks as $name) { |
82 | $pluginData = []; | 84 | $pluginData = []; |
83 | $this->container->pluginManager->executeHooks( | 85 | $this->container->pluginManager->executeHooks( |
84 | 'render_' . $name, | 86 | 'render_' . $name, |
85 | $pluginData, | 87 | $pluginData, |
86 | [ | 88 | $parameters |
87 | 'target' => $template, | ||
88 | 'loggedin' => $this->container->loginManager->isLoggedIn(), | ||
89 | 'basePath' => $this->container->basePath, | ||
90 | ] | ||
91 | ); | 89 | ); |
92 | $this->assignView('plugins_' . $name, $pluginData); | 90 | $this->assignView('plugins_' . $name, $pluginData); |
93 | } | 91 | } |
@@ -95,19 +93,23 @@ abstract class ShaarliVisitorController | |||
95 | 93 | ||
96 | protected function executePageHooks(string $hook, array &$data, string $template = null): void | 94 | protected function executePageHooks(string $hook, array &$data, string $template = null): void |
97 | { | 95 | { |
98 | $params = [ | ||
99 | 'target' => $template, | ||
100 | 'loggedin' => $this->container->loginManager->isLoggedIn(), | ||
101 | 'basePath' => $this->container->basePath, | ||
102 | ]; | ||
103 | |||
104 | $this->container->pluginManager->executeHooks( | 96 | $this->container->pluginManager->executeHooks( |
105 | $hook, | 97 | $hook, |
106 | $data, | 98 | $data, |
107 | $params | 99 | $this->buildPluginParameters($template) |
108 | ); | 100 | ); |
109 | } | 101 | } |
110 | 102 | ||
103 | protected function buildPluginParameters(?string $template): array | ||
104 | { | ||
105 | return [ | ||
106 | 'target' => $template, | ||
107 | 'loggedin' => $this->container->loginManager->isLoggedIn(), | ||
108 | 'basePath' => $this->container->basePath, | ||
109 | 'bookmarkService' => $this->container->bookmarkService | ||
110 | ]; | ||
111 | } | ||
112 | |||
111 | /** | 113 | /** |
112 | * Simple helper which prepend the base path to redirect path. | 114 | * Simple helper which prepend the base path to redirect path. |
113 | * | 115 | * |
@@ -140,6 +142,13 @@ abstract class ShaarliVisitorController | |||
140 | 142 | ||
141 | if (null !== $referer) { | 143 | if (null !== $referer) { |
142 | $currentUrl = parse_url($referer); | 144 | $currentUrl = parse_url($referer); |
145 | // If the referer is not related to Shaarli instance, redirect to default | ||
146 | if (isset($currentUrl['host']) | ||
147 | && strpos(index_url($this->container->environment), $currentUrl['host']) === false | ||
148 | ) { | ||
149 | return $response->withRedirect($defaultPath); | ||
150 | } | ||
151 | |||
143 | parse_str($currentUrl['query'] ?? '', $params); | 152 | parse_str($currentUrl['query'] ?? '', $params); |
144 | $path = $currentUrl['path'] ?? $defaultPath; | 153 | $path = $currentUrl['path'] ?? $defaultPath; |
145 | } else { | 154 | } else { |
diff --git a/application/front/controller/visitor/TagCloudController.php b/application/front/controller/visitor/TagCloudController.php index f9c529bc..76ed7690 100644 --- a/application/front/controller/visitor/TagCloudController.php +++ b/application/front/controller/visitor/TagCloudController.php | |||
@@ -66,10 +66,18 @@ class TagCloudController extends ShaarliVisitorController | |||
66 | $tags = $this->formatTagsForCloud($tags); | 66 | $tags = $this->formatTagsForCloud($tags); |
67 | } | 67 | } |
68 | 68 | ||
69 | $tagsUrl = []; | ||
70 | foreach ($tags as $tag => $value) { | ||
71 | $tagsUrl[escape($tag)] = urlencode((string) $tag); | ||
72 | } | ||
73 | |||
69 | $searchTags = implode(' ', escape($filteringTags)); | 74 | $searchTags = implode(' ', escape($filteringTags)); |
75 | $searchTagsUrl = urlencode(implode(' ', $filteringTags)); | ||
70 | $data = [ | 76 | $data = [ |
71 | 'search_tags' => $searchTags, | 77 | 'search_tags' => escape($searchTags), |
72 | 'tags' => $tags, | 78 | 'search_tags_url' => $searchTagsUrl, |
79 | 'tags' => escape($tags), | ||
80 | 'tags_url' => $tagsUrl, | ||
73 | ]; | 81 | ]; |
74 | $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); | 82 | $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); |
75 | $this->assignAllView($data); | 83 | $this->assignAllView($data); |
diff --git a/application/http/HttpUtils.php b/application/http/HttpUtils.php index 4fc4e3dc..9f414073 100644 --- a/application/http/HttpUtils.php +++ b/application/http/HttpUtils.php | |||
@@ -369,7 +369,11 @@ function server_url($server) | |||
369 | */ | 369 | */ |
370 | function index_url($server) | 370 | function index_url($server) |
371 | { | 371 | { |
372 | $scriptname = $server['SCRIPT_NAME'] ?? ''; | 372 | if (defined('SHAARLI_ROOT_URL') && null !== SHAARLI_ROOT_URL) { |
373 | return rtrim(SHAARLI_ROOT_URL, '/') . '/'; | ||
374 | } | ||
375 | |||
376 | $scriptname = !empty($server['SCRIPT_NAME']) ? $server['SCRIPT_NAME'] : '/'; | ||
373 | if (endsWith($scriptname, 'index.php')) { | 377 | if (endsWith($scriptname, 'index.php')) { |
374 | $scriptname = substr($scriptname, 0, -9); | 378 | $scriptname = substr($scriptname, 0, -9); |
375 | } | 379 | } |
@@ -392,7 +396,7 @@ function page_url($server) | |||
392 | $scriptname = substr($scriptname, 0, -9); | 396 | $scriptname = substr($scriptname, 0, -9); |
393 | } | 397 | } |
394 | 398 | ||
395 | $route = ltrim($server['REQUEST_URI'] ?? '', $scriptname); | 399 | $route = preg_replace('@^' . $scriptname . '@', '', $server['REQUEST_URI'] ?? ''); |
396 | if (! empty($server['QUERY_STRING'])) { | 400 | if (! empty($server['QUERY_STRING'])) { |
397 | return index_url($server) . $route . '?' . $server['QUERY_STRING']; | 401 | return index_url($server) . $route . '?' . $server['QUERY_STRING']; |
398 | } | 402 | } |
diff --git a/application/legacy/LegacyController.php b/application/legacy/LegacyController.php index e16dd0f4..826604e7 100644 --- a/application/legacy/LegacyController.php +++ b/application/legacy/LegacyController.php | |||
@@ -39,13 +39,23 @@ class LegacyController extends ShaarliVisitorController | |||
39 | /** Legacy route: ?post= */ | 39 | /** Legacy route: ?post= */ |
40 | public function post(Request $request, Response $response): Response | 40 | public function post(Request $request, Response $response): Response |
41 | { | 41 | { |
42 | $parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : ''; | ||
43 | $route = '/admin/shaare'; | 42 | $route = '/admin/shaare'; |
43 | $buildParameters = function (?array $parameters, bool $encode) { | ||
44 | if ($encode) { | ||
45 | $parameters = array_map('urlencode', $parameters); | ||
46 | } | ||
47 | |||
48 | return count($parameters) > 0 ? '?' . http_build_query($parameters) : ''; | ||
49 | }; | ||
50 | |||
44 | 51 | ||
45 | if (!$this->container->loginManager->isLoggedIn()) { | 52 | if (!$this->container->loginManager->isLoggedIn()) { |
53 | $parameters = $buildParameters($request->getQueryParams(), true); | ||
46 | return $this->redirect($response, '/login?returnurl='. $this->getBasePath() . $route . $parameters); | 54 | return $this->redirect($response, '/login?returnurl='. $this->getBasePath() . $route . $parameters); |
47 | } | 55 | } |
48 | 56 | ||
57 | $parameters = $buildParameters($request->getQueryParams(), false); | ||
58 | |||
49 | return $this->redirect($response, $route . $parameters); | 59 | return $this->redirect($response, $route . $parameters); |
50 | } | 60 | } |
51 | 61 | ||
diff --git a/application/legacy/LegacyRouter.php b/application/legacy/LegacyRouter.php index cea99154..0449c7e1 100644 --- a/application/legacy/LegacyRouter.php +++ b/application/legacy/LegacyRouter.php | |||
@@ -17,15 +17,15 @@ class LegacyRouter | |||
17 | 17 | ||
18 | public static $PAGE_PICWALL = 'picwall'; | 18 | public static $PAGE_PICWALL = 'picwall'; |
19 | 19 | ||
20 | public static $PAGE_TAGCLOUD = 'tagcloud'; | 20 | public static $PAGE_TAGCLOUD = 'tag.cloud'; |
21 | 21 | ||
22 | public static $PAGE_TAGLIST = 'taglist'; | 22 | public static $PAGE_TAGLIST = 'tag.list'; |
23 | 23 | ||
24 | public static $PAGE_DAILY = 'daily'; | 24 | public static $PAGE_DAILY = 'daily'; |
25 | 25 | ||
26 | public static $PAGE_FEED_ATOM = 'atom'; | 26 | public static $PAGE_FEED_ATOM = 'feed.atom'; |
27 | 27 | ||
28 | public static $PAGE_FEED_RSS = 'rss'; | 28 | public static $PAGE_FEED_RSS = 'feed.rss'; |
29 | 29 | ||
30 | public static $PAGE_TOOLS = 'tools'; | 30 | public static $PAGE_TOOLS = 'tools'; |
31 | 31 | ||
@@ -37,7 +37,7 @@ class LegacyRouter | |||
37 | 37 | ||
38 | public static $PAGE_ADDLINK = 'addlink'; | 38 | public static $PAGE_ADDLINK = 'addlink'; |
39 | 39 | ||
40 | public static $PAGE_EDITLINK = 'edit_link'; | 40 | public static $PAGE_EDITLINK = 'editlink'; |
41 | 41 | ||
42 | public static $PAGE_DELETELINK = 'delete_link'; | 42 | public static $PAGE_DELETELINK = 'delete_link'; |
43 | 43 | ||
@@ -60,128 +60,4 @@ class LegacyRouter | |||
60 | public static $PAGE_THUMBS_UPDATE = 'thumbs_update'; | 60 | public static $PAGE_THUMBS_UPDATE = 'thumbs_update'; |
61 | 61 | ||
62 | public static $GET_TOKEN = 'token'; | 62 | public static $GET_TOKEN = 'token'; |
63 | |||
64 | /** | ||
65 | * Reproducing renderPage() if hell, to avoid regression. | ||
66 | * | ||
67 | * This highlights how bad this needs to be rewrite, | ||
68 | * but let's focus on plugins for now. | ||
69 | * | ||
70 | * @param string $query $_SERVER['QUERY_STRING']. | ||
71 | * @param array $get $_SERVER['GET']. | ||
72 | * @param bool $loggedIn true if authenticated user. | ||
73 | * | ||
74 | * @return string page found. | ||
75 | */ | ||
76 | public static function findPage($query, $get, $loggedIn) | ||
77 | { | ||
78 | $loggedIn = ($loggedIn === true) ? true : false; | ||
79 | |||
80 | if (empty($query) && !isset($get['edit_link']) && !isset($get['post'])) { | ||
81 | return self::$PAGE_LINKLIST; | ||
82 | } | ||
83 | |||
84 | if (startsWith($query, 'do=' . self::$PAGE_LOGIN) && $loggedIn === false) { | ||
85 | return self::$PAGE_LOGIN; | ||
86 | } | ||
87 | |||
88 | if (startsWith($query, 'do=' . self::$PAGE_PICWALL)) { | ||
89 | return self::$PAGE_PICWALL; | ||
90 | } | ||
91 | |||
92 | if (startsWith($query, 'do=' . self::$PAGE_TAGCLOUD)) { | ||
93 | return self::$PAGE_TAGCLOUD; | ||
94 | } | ||
95 | |||
96 | if (startsWith($query, 'do=' . self::$PAGE_TAGLIST)) { | ||
97 | return self::$PAGE_TAGLIST; | ||
98 | } | ||
99 | |||
100 | if (startsWith($query, 'do=' . self::$PAGE_OPENSEARCH)) { | ||
101 | return self::$PAGE_OPENSEARCH; | ||
102 | } | ||
103 | |||
104 | if (startsWith($query, 'do=' . self::$PAGE_DAILY)) { | ||
105 | return self::$PAGE_DAILY; | ||
106 | } | ||
107 | |||
108 | if (startsWith($query, 'do=' . self::$PAGE_FEED_ATOM)) { | ||
109 | return self::$PAGE_FEED_ATOM; | ||
110 | } | ||
111 | |||
112 | if (startsWith($query, 'do=' . self::$PAGE_FEED_RSS)) { | ||
113 | return self::$PAGE_FEED_RSS; | ||
114 | } | ||
115 | |||
116 | if (startsWith($query, 'do=' . self::$PAGE_THUMBS_UPDATE)) { | ||
117 | return self::$PAGE_THUMBS_UPDATE; | ||
118 | } | ||
119 | |||
120 | if (startsWith($query, 'do=' . self::$AJAX_THUMB_UPDATE)) { | ||
121 | return self::$AJAX_THUMB_UPDATE; | ||
122 | } | ||
123 | |||
124 | // At this point, only loggedin pages. | ||
125 | if (!$loggedIn) { | ||
126 | return self::$PAGE_LINKLIST; | ||
127 | } | ||
128 | |||
129 | if (startsWith($query, 'do=' . self::$PAGE_TOOLS)) { | ||
130 | return self::$PAGE_TOOLS; | ||
131 | } | ||
132 | |||
133 | if (startsWith($query, 'do=' . self::$PAGE_CHANGEPASSWORD)) { | ||
134 | return self::$PAGE_CHANGEPASSWORD; | ||
135 | } | ||
136 | |||
137 | if (startsWith($query, 'do=' . self::$PAGE_CONFIGURE)) { | ||
138 | return self::$PAGE_CONFIGURE; | ||
139 | } | ||
140 | |||
141 | if (startsWith($query, 'do=' . self::$PAGE_CHANGETAG)) { | ||
142 | return self::$PAGE_CHANGETAG; | ||
143 | } | ||
144 | |||
145 | if (startsWith($query, 'do=' . self::$PAGE_ADDLINK)) { | ||
146 | return self::$PAGE_ADDLINK; | ||
147 | } | ||
148 | |||
149 | if (isset($get['edit_link']) || isset($get['post'])) { | ||
150 | return self::$PAGE_EDITLINK; | ||
151 | } | ||
152 | |||
153 | if (isset($get['delete_link'])) { | ||
154 | return self::$PAGE_DELETELINK; | ||
155 | } | ||
156 | |||
157 | if (isset($get[self::$PAGE_CHANGE_VISIBILITY])) { | ||
158 | return self::$PAGE_CHANGE_VISIBILITY; | ||
159 | } | ||
160 | |||
161 | if (startsWith($query, 'do=' . self::$PAGE_PINLINK)) { | ||
162 | return self::$PAGE_PINLINK; | ||
163 | } | ||
164 | |||
165 | if (startsWith($query, 'do=' . self::$PAGE_EXPORT)) { | ||
166 | return self::$PAGE_EXPORT; | ||
167 | } | ||
168 | |||
169 | if (startsWith($query, 'do=' . self::$PAGE_IMPORT)) { | ||
170 | return self::$PAGE_IMPORT; | ||
171 | } | ||
172 | |||
173 | if (startsWith($query, 'do=' . self::$PAGE_PLUGINSADMIN)) { | ||
174 | return self::$PAGE_PLUGINSADMIN; | ||
175 | } | ||
176 | |||
177 | if (startsWith($query, 'do=' . self::$PAGE_SAVE_PLUGINSADMIN)) { | ||
178 | return self::$PAGE_SAVE_PLUGINSADMIN; | ||
179 | } | ||
180 | |||
181 | if (startsWith($query, 'do=' . self::$GET_TOKEN)) { | ||
182 | return self::$GET_TOKEN; | ||
183 | } | ||
184 | |||
185 | return self::$PAGE_LINKLIST; | ||
186 | } | ||
187 | } | 63 | } |
diff --git a/application/plugin/PluginManager.php b/application/plugin/PluginManager.php index 2d93cb3a..1b2197c9 100644 --- a/application/plugin/PluginManager.php +++ b/application/plugin/PluginManager.php | |||
@@ -100,16 +100,17 @@ class PluginManager | |||
100 | */ | 100 | */ |
101 | public function executeHooks($hook, &$data, $params = array()) | 101 | public function executeHooks($hook, &$data, $params = array()) |
102 | { | 102 | { |
103 | if (!empty($params['target'])) { | 103 | $metadataParameters = [ |
104 | $data['_PAGE_'] = $params['target']; | 104 | 'target' => '_PAGE_', |
105 | } | 105 | 'loggedin' => '_LOGGEDIN_', |
106 | 106 | 'basePath' => '_BASE_PATH_', | |
107 | if (isset($params['loggedin'])) { | 107 | 'bookmarkService' => '_BOOKMARK_SERVICE_', |
108 | $data['_LOGGEDIN_'] = $params['loggedin']; | 108 | ]; |
109 | } | 109 | |
110 | 110 | foreach ($metadataParameters as $parameter => $metaKey) { | |
111 | if (isset($params['basePath'])) { | 111 | if (array_key_exists($parameter, $params)) { |
112 | $data['_BASE_PATH_'] = $params['basePath']; | 112 | $data[$metaKey] = $params[$parameter]; |
113 | } | ||
113 | } | 114 | } |
114 | 115 | ||
115 | foreach ($this->loadedPlugins as $plugin) { | 116 | foreach ($this->loadedPlugins as $plugin) { |
@@ -124,6 +125,10 @@ class PluginManager | |||
124 | } | 125 | } |
125 | } | 126 | } |
126 | } | 127 | } |
128 | |||
129 | foreach ($metadataParameters as $metaKey) { | ||
130 | unset($data[$metaKey]); | ||
131 | } | ||
127 | } | 132 | } |
128 | 133 | ||
129 | /** | 134 | /** |
diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php index c52e3b76..41b357dd 100644 --- a/application/render/PageBuilder.php +++ b/application/render/PageBuilder.php | |||
@@ -137,7 +137,7 @@ class PageBuilder | |||
137 | $this->tpl->assign('language', $this->conf->get('translation.language')); | 137 | $this->tpl->assign('language', $this->conf->get('translation.language')); |
138 | 138 | ||
139 | if ($this->bookmarkService !== null) { | 139 | if ($this->bookmarkService !== null) { |
140 | $this->tpl->assign('tags', $this->bookmarkService->bookmarksCountPerTag()); | 140 | $this->tpl->assign('tags', escape($this->bookmarkService->bookmarksCountPerTag())); |
141 | } | 141 | } |
142 | 142 | ||
143 | $this->tpl->assign( | 143 | $this->tpl->assign( |