diff options
Diffstat (limited to 'application/front/controller/visitor')
10 files changed, 154 insertions, 104 deletions
diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index 18368751..fe8231be 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php | |||
@@ -35,7 +35,8 @@ class BookmarkListController extends ShaarliVisitorController | |||
35 | $formatter->addContextData('base_path', $this->container->basePath); | 35 | $formatter->addContextData('base_path', $this->container->basePath); |
36 | 36 | ||
37 | $searchTags = 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 | ||
40 | // Filter bookmarks according search parameters. | 41 | // Filter bookmarks according search parameters. |
41 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); | 42 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); |
@@ -95,6 +96,10 @@ class BookmarkListController extends ShaarliVisitorController | |||
95 | $next_page_url = '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl; | 96 | $next_page_url = '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl; |
96 | } | 97 | } |
97 | 98 | ||
99 | $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); | ||
100 | $searchTagsUrlEncoded = array_map('urlencode', tags_str2array($searchTags, $tagsSeparator)); | ||
101 | $searchTags = !empty($searchTags) ? trim($searchTags, $tagsSeparator) . $tagsSeparator : ''; | ||
102 | |||
98 | // Fill all template fields. | 103 | // Fill all template fields. |
99 | $data = array_merge( | 104 | $data = array_merge( |
100 | $this->initializeTemplateVars(), | 105 | $this->initializeTemplateVars(), |
@@ -106,7 +111,7 @@ class BookmarkListController extends ShaarliVisitorController | |||
106 | 'result_count' => count($linksToDisplay), | 111 | 'result_count' => count($linksToDisplay), |
107 | 'search_term' => escape($searchTerm), | 112 | 'search_term' => escape($searchTerm), |
108 | 'search_tags' => escape($searchTags), | 113 | 'search_tags' => escape($searchTags), |
109 | 'search_tags_url' => array_map('urlencode', explode(' ', $searchTags)), | 114 | 'search_tags_url' => $searchTagsUrlEncoded, |
110 | 'visibility' => $visibility, | 115 | 'visibility' => $visibility, |
111 | 'links' => $linkDisp, | 116 | 'links' => $linkDisp, |
112 | ] | 117 | ] |
@@ -119,8 +124,9 @@ class BookmarkListController extends ShaarliVisitorController | |||
119 | return '[' . $tag . ']'; | 124 | return '[' . $tag . ']'; |
120 | }; | 125 | }; |
121 | $data['pagetitle'] .= ! empty($searchTags) | 126 | $data['pagetitle'] .= ! empty($searchTags) |
122 | ? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchTags))) . ' ' | 127 | ? implode(' ', array_map($bracketWrap, tags_str2array($searchTags, $tagsSeparator))) . ' ' |
123 | : ''; | 128 | : '' |
129 | ; | ||
124 | $data['pagetitle'] .= '- '; | 130 | $data['pagetitle'] .= '- '; |
125 | } | 131 | } |
126 | 132 | ||
@@ -137,8 +143,10 @@ class BookmarkListController extends ShaarliVisitorController | |||
137 | */ | 143 | */ |
138 | public function permalink(Request $request, Response $response, array $args): Response | 144 | public function permalink(Request $request, Response $response, array $args): Response |
139 | { | 145 | { |
146 | $privateKey = $request->getParam('key'); | ||
147 | |||
140 | try { | 148 | try { |
141 | $bookmark = $this->container->bookmarkService->findByHash($args['hash']); | 149 | $bookmark = $this->container->bookmarkService->findByHash($args['hash'], $privateKey); |
142 | } catch (BookmarkNotFoundException $e) { | 150 | } catch (BookmarkNotFoundException $e) { |
143 | $this->assignView('error_message', $e->getMessage()); | 151 | $this->assignView('error_message', $e->getMessage()); |
144 | 152 | ||
@@ -153,7 +161,7 @@ class BookmarkListController extends ShaarliVisitorController | |||
153 | $data = array_merge( | 161 | $data = array_merge( |
154 | $this->initializeTemplateVars(), | 162 | $this->initializeTemplateVars(), |
155 | [ | 163 | [ |
156 | 'pagetitle' => $bookmark->getTitle() .' - '. $this->container->conf->get('general.title', 'Shaarli'), | 164 | 'pagetitle' => $bookmark->getTitle() . ' - ' . $this->container->conf->get('general.title', 'Shaarli'), |
157 | 'links' => [$formatter->format($bookmark)], | 165 | 'links' => [$formatter->format($bookmark)], |
158 | ] | 166 | ] |
159 | ); | 167 | ); |
@@ -169,19 +177,25 @@ class BookmarkListController extends ShaarliVisitorController | |||
169 | */ | 177 | */ |
170 | protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool | 178 | protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool |
171 | { | 179 | { |
172 | // Logged in, thumbnails enabled, not a note, is HTTP | 180 | if (false === $this->container->loginManager->isLoggedIn()) { |
173 | // and (never retrieved yet or no valid cache file) | 181 | return false; |
174 | if ($this->container->loginManager->isLoggedIn() | 182 | } |
175 | && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE | 183 | |
176 | && false !== $bookmark->getThumbnail() | 184 | // If thumbnail should be updated, we reset it to null |
177 | && !$bookmark->isNote() | 185 | if ($bookmark->shouldUpdateThumbnail()) { |
178 | && (null === $bookmark->getThumbnail() || !is_file($bookmark->getThumbnail())) | 186 | $bookmark->setThumbnail(null); |
179 | && startsWith(strtolower($bookmark->getUrl()), 'http') | 187 | |
180 | ) { | 188 | // Requires an update, not async retrieval, thumbnails enabled |
181 | $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); | 189 | if ( |
182 | $this->container->bookmarkService->set($bookmark, $writeDatastore); | 190 | $bookmark->shouldUpdateThumbnail() |
183 | 191 | && true !== $this->container->conf->get('general.enable_async_metadata', true) | |
184 | return true; | 192 | && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE |
193 | ) { | ||
194 | $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); | ||
195 | $this->container->bookmarkService->set($bookmark, $writeDatastore); | ||
196 | |||
197 | return true; | ||
198 | } | ||
185 | } | 199 | } |
186 | 200 | ||
187 | return false; | 201 | return false; |
@@ -198,6 +212,7 @@ class BookmarkListController extends ShaarliVisitorController | |||
198 | 'page_max' => '', | 212 | 'page_max' => '', |
199 | 'search_tags' => '', | 213 | 'search_tags' => '', |
200 | 'result_count' => '', | 214 | 'result_count' => '', |
215 | 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true) | ||
201 | ]; | 216 | ]; |
202 | } | 217 | } |
203 | 218 | ||
diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 07617cf1..846cfe22 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php | |||
@@ -5,8 +5,8 @@ declare(strict_types=1); | |||
5 | namespace Shaarli\Front\Controller\Visitor; | 5 | namespace Shaarli\Front\Controller\Visitor; |
6 | 6 | ||
7 | use DateTime; | 7 | use DateTime; |
8 | use DateTimeImmutable; | ||
9 | use Shaarli\Bookmark\Bookmark; | 8 | use Shaarli\Bookmark\Bookmark; |
9 | use Shaarli\Helper\DailyPageHelper; | ||
10 | use Shaarli\Render\TemplatePage; | 10 | use Shaarli\Render\TemplatePage; |
11 | use Slim\Http\Request; | 11 | use Slim\Http\Request; |
12 | use Slim\Http\Response; | 12 | use Slim\Http\Response; |
@@ -26,32 +26,20 @@ class DailyController extends ShaarliVisitorController | |||
26 | */ | 26 | */ |
27 | public function index(Request $request, Response $response): Response | 27 | public function index(Request $request, Response $response): Response |
28 | { | 28 | { |
29 | $day = $request->getQueryParam('day') ?? date('Ymd'); | 29 | $type = DailyPageHelper::extractRequestedType($request); |
30 | 30 | $format = DailyPageHelper::getFormatByType($type); | |
31 | $availableDates = $this->container->bookmarkService->days(); | 31 | $latestBookmark = $this->container->bookmarkService->getLatest(); |
32 | $nbAvailableDates = count($availableDates); | 32 | $dateTime = DailyPageHelper::extractRequestedDateTime($type, $request->getQueryParam($type), $latestBookmark); |
33 | $index = array_search($day, $availableDates); | 33 | $start = DailyPageHelper::getStartDateTimeByType($type, $dateTime); |
34 | 34 | $end = DailyPageHelper::getEndDateTimeByType($type, $dateTime); | |
35 | if ($index === false) { | 35 | $dailyDesc = DailyPageHelper::getDescriptionByType($type, $dateTime); |
36 | // no bookmarks for day, but at least one day with bookmarks | 36 | |
37 | $day = $availableDates[$nbAvailableDates - 1] ?? $day; | 37 | $linksToDisplay = $this->container->bookmarkService->findByDate( |
38 | $previousDay = $availableDates[$nbAvailableDates - 2] ?? ''; | 38 | $start, |
39 | } else { | 39 | $end, |
40 | $previousDay = $availableDates[$index - 1] ?? ''; | 40 | $previousDay, |
41 | $nextDay = $availableDates[$index + 1] ?? ''; | 41 | $nextDay |
42 | } | 42 | ); |
43 | |||
44 | if ($day === date('Ymd')) { | ||
45 | $this->assignView('dayDesc', t('Today')); | ||
46 | } elseif ($day === date('Ymd', strtotime('-1 days'))) { | ||
47 | $this->assignView('dayDesc', t('Yesterday')); | ||
48 | } | ||
49 | |||
50 | try { | ||
51 | $linksToDisplay = $this->container->bookmarkService->filterDay($day); | ||
52 | } catch (\Exception $exc) { | ||
53 | $linksToDisplay = []; | ||
54 | } | ||
55 | 43 | ||
56 | $formatter = $this->container->formatterFactory->getFormatter(); | 44 | $formatter = $this->container->formatterFactory->getFormatter(); |
57 | $formatter->addContextData('base_path', $this->container->basePath); | 45 | $formatter->addContextData('base_path', $this->container->basePath); |
@@ -63,13 +51,15 @@ class DailyController extends ShaarliVisitorController | |||
63 | $linksToDisplay[$key]['description'] = $bookmark->getDescription(); | 51 | $linksToDisplay[$key]['description'] = $bookmark->getDescription(); |
64 | } | 52 | } |
65 | 53 | ||
66 | $dayDate = DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000'); | ||
67 | $data = [ | 54 | $data = [ |
68 | 'linksToDisplay' => $linksToDisplay, | 55 | 'linksToDisplay' => $linksToDisplay, |
69 | 'day' => $dayDate->getTimestamp(), | 56 | 'dayDate' => $start, |
70 | 'dayDate' => $dayDate, | 57 | 'day' => $start->getTimestamp(), |
71 | 'previousday' => $previousDay ?? '', | 58 | 'previousday' => $previousDay ? $previousDay->format($format) : '', |
72 | 'nextday' => $nextDay ?? '', | 59 | 'nextday' => $nextDay ? $nextDay->format($format) : '', |
60 | 'dayDesc' => $dailyDesc, | ||
61 | 'type' => $type, | ||
62 | 'localizedType' => $this->translateType($type), | ||
73 | ]; | 63 | ]; |
74 | 64 | ||
75 | // Hooks are called before column construction so that plugins don't have to deal with columns. | 65 | // Hooks are called before column construction so that plugins don't have to deal with columns. |
@@ -82,7 +72,7 @@ class DailyController extends ShaarliVisitorController | |||
82 | $mainTitle = $this->container->conf->get('general.title', 'Shaarli'); | 72 | $mainTitle = $this->container->conf->get('general.title', 'Shaarli'); |
83 | $this->assignView( | 73 | $this->assignView( |
84 | 'pagetitle', | 74 | 'pagetitle', |
85 | t('Daily') .' - '. format_date($dayDate, false) . ' - ' . $mainTitle | 75 | $data['localizedType'] . ' - ' . $data['dayDesc'] . ' - ' . $mainTitle |
86 | ); | 76 | ); |
87 | 77 | ||
88 | return $response->write($this->render(TemplatePage::DAILY)); | 78 | return $response->write($this->render(TemplatePage::DAILY)); |
@@ -106,11 +96,14 @@ class DailyController extends ShaarliVisitorController | |||
106 | } | 96 | } |
107 | 97 | ||
108 | $days = []; | 98 | $days = []; |
99 | $type = DailyPageHelper::extractRequestedType($request); | ||
100 | $format = DailyPageHelper::getFormatByType($type); | ||
101 | $length = DailyPageHelper::getRssLengthByType($type); | ||
109 | foreach ($this->container->bookmarkService->search() as $bookmark) { | 102 | foreach ($this->container->bookmarkService->search() as $bookmark) { |
110 | $day = $bookmark->getCreated()->format('Ymd'); | 103 | $day = $bookmark->getCreated()->format($format); |
111 | 104 | ||
112 | // Stop iterating after DAILY_RSS_NB_DAYS entries | 105 | // Stop iterating after DAILY_RSS_NB_DAYS entries |
113 | if (count($days) === static::$DAILY_RSS_NB_DAYS && !isset($days[$day])) { | 106 | if (count($days) === $length && !isset($days[$day])) { |
114 | break; | 107 | break; |
115 | } | 108 | } |
116 | 109 | ||
@@ -127,12 +120,19 @@ class DailyController extends ShaarliVisitorController | |||
127 | 120 | ||
128 | /** @var Bookmark[] $bookmarks */ | 121 | /** @var Bookmark[] $bookmarks */ |
129 | foreach ($days as $day => $bookmarks) { | 122 | foreach ($days as $day => $bookmarks) { |
130 | $dayDatetime = DateTimeImmutable::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000'); | 123 | $dayDateTime = DailyPageHelper::extractRequestedDateTime($type, (string) $day); |
124 | $endDateTime = DailyPageHelper::getEndDateTimeByType($type, $dayDateTime); | ||
125 | |||
126 | // We only want the RSS entry to be published when the period is over. | ||
127 | if (new DateTime() < $endDateTime) { | ||
128 | continue; | ||
129 | } | ||
130 | |||
131 | $dataPerDay[$day] = [ | 131 | $dataPerDay[$day] = [ |
132 | 'date' => $dayDatetime, | 132 | 'date' => $endDateTime, |
133 | 'date_rss' => $dayDatetime->format(DateTime::RSS), | 133 | 'date_rss' => $endDateTime->format(DateTime::RSS), |
134 | 'date_human' => format_date($dayDatetime, false, true), | 134 | 'date_human' => DailyPageHelper::getDescriptionByType($type, $dayDateTime), |
135 | 'absolute_url' => $indexUrl . 'daily?day=' . $day, | 135 | 'absolute_url' => $indexUrl . 'daily?' . $type . '=' . $day, |
136 | 'links' => [], | 136 | 'links' => [], |
137 | ]; | 137 | ]; |
138 | 138 | ||
@@ -141,16 +141,20 @@ class DailyController extends ShaarliVisitorController | |||
141 | 141 | ||
142 | // Make permalink URL absolute | 142 | // Make permalink URL absolute |
143 | if ($bookmark->isNote()) { | 143 | if ($bookmark->isNote()) { |
144 | $dataPerDay[$day]['links'][$key]['url'] = $indexUrl . $bookmark->getUrl(); | 144 | $dataPerDay[$day]['links'][$key]['url'] = rtrim($indexUrl, '/') . $bookmark->getUrl(); |
145 | } | 145 | } |
146 | } | 146 | } |
147 | } | 147 | } |
148 | 148 | ||
149 | $this->assignView('title', $this->container->conf->get('general.title', 'Shaarli')); | 149 | $this->assignAllView([ |
150 | $this->assignView('index_url', $indexUrl); | 150 | 'title' => $this->container->conf->get('general.title', 'Shaarli'), |
151 | $this->assignView('page_url', $pageUrl); | 151 | 'index_url' => $indexUrl, |
152 | $this->assignView('hide_timestamps', $this->container->conf->get('privacy.hide_timestamps', false)); | 152 | 'page_url' => $pageUrl, |
153 | $this->assignView('days', $dataPerDay); | 153 | 'hide_timestamps' => $this->container->conf->get('privacy.hide_timestamps', false), |
154 | 'days' => $dataPerDay, | ||
155 | 'type' => $type, | ||
156 | 'localizedType' => $this->translateType($type), | ||
157 | ]); | ||
154 | 158 | ||
155 | $rssContent = $this->render(TemplatePage::DAILY_RSS); | 159 | $rssContent = $this->render(TemplatePage::DAILY_RSS); |
156 | 160 | ||
@@ -189,4 +193,13 @@ class DailyController extends ShaarliVisitorController | |||
189 | 193 | ||
190 | return $columns; | 194 | return $columns; |
191 | } | 195 | } |
196 | |||
197 | protected function translateType($type): string | ||
198 | { | ||
199 | return [ | ||
200 | t('day') => t('Daily'), | ||
201 | t('week') => t('Weekly'), | ||
202 | t('month') => t('Monthly'), | ||
203 | ][t($type)] ?? t('Daily'); | ||
204 | } | ||
192 | } | 205 | } |
diff --git a/application/front/controller/visitor/ErrorController.php b/application/front/controller/visitor/ErrorController.php index 10aa84c8..428e8254 100644 --- a/application/front/controller/visitor/ErrorController.php +++ b/application/front/controller/visitor/ErrorController.php | |||
@@ -26,12 +26,15 @@ class ErrorController extends ShaarliVisitorController | |||
26 | $response = $response->withStatus($throwable->getCode()); | 26 | $response = $response->withStatus($throwable->getCode()); |
27 | } else { | 27 | } else { |
28 | // Internal error (any other Throwable) | 28 | // Internal error (any other Throwable) |
29 | if ($this->container->conf->get('dev.debug', false)) { | 29 | if ($this->container->conf->get('dev.debug', false) || $this->container->loginManager->isLoggedIn()) { |
30 | $this->assignView('message', $throwable->getMessage()); | 30 | $this->assignView('message', t('Error: ') . $throwable->getMessage()); |
31 | $this->assignView( | 31 | $this->assignView( |
32 | 'stacktrace', | 32 | 'text', |
33 | nl2br(get_class($throwable) .': '. PHP_EOL . $throwable->getTraceAsString()) | 33 | '<a href="https://github.com/shaarli/Shaarli/issues/new">' |
34 | . t('Please report it on Github.') | ||
35 | . '</a>' | ||
34 | ); | 36 | ); |
37 | $this->assignView('stacktrace', exception2text($throwable)); | ||
35 | } else { | 38 | } else { |
36 | $this->assignView('message', t('An unexpected error occurred.')); | 39 | $this->assignView('message', t('An unexpected error occurred.')); |
37 | } | 40 | } |
@@ -39,7 +42,6 @@ class ErrorController extends ShaarliVisitorController | |||
39 | $response = $response->withStatus(500); | 42 | $response = $response->withStatus(500); |
40 | } | 43 | } |
41 | 44 | ||
42 | |||
43 | return $response->write($this->render('error')); | 45 | return $response->write($this->render('error')); |
44 | } | 46 | } |
45 | } | 47 | } |
diff --git a/application/front/controller/visitor/FeedController.php b/application/front/controller/visitor/FeedController.php index 8d8b546a..edc7ef43 100644 --- a/application/front/controller/visitor/FeedController.php +++ b/application/front/controller/visitor/FeedController.php | |||
@@ -27,7 +27,7 @@ class FeedController extends ShaarliVisitorController | |||
27 | 27 | ||
28 | protected function processRequest(string $feedType, Request $request, Response $response): Response | 28 | protected function processRequest(string $feedType, Request $request, Response $response): Response |
29 | { | 29 | { |
30 | $response = $response->withHeader('Content-Type', 'application/'. $feedType .'+xml; charset=utf-8'); | 30 | $response = $response->withHeader('Content-Type', 'application/' . $feedType . '+xml; charset=utf-8'); |
31 | 31 | ||
32 | $pageUrl = page_url($this->container->environment); | 32 | $pageUrl = page_url($this->container->environment); |
33 | $cache = $this->container->pageCacheManager->getCachePage($pageUrl); | 33 | $cache = $this->container->pageCacheManager->getCachePage($pageUrl); |
diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php index 7cb32777..bf965929 100644 --- a/application/front/controller/visitor/InstallController.php +++ b/application/front/controller/visitor/InstallController.php | |||
@@ -4,10 +4,10 @@ declare(strict_types=1); | |||
4 | 4 | ||
5 | namespace Shaarli\Front\Controller\Visitor; | 5 | namespace Shaarli\Front\Controller\Visitor; |
6 | 6 | ||
7 | use Shaarli\ApplicationUtils; | ||
8 | use Shaarli\Container\ShaarliContainer; | 7 | use Shaarli\Container\ShaarliContainer; |
9 | use Shaarli\Front\Exception\AlreadyInstalledException; | 8 | use Shaarli\Front\Exception\AlreadyInstalledException; |
10 | use Shaarli\Front\Exception\ResourcePermissionException; | 9 | use Shaarli\Front\Exception\ResourcePermissionException; |
10 | use Shaarli\Helper\ApplicationUtils; | ||
11 | use Shaarli\Languages; | 11 | use Shaarli\Languages; |
12 | use Shaarli\Security\SessionManager; | 12 | use Shaarli\Security\SessionManager; |
13 | use Slim\Http\Request; | 13 | use Slim\Http\Request; |
@@ -39,7 +39,8 @@ class InstallController extends ShaarliVisitorController | |||
39 | // Before installation, we'll make sure that permissions are set properly, and sessions are working. | 39 | // Before installation, we'll make sure that permissions are set properly, and sessions are working. |
40 | $this->checkPermissions(); | 40 | $this->checkPermissions(); |
41 | 41 | ||
42 | if (static::SESSION_TEST_VALUE | 42 | if ( |
43 | static::SESSION_TEST_VALUE | ||
43 | !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY) | 44 | !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY) |
44 | ) { | 45 | ) { |
45 | $this->container->sessionManager->setSessionParameter(static::SESSION_TEST_KEY, static::SESSION_TEST_VALUE); | 46 | $this->container->sessionManager->setSessionParameter(static::SESSION_TEST_KEY, static::SESSION_TEST_VALUE); |
@@ -53,6 +54,16 @@ class InstallController extends ShaarliVisitorController | |||
53 | $this->assignView('cities', $cities); | 54 | $this->assignView('cities', $cities); |
54 | $this->assignView('languages', Languages::getAvailableLanguages()); | 55 | $this->assignView('languages', Languages::getAvailableLanguages()); |
55 | 56 | ||
57 | $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION)); | ||
58 | |||
59 | $this->assignView('php_version', PHP_VERSION); | ||
60 | $this->assignView('php_eol', format_date($phpEol, false)); | ||
61 | $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable()); | ||
62 | $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement()); | ||
63 | $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf)); | ||
64 | |||
65 | $this->assignView('pagetitle', t('Install Shaarli')); | ||
66 | |||
56 | return $response->write($this->render('install')); | 67 | return $response->write($this->render('install')); |
57 | } | 68 | } |
58 | 69 | ||
@@ -65,17 +76,18 @@ class InstallController extends ShaarliVisitorController | |||
65 | // This part makes sure sessions works correctly. | 76 | // This part makes sure sessions works correctly. |
66 | // (Because on some hosts, session.save_path may not be set correctly, | 77 | // (Because on some hosts, session.save_path may not be set correctly, |
67 | // or we may not have write access to it.) | 78 | // or we may not have write access to it.) |
68 | if (static::SESSION_TEST_VALUE | 79 | if ( |
80 | static::SESSION_TEST_VALUE | ||
69 | !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY) | 81 | !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY) |
70 | ) { | 82 | ) { |
71 | // Step 2: Check if data in session is correct. | 83 | // Step 2: Check if data in session is correct. |
72 | $msg = t( | 84 | $msg = t( |
73 | '<pre>Sessions do not seem to work correctly on your server.<br>'. | 85 | '<pre>Sessions do not seem to work correctly on your server.<br>' . |
74 | 'Make sure the variable "session.save_path" is set correctly in your PHP config, '. | 86 | 'Make sure the variable "session.save_path" is set correctly in your PHP config, ' . |
75 | 'and that you have write access to it.<br>'. | 87 | 'and that you have write access to it.<br>' . |
76 | 'It currently points to %s.<br>'. | 88 | 'It currently points to %s.<br>' . |
77 | 'On some browsers, accessing your server via a hostname like \'localhost\' '. | 89 | 'On some browsers, accessing your server via a hostname like \'localhost\' ' . |
78 | 'or any custom hostname without a dot causes cookie storage to fail. '. | 90 | 'or any custom hostname without a dot causes cookie storage to fail. ' . |
79 | 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>' | 91 | 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>' |
80 | ); | 92 | ); |
81 | $msg = sprintf($msg, $this->container->sessionManager->getSavePath()); | 93 | $msg = sprintf($msg, $this->container->sessionManager->getSavePath()); |
@@ -94,7 +106,8 @@ class InstallController extends ShaarliVisitorController | |||
94 | public function save(Request $request, Response $response): Response | 106 | public function save(Request $request, Response $response): Response |
95 | { | 107 | { |
96 | $timezone = 'UTC'; | 108 | $timezone = 'UTC'; |
97 | if (!empty($request->getParam('continent')) | 109 | if ( |
110 | !empty($request->getParam('continent')) | ||
98 | && !empty($request->getParam('city')) | 111 | && !empty($request->getParam('city')) |
99 | && isTimeZoneValid($request->getParam('continent'), $request->getParam('city')) | 112 | && isTimeZoneValid($request->getParam('continent'), $request->getParam('city')) |
100 | ) { | 113 | ) { |
@@ -104,7 +117,7 @@ class InstallController extends ShaarliVisitorController | |||
104 | 117 | ||
105 | $login = $request->getParam('setlogin'); | 118 | $login = $request->getParam('setlogin'); |
106 | $this->container->conf->set('credentials.login', $login); | 119 | $this->container->conf->set('credentials.login', $login); |
107 | $salt = sha1(uniqid('', true) .'_'. mt_rand()); | 120 | $salt = sha1(uniqid('', true) . '_' . mt_rand()); |
108 | $this->container->conf->set('credentials.salt', $salt); | 121 | $this->container->conf->set('credentials.salt', $salt); |
109 | $this->container->conf->set('credentials.hash', sha1($request->getParam('setpassword') . $login . $salt)); | 122 | $this->container->conf->set('credentials.hash', sha1($request->getParam('setpassword') . $login . $salt)); |
110 | 123 | ||
@@ -113,7 +126,7 @@ class InstallController extends ShaarliVisitorController | |||
113 | } else { | 126 | } else { |
114 | $this->container->conf->set( | 127 | $this->container->conf->set( |
115 | 'general.title', | 128 | 'general.title', |
116 | 'Shared bookmarks on '.escape(index_url($this->container->environment)) | 129 | 'Shared bookmarks on ' . escape(index_url($this->container->environment)) |
117 | ); | 130 | ); |
118 | } | 131 | } |
119 | 132 | ||
@@ -150,7 +163,7 @@ class InstallController extends ShaarliVisitorController | |||
150 | protected function checkPermissions(): bool | 163 | protected function checkPermissions(): bool |
151 | { | 164 | { |
152 | // Ensure Shaarli has proper access to its resources | 165 | // Ensure Shaarli has proper access to its resources |
153 | $errors = ApplicationUtils::checkResourcePermissions($this->container->conf); | 166 | $errors = ApplicationUtils::checkResourcePermissions($this->container->conf, true); |
154 | if (empty($errors)) { | 167 | if (empty($errors)) { |
155 | return true; | 168 | return true; |
156 | } | 169 | } |
diff --git a/application/front/controller/visitor/LoginController.php b/application/front/controller/visitor/LoginController.php index 121ba40b..4b881535 100644 --- a/application/front/controller/visitor/LoginController.php +++ b/application/front/controller/visitor/LoginController.php | |||
@@ -43,7 +43,7 @@ class LoginController extends ShaarliVisitorController | |||
43 | $this | 43 | $this |
44 | ->assignView('returnurl', escape($returnUrl)) | 44 | ->assignView('returnurl', escape($returnUrl)) |
45 | ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true)) | 45 | ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true)) |
46 | ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli')) | 46 | ->assignView('pagetitle', t('Login') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')) |
47 | ; | 47 | ; |
48 | 48 | ||
49 | return $response->write($this->render(TemplatePage::LOGIN)); | 49 | return $response->write($this->render(TemplatePage::LOGIN)); |
@@ -64,8 +64,8 @@ class LoginController extends ShaarliVisitorController | |||
64 | return $this->redirect($response, '/'); | 64 | return $this->redirect($response, '/'); |
65 | } | 65 | } |
66 | 66 | ||
67 | if (!$this->container->loginManager->checkCredentials( | 67 | if ( |
68 | $this->container->environment['REMOTE_ADDR'], | 68 | !$this->container->loginManager->checkCredentials( |
69 | client_ip_id($this->container->environment), | 69 | client_ip_id($this->container->environment), |
70 | $request->getParam('login'), | 70 | $request->getParam('login'), |
71 | $request->getParam('password') | 71 | $request->getParam('password') |
@@ -102,7 +102,8 @@ class LoginController extends ShaarliVisitorController | |||
102 | */ | 102 | */ |
103 | protected function checkLoginState(): bool | 103 | protected function checkLoginState(): bool |
104 | { | 104 | { |
105 | if ($this->container->loginManager->isLoggedIn() | 105 | if ( |
106 | $this->container->loginManager->isLoggedIn() | ||
106 | || $this->container->conf->get('security.open_shaarli', false) | 107 | || $this->container->conf->get('security.open_shaarli', false) |
107 | ) { | 108 | ) { |
108 | throw new CantLoginException(); | 109 | throw new CantLoginException(); |
diff --git a/application/front/controller/visitor/PictureWallController.php b/application/front/controller/visitor/PictureWallController.php index 3c57f8dd..23553ee6 100644 --- a/application/front/controller/visitor/PictureWallController.php +++ b/application/front/controller/visitor/PictureWallController.php | |||
@@ -26,7 +26,7 @@ class PictureWallController extends ShaarliVisitorController | |||
26 | 26 | ||
27 | $this->assignView( | 27 | $this->assignView( |
28 | 'pagetitle', | 28 | 'pagetitle', |
29 | t('Picture wall') .' - '. $this->container->conf->get('general.title', 'Shaarli') | 29 | t('Picture wall') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') |
30 | ); | 30 | ); |
31 | 31 | ||
32 | // Optionally filter the results: | 32 | // Optionally filter the results: |
diff --git a/application/front/controller/visitor/ShaarliVisitorController.php b/application/front/controller/visitor/ShaarliVisitorController.php index 55c075a2..ae946c59 100644 --- a/application/front/controller/visitor/ShaarliVisitorController.php +++ b/application/front/controller/visitor/ShaarliVisitorController.php | |||
@@ -106,6 +106,7 @@ abstract class ShaarliVisitorController | |||
106 | 'target' => $template, | 106 | 'target' => $template, |
107 | 'loggedin' => $this->container->loginManager->isLoggedIn(), | 107 | 'loggedin' => $this->container->loginManager->isLoggedIn(), |
108 | 'basePath' => $this->container->basePath, | 108 | 'basePath' => $this->container->basePath, |
109 | 'rootPath' => preg_replace('#/index\.php$#', '', $this->container->basePath), | ||
109 | 'bookmarkService' => $this->container->bookmarkService | 110 | 'bookmarkService' => $this->container->bookmarkService |
110 | ]; | 111 | ]; |
111 | } | 112 | } |
@@ -143,7 +144,8 @@ abstract class ShaarliVisitorController | |||
143 | if (null !== $referer) { | 144 | if (null !== $referer) { |
144 | $currentUrl = parse_url($referer); | 145 | $currentUrl = parse_url($referer); |
145 | // If the referer is not related to Shaarli instance, redirect to default | 146 | // If the referer is not related to Shaarli instance, redirect to default |
146 | if (isset($currentUrl['host']) | 147 | if ( |
148 | isset($currentUrl['host']) | ||
147 | && strpos(index_url($this->container->environment), $currentUrl['host']) === false | 149 | && strpos(index_url($this->container->environment), $currentUrl['host']) === false |
148 | ) { | 150 | ) { |
149 | return $response->withRedirect($defaultPath); | 151 | return $response->withRedirect($defaultPath); |
@@ -172,7 +174,7 @@ abstract class ShaarliVisitorController | |||
172 | } | 174 | } |
173 | } | 175 | } |
174 | 176 | ||
175 | $queryString = count($params) > 0 ? '?'. http_build_query($params) : ''; | 177 | $queryString = count($params) > 0 ? '?' . http_build_query($params) : ''; |
176 | $anchor = $anchor ? '#' . $anchor : ''; | 178 | $anchor = $anchor ? '#' . $anchor : ''; |
177 | 179 | ||
178 | return $response->withRedirect($path . $queryString . $anchor); | 180 | return $response->withRedirect($path . $queryString . $anchor); |
diff --git a/application/front/controller/visitor/TagCloudController.php b/application/front/controller/visitor/TagCloudController.php index 76ed7690..46d62779 100644 --- a/application/front/controller/visitor/TagCloudController.php +++ b/application/front/controller/visitor/TagCloudController.php | |||
@@ -47,13 +47,14 @@ class TagCloudController extends ShaarliVisitorController | |||
47 | */ | 47 | */ |
48 | protected function processRequest(string $type, Request $request, Response $response): Response | 48 | protected function processRequest(string $type, Request $request, Response $response): Response |
49 | { | 49 | { |
50 | $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); | ||
50 | if ($this->container->loginManager->isLoggedIn() === true) { | 51 | if ($this->container->loginManager->isLoggedIn() === true) { |
51 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); | 52 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); |
52 | } | 53 | } |
53 | 54 | ||
54 | $sort = $request->getQueryParam('sort'); | 55 | $sort = $request->getQueryParam('sort'); |
55 | $searchTags = $request->getQueryParam('searchtags'); | 56 | $searchTags = $request->getQueryParam('searchtags'); |
56 | $filteringTags = $searchTags !== null ? explode(' ', $searchTags) : []; | 57 | $filteringTags = $searchTags !== null ? explode($tagsSeparator, $searchTags) : []; |
57 | 58 | ||
58 | $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); | 59 | $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); |
59 | 60 | ||
@@ -71,8 +72,9 @@ class TagCloudController extends ShaarliVisitorController | |||
71 | $tagsUrl[escape($tag)] = urlencode((string) $tag); | 72 | $tagsUrl[escape($tag)] = urlencode((string) $tag); |
72 | } | 73 | } |
73 | 74 | ||
74 | $searchTags = implode(' ', escape($filteringTags)); | 75 | $searchTags = tags_array2str($filteringTags, $tagsSeparator); |
75 | $searchTagsUrl = urlencode(implode(' ', $filteringTags)); | 76 | $searchTags = !empty($searchTags) ? trim($searchTags, $tagsSeparator) . $tagsSeparator : ''; |
77 | $searchTagsUrl = urlencode($searchTags); | ||
76 | $data = [ | 78 | $data = [ |
77 | 'search_tags' => escape($searchTags), | 79 | 'search_tags' => escape($searchTags), |
78 | 'search_tags_url' => $searchTagsUrl, | 80 | 'search_tags_url' => $searchTagsUrl, |
@@ -82,10 +84,10 @@ class TagCloudController extends ShaarliVisitorController | |||
82 | $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); | 84 | $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); |
83 | $this->assignAllView($data); | 85 | $this->assignAllView($data); |
84 | 86 | ||
85 | $searchTags = !empty($searchTags) ? $searchTags .' - ' : ''; | 87 | $searchTags = !empty($searchTags) ? trim(str_replace($tagsSeparator, ' ', $searchTags)) . ' - ' : ''; |
86 | $this->assignView( | 88 | $this->assignView( |
87 | 'pagetitle', | 89 | 'pagetitle', |
88 | $searchTags . t('Tag '. $type) .' - '. $this->container->conf->get('general.title', 'Shaarli') | 90 | $searchTags . t('Tag ' . $type) . ' - ' . $this->container->conf->get('general.title', 'Shaarli') |
89 | ); | 91 | ); |
90 | 92 | ||
91 | return $response->write($this->render('tag.' . $type)); | 93 | return $response->write($this->render('tag.' . $type)); |
diff --git a/application/front/controller/visitor/TagController.php b/application/front/controller/visitor/TagController.php index de4e7ea2..3aa58542 100644 --- a/application/front/controller/visitor/TagController.php +++ b/application/front/controller/visitor/TagController.php | |||
@@ -27,7 +27,7 @@ class TagController extends ShaarliVisitorController | |||
27 | // In case browser does not send HTTP_REFERER, we search a single tag | 27 | // In case browser does not send HTTP_REFERER, we search a single tag |
28 | if (null === $referer) { | 28 | if (null === $referer) { |
29 | if (null !== $newTag) { | 29 | if (null !== $newTag) { |
30 | return $this->redirect($response, '/?searchtags='. urlencode($newTag)); | 30 | return $this->redirect($response, '/?searchtags=' . urlencode($newTag)); |
31 | } | 31 | } |
32 | 32 | ||
33 | return $this->redirect($response, '/'); | 33 | return $this->redirect($response, '/'); |
@@ -37,7 +37,7 @@ class TagController extends ShaarliVisitorController | |||
37 | parse_str($currentUrl['query'] ?? '', $params); | 37 | parse_str($currentUrl['query'] ?? '', $params); |
38 | 38 | ||
39 | if (null === $newTag) { | 39 | if (null === $newTag) { |
40 | return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params)); | 40 | return $response->withRedirect(($currentUrl['path'] ?? './') . '?' . http_build_query($params)); |
41 | } | 41 | } |
42 | 42 | ||
43 | // Prevent redirection loop | 43 | // Prevent redirection loop |
@@ -45,9 +45,10 @@ class TagController extends ShaarliVisitorController | |||
45 | unset($params['addtag']); | 45 | unset($params['addtag']); |
46 | } | 46 | } |
47 | 47 | ||
48 | $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); | ||
48 | // Check if this tag is already in the search query and ignore it if it is. | 49 | // Check if this tag is already in the search query and ignore it if it is. |
49 | // Each tag is always separated by a space | 50 | // Each tag is always separated by a space |
50 | $currentTags = isset($params['searchtags']) ? explode(' ', $params['searchtags']) : []; | 51 | $currentTags = tags_str2array($params['searchtags'] ?? '', $tagsSeparator); |
51 | 52 | ||
52 | $addtag = true; | 53 | $addtag = true; |
53 | foreach ($currentTags as $value) { | 54 | foreach ($currentTags as $value) { |
@@ -62,12 +63,12 @@ class TagController extends ShaarliVisitorController | |||
62 | $currentTags[] = trim($newTag); | 63 | $currentTags[] = trim($newTag); |
63 | } | 64 | } |
64 | 65 | ||
65 | $params['searchtags'] = trim(implode(' ', $currentTags)); | 66 | $params['searchtags'] = tags_array2str($currentTags, $tagsSeparator); |
66 | 67 | ||
67 | // We also remove page (keeping the same page has no sense, since the results are different) | 68 | // We also remove page (keeping the same page has no sense, since the results are different) |
68 | unset($params['page']); | 69 | unset($params['page']); |
69 | 70 | ||
70 | return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params)); | 71 | return $response->withRedirect(($currentUrl['path'] ?? './') . '?' . http_build_query($params)); |
71 | } | 72 | } |
72 | 73 | ||
73 | /** | 74 | /** |
@@ -89,7 +90,7 @@ class TagController extends ShaarliVisitorController | |||
89 | parse_str($currentUrl['query'] ?? '', $params); | 90 | parse_str($currentUrl['query'] ?? '', $params); |
90 | 91 | ||
91 | if (null === $tagToRemove) { | 92 | if (null === $tagToRemove) { |
92 | return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params)); | 93 | return $response->withRedirect(($currentUrl['path'] ?? './') . '?' . http_build_query($params)); |
93 | } | 94 | } |
94 | 95 | ||
95 | // Prevent redirection loop | 96 | // Prevent redirection loop |
@@ -98,10 +99,11 @@ class TagController extends ShaarliVisitorController | |||
98 | } | 99 | } |
99 | 100 | ||
100 | if (isset($params['searchtags'])) { | 101 | if (isset($params['searchtags'])) { |
101 | $tags = explode(' ', $params['searchtags']); | 102 | $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); |
103 | $tags = tags_str2array($params['searchtags'] ?? '', $tagsSeparator); | ||
102 | // Remove value from array $tags. | 104 | // Remove value from array $tags. |
103 | $tags = array_diff($tags, [$tagToRemove]); | 105 | $tags = array_diff($tags, [$tagToRemove]); |
104 | $params['searchtags'] = implode(' ', $tags); | 106 | $params['searchtags'] = tags_array2str($tags, $tagsSeparator); |
105 | 107 | ||
106 | if (empty($params['searchtags'])) { | 108 | if (empty($params['searchtags'])) { |
107 | unset($params['searchtags']); | 109 | unset($params['searchtags']); |