From 4cf3564d28dc8e4d08a3e64f09ad045ffbde97ae Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 25 Sep 2020 13:29:36 +0200 Subject: Add a setting to retrieve bookmark metadata asynchrounously - There is a new standalone script (metadata.js) which requests a new controller to get bookmark metadata and fill the form async - This feature is enabled with the new setting: general.enable_async_metadata (enabled by default) - general.retrieve_description is now enabled by default - A small rotating loader animation has a been added to bookmark inputs when metadata is being retrieved (default template) - Custom JS htmlentities has been removed and mathiasbynens/he library is used instead Fixes #1563 --- .../controller/admin/ManageShaareController.php | 36 ++++++++-------------- .../front/controller/admin/MetadataController.php | 29 +++++++++++++++++ 2 files changed, 41 insertions(+), 24 deletions(-) create mode 100644 application/front/controller/admin/MetadataController.php (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index bb083486..df2f1631 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -53,36 +53,22 @@ class ManageShaareController extends ShaarliAdminController // If this is an HTTP(S) link, we try go get the page to extract // the title (otherwise we will to straight to the edit form.) - if (empty($title) && strpos(get_url_scheme($url) ?: '', 'http') !== false) { - $retrieveDescription = $this->container->conf->get('general.retrieve_description'); - // Short timeout to keep the application responsive - // The callback will fill $charset and $title with data from the downloaded page. - $this->container->httpAccess->getHttpResponse( - $url, - $this->container->conf->get('general.download_timeout', 30), - $this->container->conf->get('general.download_max_size', 4194304), - $this->container->httpAccess->getCurlDownloadCallback( - $charset, - $title, - $description, - $tags, - $retrieveDescription - ) - ); - if (! empty($title) && strtolower($charset) !== 'utf-8' && mb_check_encoding($charset)) { - $title = mb_convert_encoding($title, 'utf-8', $charset); - } + if (true !== $this->container->conf->get('general.enable_async_metadata', true) + && empty($title) + && strpos(get_url_scheme($url) ?: '', 'http') !== false + ) { + $metadata = $this->container->metadataRetriever->retrieve($url); } - if (empty($url) && empty($title)) { - $title = $this->container->conf->get('general.default_note_title', t('Note: ')); + if (empty($url)) { + $metadata['title'] = $this->container->conf->get('general.default_note_title', t('Note: ')); } $link = [ - 'title' => $title, + 'title' => $title ?? $metadata['title'] ?? '', 'url' => $url ?? '', - 'description' => $description ?? '', - 'tags' => $tags ?? '', + 'description' => $description ?? $metadata['description'] ?? '', + 'tags' => $tags ?? $metadata['tags'] ?? '', 'private' => $private, ]; } else { @@ -352,6 +338,8 @@ class ManageShaareController extends ShaarliAdminController 'source' => $request->getParam('source') ?? '', 'tags' => $tags, 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), + 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true), + 'retrieve_description' => $this->container->conf->get('general.retrieve_description', false), ]); $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); diff --git a/application/front/controller/admin/MetadataController.php b/application/front/controller/admin/MetadataController.php new file mode 100644 index 00000000..ff845944 --- /dev/null +++ b/application/front/controller/admin/MetadataController.php @@ -0,0 +1,29 @@ +getParam('url'); + + // Only try to extract metadata from URL with HTTP(s) scheme + if (!empty($url) && strpos(get_url_scheme($url) ?: '', 'http') !== false) { + return $response->withJson($this->container->metadataRetriever->retrieve($url)); + } + + return $response->withJson([]); + } +} -- cgit v1.2.3 From 21e72da9ee34cec56b10c83ae0c75b4bf320dfcb Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 15 Oct 2020 11:46:24 +0200 Subject: Asynchronous retrieval of bookmark's thumbnails This feature is based general.enable_async_metadata setting and works with existing metadata.js file. The script is compatible with any template: - the thumbnail div bloc must have attribute - the bookmark bloc must have attribute with the bookmark ID as value Fixes #1564 --- application/front/controller/admin/ManageShaareController.php | 3 ++- .../front/controller/visitor/BookmarkListController.php | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index df2f1631..908ebae3 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -129,7 +129,8 @@ class ManageShaareController extends ShaarliAdminController $bookmark->setTagsString($request->getParam('lf_tags')); if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE - && false === $bookmark->isNote() + && true !== $this->container->conf->get('general.enable_async_metadata', true) + && $bookmark->shouldUpdateThumbnail() ) { $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); } diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index 18368751..a8019ead 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php @@ -169,14 +169,11 @@ class BookmarkListController extends ShaarliVisitorController */ protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool { - // Logged in, thumbnails enabled, not a note, is HTTP - // and (never retrieved yet or no valid cache file) + // Logged in, not async retrieval, thumbnails enabled, and thumbnail should be updated if ($this->container->loginManager->isLoggedIn() + && true !== $this->container->conf->get('general.enable_async_metadata', true) && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE - && false !== $bookmark->getThumbnail() - && !$bookmark->isNote() - && (null === $bookmark->getThumbnail() || !is_file($bookmark->getThumbnail())) - && startsWith(strtolower($bookmark->getUrl()), 'http') + && $bookmark->shouldUpdateThumbnail() ) { $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); $this->container->bookmarkService->set($bookmark, $writeDatastore); @@ -198,6 +195,7 @@ class BookmarkListController extends ShaarliVisitorController 'page_max' => '', 'search_tags' => '', 'result_count' => '', + 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true) ]; } -- cgit v1.2.3 From b38a1b0209f546d4824a0db81a34c4e30fcdebaf Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 20 Oct 2020 11:47:07 +0200 Subject: Use PSR-3 logger for login attempts Fixes #1122 --- application/front/controller/visitor/LoginController.php | 1 - 1 file changed, 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/LoginController.php b/application/front/controller/visitor/LoginController.php index 121ba40b..f5038fe3 100644 --- a/application/front/controller/visitor/LoginController.php +++ b/application/front/controller/visitor/LoginController.php @@ -65,7 +65,6 @@ class LoginController extends ShaarliVisitorController } if (!$this->container->loginManager->checkCredentials( - $this->container->environment['REMOTE_ADDR'], client_ip_id($this->container->environment), $request->getParam('login'), $request->getParam('password') -- cgit v1.2.3 From 5c06c0870f8e425c2d4ed0f7c330c13e1605628e Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 20 Oct 2020 18:32:46 +0200 Subject: Dislay an error if an exception occurs in the error handler Related to #1598 --- application/front/controller/visitor/ErrorController.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/ErrorController.php b/application/front/controller/visitor/ErrorController.php index 10aa84c8..8da11172 100644 --- a/application/front/controller/visitor/ErrorController.php +++ b/application/front/controller/visitor/ErrorController.php @@ -28,10 +28,7 @@ class ErrorController extends ShaarliVisitorController // Internal error (any other Throwable) if ($this->container->conf->get('dev.debug', false)) { $this->assignView('message', $throwable->getMessage()); - $this->assignView( - 'stacktrace', - nl2br(get_class($throwable) .': '. PHP_EOL . $throwable->getTraceAsString()) - ); + $this->assignView('stacktrace', exception2text($throwable)); } else { $this->assignView('message', t('An unexpected error occurred.')); } -- cgit v1.2.3 From 0cf76ccb4736473a958d9fd36ed914e2d25d594a Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 21 Oct 2020 13:12:15 +0200 Subject: Feature: add a Server administration page It contains mostly read only information about the current Shaarli instance, PHP version, extensions, file and folder permissions, etc. Also action buttons to clear the cache or sync thumbnails. Part of the content of this page is also displayed on the install page, to check server requirement before installing Shaarli config file. Fixes #40 Fixes #185 --- .../front/controller/admin/ServerController.php | 87 ++++++++++++++++++++++ .../controller/visitor/BookmarkListController.php | 28 ++++--- .../front/controller/visitor/InstallController.php | 12 ++- 3 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 application/front/controller/admin/ServerController.php (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php new file mode 100644 index 00000000..85654a43 --- /dev/null +++ b/application/front/controller/admin/ServerController.php @@ -0,0 +1,87 @@ +assignView('php_version', PHP_VERSION); + $this->assignView('php_eol', format_date($phpEol, false)); + $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable()); + $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement()); + $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf)); + $this->assignView('release_url', ApplicationUtils::$GITHUB_URL . '/releases/tag/' . $latestVersion); + $this->assignView('latest_version', $latestVersion); + $this->assignView('current_version', $currentVersion); + $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode')); + $this->assignView('index_url', index_url($this->container->environment)); + $this->assignView('client_ip', client_ip_id($this->container->environment)); + $this->assignView('trusted_proxies', $this->container->conf->get('security.trusted_proxies', [])); + + $this->assignView( + 'pagetitle', + t('Server administration') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render('server')); + } + + /** + * GET /admin/clear-cache?type={$type} - Action to trigger cache folder clearing (either main or thumbnails). + */ + public function clearCache(Request $request, Response $response): Response + { + $exclude = ['.htaccess']; + + if ($request->getQueryParam('type') === static::CACHE_THUMB) { + $folders = [$this->container->conf->get('resource.thumbnails_cache')]; + + $this->saveWarningMessage( + t('Thumbnails cache has been cleared.') . ' ' . + '' . t('Please synchronize them.') .'' + ); + } else { + $folders = [ + $this->container->conf->get('resource.page_cache'), + $this->container->conf->get('resource.raintpl_tmp'), + ]; + + $this->saveSuccessMessage(t('Shaarli\'s cache folder has been cleared!')); + } + + // Make sure that we don't delete root cache folder + $folders = array_map('realpath', array_values(array_filter(array_map('trim', $folders)))); + foreach ($folders as $folder) { + FileUtils::clearFolder($folder, false, $exclude); + } + + return $this->redirect($response, '/admin/server'); + } +} diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index a8019ead..5267c8f5 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php @@ -169,16 +169,24 @@ class BookmarkListController extends ShaarliVisitorController */ protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool { - // Logged in, not async retrieval, thumbnails enabled, and thumbnail should be updated - if ($this->container->loginManager->isLoggedIn() - && true !== $this->container->conf->get('general.enable_async_metadata', true) - && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE - && $bookmark->shouldUpdateThumbnail() - ) { - $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); - $this->container->bookmarkService->set($bookmark, $writeDatastore); - - return true; + if (false === $this->container->loginManager->isLoggedIn()) { + return false; + } + + // If thumbnail should be updated, we reset it to null + if ($bookmark->shouldUpdateThumbnail()) { + $bookmark->setThumbnail(null); + + // Requires an update, not async retrieval, thumbnails enabled + if ($bookmark->shouldUpdateThumbnail() + && true !== $this->container->conf->get('general.enable_async_metadata', true) + && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE + ) { + $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); + $this->container->bookmarkService->set($bookmark, $writeDatastore); + + return true; + } } return false; diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php index 7cb32777..564a5777 100644 --- a/application/front/controller/visitor/InstallController.php +++ b/application/front/controller/visitor/InstallController.php @@ -53,6 +53,16 @@ class InstallController extends ShaarliVisitorController $this->assignView('cities', $cities); $this->assignView('languages', Languages::getAvailableLanguages()); + $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION)); + + $this->assignView('php_version', PHP_VERSION); + $this->assignView('php_eol', format_date($phpEol, false)); + $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable()); + $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement()); + $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf)); + + $this->assignView('pagetitle', t('Install Shaarli')); + return $response->write($this->render('install')); } @@ -150,7 +160,7 @@ class InstallController extends ShaarliVisitorController protected function checkPermissions(): bool { // Ensure Shaarli has proper access to its resources - $errors = ApplicationUtils::checkResourcePermissions($this->container->conf); + $errors = ApplicationUtils::checkResourcePermissions($this->container->conf, true); if (empty($errors)) { return true; } -- cgit v1.2.3 From 9c04921a8c28c18ef757f2d43ba35e7e2a7f1a4b Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 16 Oct 2020 20:17:08 +0200 Subject: Feature: Share private bookmarks using a URL containing a private key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a share link next to « Permalink » in linklist (using share icon from fork awesome) - This link generates a private key associated to the bookmark - Accessing the bookmark while logged out with the proper key will display it Fixes #475 --- .../controller/admin/ManageShaareController.php | 26 ++++++++++++++++++++++ .../controller/visitor/BookmarkListController.php | 4 +++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index 908ebae3..e490f85a 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -320,6 +320,32 @@ class ManageShaareController extends ShaarliAdminController return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); } + /** + * GET /admin/shaare/private/{hash} - Attach a private key to given bookmark, then redirect to the sharing URL. + */ + public function sharePrivate(Request $request, Response $response, array $args): Response + { + $this->checkToken($request); + + $hash = $args['hash'] ?? ''; + $bookmark = $this->container->bookmarkService->findByHash($hash); + + if ($bookmark->isPrivate() !== true) { + return $this->redirect($response, '/shaare/' . $hash); + } + + if (empty($bookmark->getAdditionalContentEntry('private_key'))) { + $privateKey = bin2hex(random_bytes(16)); + $bookmark->addAdditionalContentEntry('private_key', $privateKey); + $this->container->bookmarkService->set($bookmark); + } + + return $this->redirect( + $response, + '/shaare/' . $hash . '?key=' . $bookmark->getAdditionalContentEntry('private_key') + ); + } + /** * Helper function used to display the shaare form whether it's a new or existing bookmark. * diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index 5267c8f5..78c474c9 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php @@ -137,8 +137,10 @@ class BookmarkListController extends ShaarliVisitorController */ public function permalink(Request $request, Response $response, array $args): Response { + $privateKey = $request->getParam('key'); + try { - $bookmark = $this->container->bookmarkService->findByHash($args['hash']); + $bookmark = $this->container->bookmarkService->findByHash($args['hash'], $privateKey); } catch (BookmarkNotFoundException $e) { $this->assignView('error_message', $e->getMessage()); -- cgit v1.2.3 From c2cd15dac2bfaebe6d32f7649fbdedc07400fa08 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 16 Oct 2020 13:34:59 +0200 Subject: Move utils classes to Shaarli\Helper namespace and folder --- application/front/controller/visitor/InstallController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php index 564a5777..22329294 100644 --- a/application/front/controller/visitor/InstallController.php +++ b/application/front/controller/visitor/InstallController.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Visitor; -use Shaarli\ApplicationUtils; use Shaarli\Container\ShaarliContainer; use Shaarli\Front\Exception\AlreadyInstalledException; use Shaarli\Front\Exception\ResourcePermissionException; +use Shaarli\Helper\ApplicationUtils; use Shaarli\Languages; use Shaarli\Security\SessionManager; use Slim\Http\Request; -- cgit v1.2.3 From 36e6d88dbfd753665224664d5214f39ccfbbf6a5 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 16 Oct 2020 11:50:53 +0200 Subject: Feature: add weekly and monthly view/RSS feed for daily page - Heavy refactoring of DailyController - Add a banner like in tag cloud to display monthly and weekly links - Translations: t() now supports variables with optional first letter uppercase Fixes #160 --- .../front/controller/visitor/DailyController.php | 105 ++++++++++++--------- 1 file changed, 59 insertions(+), 46 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 07617cf1..728bc2d8 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Visitor; use DateTime; -use DateTimeImmutable; use Shaarli\Bookmark\Bookmark; +use Shaarli\Helper\DailyPageHelper; use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -26,32 +26,20 @@ class DailyController extends ShaarliVisitorController */ public function index(Request $request, Response $response): Response { - $day = $request->getQueryParam('day') ?? date('Ymd'); - - $availableDates = $this->container->bookmarkService->days(); - $nbAvailableDates = count($availableDates); - $index = array_search($day, $availableDates); - - if ($index === false) { - // no bookmarks for day, but at least one day with bookmarks - $day = $availableDates[$nbAvailableDates - 1] ?? $day; - $previousDay = $availableDates[$nbAvailableDates - 2] ?? ''; - } else { - $previousDay = $availableDates[$index - 1] ?? ''; - $nextDay = $availableDates[$index + 1] ?? ''; - } - - if ($day === date('Ymd')) { - $this->assignView('dayDesc', t('Today')); - } elseif ($day === date('Ymd', strtotime('-1 days'))) { - $this->assignView('dayDesc', t('Yesterday')); - } - - try { - $linksToDisplay = $this->container->bookmarkService->filterDay($day); - } catch (\Exception $exc) { - $linksToDisplay = []; - } + $type = DailyPageHelper::extractRequestedType($request); + $format = DailyPageHelper::getFormatByType($type); + $latestBookmark = $this->container->bookmarkService->getLatest(); + $dateTime = DailyPageHelper::extractRequestedDateTime($type, $request->getQueryParam($type), $latestBookmark); + $start = DailyPageHelper::getStartDateTimeByType($type, $dateTime); + $end = DailyPageHelper::getEndDateTimeByType($type, $dateTime); + $dailyDesc = DailyPageHelper::getDescriptionByType($type, $dateTime); + + $linksToDisplay = $this->container->bookmarkService->findByDate( + $start, + $end, + $previousDay, + $nextDay + ); $formatter = $this->container->formatterFactory->getFormatter(); $formatter->addContextData('base_path', $this->container->basePath); @@ -63,13 +51,15 @@ class DailyController extends ShaarliVisitorController $linksToDisplay[$key]['description'] = $bookmark->getDescription(); } - $dayDate = DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000'); $data = [ 'linksToDisplay' => $linksToDisplay, - 'day' => $dayDate->getTimestamp(), - 'dayDate' => $dayDate, - 'previousday' => $previousDay ?? '', - 'nextday' => $nextDay ?? '', + 'dayDate' => $start, + 'day' => $start->getTimestamp(), + 'previousday' => $previousDay ? $previousDay->format($format) : '', + 'nextday' => $nextDay ? $nextDay->format($format) : '', + 'dayDesc' => $dailyDesc, + 'type' => $type, + 'localizedType' => $this->translateType($type), ]; // Hooks are called before column construction so that plugins don't have to deal with columns. @@ -82,7 +72,7 @@ class DailyController extends ShaarliVisitorController $mainTitle = $this->container->conf->get('general.title', 'Shaarli'); $this->assignView( 'pagetitle', - t('Daily') .' - '. format_date($dayDate, false) . ' - ' . $mainTitle + $data['localizedType'] . ' - ' . $data['dayDesc'] . ' - ' . $mainTitle ); return $response->write($this->render(TemplatePage::DAILY)); @@ -106,11 +96,14 @@ class DailyController extends ShaarliVisitorController } $days = []; + $type = DailyPageHelper::extractRequestedType($request); + $format = DailyPageHelper::getFormatByType($type); + $length = DailyPageHelper::getRssLengthByType($type); foreach ($this->container->bookmarkService->search() as $bookmark) { - $day = $bookmark->getCreated()->format('Ymd'); + $day = $bookmark->getCreated()->format($format); // Stop iterating after DAILY_RSS_NB_DAYS entries - if (count($days) === static::$DAILY_RSS_NB_DAYS && !isset($days[$day])) { + if (count($days) === $length && !isset($days[$day])) { break; } @@ -127,12 +120,19 @@ class DailyController extends ShaarliVisitorController /** @var Bookmark[] $bookmarks */ foreach ($days as $day => $bookmarks) { - $dayDatetime = DateTimeImmutable::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000'); + $dayDateTime = DailyPageHelper::extractRequestedDateTime($type, (string) $day); + $endDateTime = DailyPageHelper::getEndDateTimeByType($type, $dayDateTime); + + // We only want the RSS entry to be published when the period is over. + if (new DateTime() < $endDateTime) { + continue; + } + $dataPerDay[$day] = [ - 'date' => $dayDatetime, - 'date_rss' => $dayDatetime->format(DateTime::RSS), - 'date_human' => format_date($dayDatetime, false, true), - 'absolute_url' => $indexUrl . 'daily?day=' . $day, + 'date' => $endDateTime, + 'date_rss' => $endDateTime->format(DateTime::RSS), + 'date_human' => DailyPageHelper::getDescriptionByType($type, $dayDateTime), + 'absolute_url' => $indexUrl . 'daily?'. $type .'=' . $day, 'links' => [], ]; @@ -141,16 +141,20 @@ class DailyController extends ShaarliVisitorController // Make permalink URL absolute if ($bookmark->isNote()) { - $dataPerDay[$day]['links'][$key]['url'] = $indexUrl . $bookmark->getUrl(); + $dataPerDay[$day]['links'][$key]['url'] = rtrim($indexUrl, '/') . $bookmark->getUrl(); } } } - $this->assignView('title', $this->container->conf->get('general.title', 'Shaarli')); - $this->assignView('index_url', $indexUrl); - $this->assignView('page_url', $pageUrl); - $this->assignView('hide_timestamps', $this->container->conf->get('privacy.hide_timestamps', false)); - $this->assignView('days', $dataPerDay); + $this->assignAllView([ + 'title' => $this->container->conf->get('general.title', 'Shaarli'), + 'index_url' => $indexUrl, + 'page_url' => $pageUrl, + 'hide_timestamps' => $this->container->conf->get('privacy.hide_timestamps', false), + 'days' => $dataPerDay, + 'type' => $type, + 'localizedType' => $this->translateType($type), + ]); $rssContent = $this->render(TemplatePage::DAILY_RSS); @@ -189,4 +193,13 @@ class DailyController extends ShaarliVisitorController return $columns; } + + protected function translateType($type): string + { + return [ + t('day') => t('Daily'), + t('week') => t('Weekly'), + t('month') => t('Monthly'), + ][t($type)] ?? t('Daily'); + } } -- cgit v1.2.3 From 54afb1d6f65f727b20b66582bb63a42c421eea4d Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 27 Oct 2020 19:55:29 +0100 Subject: Fix rebase issue --- application/front/controller/admin/ServerController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php index 85654a43..bfc99422 100644 --- a/application/front/controller/admin/ServerController.php +++ b/application/front/controller/admin/ServerController.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; -use Shaarli\ApplicationUtils; -use Shaarli\FileUtils; +use Shaarli\Helper\ApplicationUtils; +use Shaarli\Helper\FileUtils; use Slim\Http\Request; use Slim\Http\Response; -- cgit v1.2.3 From 5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 10 Oct 2020 17:40:26 +0200 Subject: Feature: bulk creation of bookmarks This changes creates a new form in addlink page allowing to create multiple bookmarks at once more easily. It focuses on re-using as much existing code and template component as possible. These changes includes: - a new form in addlink (hidden behind a button by default), containing a text area for URL, and tags/private status to apply to created links - this form displays a new template called editlink.batch, itself including editlink template multiple times - User interation in this new templates are handle by a new JS script (shaare-batch.js) making AJAX requests, and therefore does not need page reloading - ManageShaareController has been split into 3 distinct controllers: + ShaareAdd: displays addlink template + ShaareManage: various operation applied on existing shaares (change visibility, pin, deletion, etc.) + ShaarePublish: handles creation/edit forms and saving Shaare's form - Updated translations Fixes #137 --- .../controller/admin/ManageShaareController.php | 386 --------------------- .../front/controller/admin/ShaareAddController.php | 34 ++ .../controller/admin/ShaareManageController.php | 202 +++++++++++ .../controller/admin/ShaarePublishController.php | 222 ++++++++++++ 4 files changed, 458 insertions(+), 386 deletions(-) delete mode 100644 application/front/controller/admin/ManageShaareController.php create mode 100644 application/front/controller/admin/ShaareAddController.php create mode 100644 application/front/controller/admin/ShaareManageController.php create mode 100644 application/front/controller/admin/ShaarePublishController.php (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php deleted file mode 100644 index e490f85a..00000000 --- a/application/front/controller/admin/ManageShaareController.php +++ /dev/null @@ -1,386 +0,0 @@ -assignView( - 'pagetitle', - t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') - ); - - return $response->write($this->render(TemplatePage::ADDLINK)); - } - - /** - * GET /admin/shaare - Displays the bookmark form for creation. - * Note that if the URL is found in existing bookmarks, then it will be in edit mode. - */ - public function displayCreateForm(Request $request, Response $response): Response - { - $url = cleanup_url($request->getParam('post')); - - $linkIsNew = false; - // Check if URL is not already in database (in this case, we will edit the existing link) - $bookmark = $this->container->bookmarkService->findByUrl($url); - if (null === $bookmark) { - $linkIsNew = true; - // Get shaare data if it was provided in URL (e.g.: by the bookmarklet). - $title = $request->getParam('title'); - $description = $request->getParam('description'); - $tags = $request->getParam('tags'); - $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); - - // If this is an HTTP(S) link, we try go get the page to extract - // the title (otherwise we will to straight to the edit form.) - if (true !== $this->container->conf->get('general.enable_async_metadata', true) - && empty($title) - && strpos(get_url_scheme($url) ?: '', 'http') !== false - ) { - $metadata = $this->container->metadataRetriever->retrieve($url); - } - - if (empty($url)) { - $metadata['title'] = $this->container->conf->get('general.default_note_title', t('Note: ')); - } - - $link = [ - 'title' => $title ?? $metadata['title'] ?? '', - 'url' => $url ?? '', - 'description' => $description ?? $metadata['description'] ?? '', - 'tags' => $tags ?? $metadata['tags'] ?? '', - 'private' => $private, - ]; - } else { - $formatter = $this->container->formatterFactory->getFormatter('raw'); - $link = $formatter->format($bookmark); - } - - return $this->displayForm($link, $linkIsNew, $request, $response); - } - - /** - * GET /admin/shaare/{id} - Displays the bookmark form in edition mode. - */ - public function displayEditForm(Request $request, Response $response, array $args): Response - { - $id = $args['id'] ?? ''; - try { - if (false === ctype_digit($id)) { - throw new BookmarkNotFoundException(); - } - $bookmark = $this->container->bookmarkService->get((int) $id); // Read database - } catch (BookmarkNotFoundException $e) { - $this->saveErrorMessage(sprintf( - t('Bookmark with identifier %s could not be found.'), - $id - )); - - return $this->redirect($response, '/'); - } - - $formatter = $this->container->formatterFactory->getFormatter('raw'); - $link = $formatter->format($bookmark); - - return $this->displayForm($link, false, $request, $response); - } - - /** - * POST /admin/shaare - */ - public function save(Request $request, Response $response): Response - { - $this->checkToken($request); - - // lf_id should only be present if the link exists. - $id = $request->getParam('lf_id') !== null ? intval(escape($request->getParam('lf_id'))) : null; - if (null !== $id && true === $this->container->bookmarkService->exists($id)) { - // Edit - $bookmark = $this->container->bookmarkService->get($id); - } else { - // New link - $bookmark = new Bookmark(); - } - - $bookmark->setTitle($request->getParam('lf_title')); - $bookmark->setDescription($request->getParam('lf_description')); - $bookmark->setUrl($request->getParam('lf_url'), $this->container->conf->get('security.allowed_protocols', [])); - $bookmark->setPrivate(filter_var($request->getParam('lf_private'), FILTER_VALIDATE_BOOLEAN)); - $bookmark->setTagsString($request->getParam('lf_tags')); - - if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE - && true !== $this->container->conf->get('general.enable_async_metadata', true) - && $bookmark->shouldUpdateThumbnail() - ) { - $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); - } - $this->container->bookmarkService->addOrSet($bookmark, false); - - // To preserve backward compatibility with 3rd parties, plugins still use arrays - $formatter = $this->container->formatterFactory->getFormatter('raw'); - $data = $formatter->format($bookmark); - $this->executePageHooks('save_link', $data); - - $bookmark->fromArray($data); - $this->container->bookmarkService->set($bookmark); - - // If we are called from the bookmarklet, we must close the popup: - if ($request->getParam('source') === 'bookmarklet') { - return $response->write(''); - } - - if (!empty($request->getParam('returnurl'))) { - $this->container->environment['HTTP_REFERER'] = escape($request->getParam('returnurl')); - } - - return $this->redirectFromReferer( - $request, - $response, - ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'], - $bookmark->getShortUrl() - ); - } - - /** - * GET /admin/shaare/delete - Delete one or multiple bookmarks (depending on `id` query parameter). - */ - public function deleteBookmark(Request $request, Response $response): Response - { - $this->checkToken($request); - - $ids = escape(trim($request->getParam('id') ?? '')); - if (empty($ids) || strpos($ids, ' ') !== false) { - // multiple, space-separated ids provided - $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'ctype_digit')); - } else { - $ids = [$ids]; - } - - // assert at least one id is given - if (0 === count($ids)) { - $this->saveErrorMessage(t('Invalid bookmark ID provided.')); - - return $this->redirectFromReferer($request, $response, [], ['delete-shaare']); - } - - $formatter = $this->container->formatterFactory->getFormatter('raw'); - $count = 0; - foreach ($ids as $id) { - try { - $bookmark = $this->container->bookmarkService->get((int) $id); - } catch (BookmarkNotFoundException $e) { - $this->saveErrorMessage(sprintf( - t('Bookmark with identifier %s could not be found.'), - $id - )); - - continue; - } - - $data = $formatter->format($bookmark); - $this->executePageHooks('delete_link', $data); - $this->container->bookmarkService->remove($bookmark, false); - ++ $count; - } - - if ($count > 0) { - $this->container->bookmarkService->save(); - } - - // If we are called from the bookmarklet, we must close the popup: - if ($request->getParam('source') === 'bookmarklet') { - return $response->write(''); - } - - // Don't redirect to where we were previously because the datastore has changed. - return $this->redirect($response, '/'); - } - - /** - * GET /admin/shaare/visibility - * - * Change visibility (public/private) of one or multiple bookmarks (depending on `id` query parameter). - */ - public function changeVisibility(Request $request, Response $response): Response - { - $this->checkToken($request); - - $ids = trim(escape($request->getParam('id') ?? '')); - if (empty($ids) || strpos($ids, ' ') !== false) { - // multiple, space-separated ids provided - $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'ctype_digit')); - } else { - // only a single id provided - $ids = [$ids]; - } - - // assert at least one id is given - if (0 === count($ids)) { - $this->saveErrorMessage(t('Invalid bookmark ID provided.')); - - return $this->redirectFromReferer($request, $response, [], ['change_visibility']); - } - - // assert that the visibility is valid - $visibility = $request->getParam('newVisibility'); - if (null === $visibility || false === in_array($visibility, ['public', 'private'], true)) { - $this->saveErrorMessage(t('Invalid visibility provided.')); - - return $this->redirectFromReferer($request, $response, [], ['change_visibility']); - } else { - $isPrivate = $visibility === 'private'; - } - - $formatter = $this->container->formatterFactory->getFormatter('raw'); - $count = 0; - - foreach ($ids as $id) { - try { - $bookmark = $this->container->bookmarkService->get((int) $id); - } catch (BookmarkNotFoundException $e) { - $this->saveErrorMessage(sprintf( - t('Bookmark with identifier %s could not be found.'), - $id - )); - - continue; - } - - $bookmark->setPrivate($isPrivate); - - // To preserve backward compatibility with 3rd parties, plugins still use arrays - $data = $formatter->format($bookmark); - $this->executePageHooks('save_link', $data); - $bookmark->fromArray($data); - - $this->container->bookmarkService->set($bookmark, false); - ++$count; - } - - if ($count > 0) { - $this->container->bookmarkService->save(); - } - - return $this->redirectFromReferer($request, $response, ['/visibility'], ['change_visibility']); - } - - /** - * GET /admin/shaare/{id}/pin - Pin or unpin a bookmark. - */ - public function pinBookmark(Request $request, Response $response, array $args): Response - { - $this->checkToken($request); - - $id = $args['id'] ?? ''; - try { - if (false === ctype_digit($id)) { - throw new BookmarkNotFoundException(); - } - $bookmark = $this->container->bookmarkService->get((int) $id); // Read database - } catch (BookmarkNotFoundException $e) { - $this->saveErrorMessage(sprintf( - t('Bookmark with identifier %s could not be found.'), - $id - )); - - return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); - } - - $formatter = $this->container->formatterFactory->getFormatter('raw'); - - $bookmark->setSticky(!$bookmark->isSticky()); - - // To preserve backward compatibility with 3rd parties, plugins still use arrays - $data = $formatter->format($bookmark); - $this->executePageHooks('save_link', $data); - $bookmark->fromArray($data); - - $this->container->bookmarkService->set($bookmark); - - return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); - } - - /** - * GET /admin/shaare/private/{hash} - Attach a private key to given bookmark, then redirect to the sharing URL. - */ - public function sharePrivate(Request $request, Response $response, array $args): Response - { - $this->checkToken($request); - - $hash = $args['hash'] ?? ''; - $bookmark = $this->container->bookmarkService->findByHash($hash); - - if ($bookmark->isPrivate() !== true) { - return $this->redirect($response, '/shaare/' . $hash); - } - - if (empty($bookmark->getAdditionalContentEntry('private_key'))) { - $privateKey = bin2hex(random_bytes(16)); - $bookmark->addAdditionalContentEntry('private_key', $privateKey); - $this->container->bookmarkService->set($bookmark); - } - - return $this->redirect( - $response, - '/shaare/' . $hash . '?key=' . $bookmark->getAdditionalContentEntry('private_key') - ); - } - - /** - * Helper function used to display the shaare form whether it's a new or existing bookmark. - * - * @param array $link data used in template, either from parameters or from the data store - */ - protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response - { - $tags = $this->container->bookmarkService->bookmarksCountPerTag(); - if ($this->container->conf->get('formatter') === 'markdown') { - $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; - } - - $data = escape([ - 'link' => $link, - 'link_is_new' => $isNew, - 'http_referer' => $this->container->environment['HTTP_REFERER'] ?? '', - 'source' => $request->getParam('source') ?? '', - 'tags' => $tags, - 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), - 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true), - 'retrieve_description' => $this->container->conf->get('general.retrieve_description', false), - ]); - - $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); - - foreach ($data as $key => $value) { - $this->assignView($key, $value); - } - - $editLabel = false === $isNew ? t('Edit') .' ' : ''; - $this->assignView( - 'pagetitle', - $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') - ); - - return $response->write($this->render(TemplatePage::EDIT_LINK)); - } -} diff --git a/application/front/controller/admin/ShaareAddController.php b/application/front/controller/admin/ShaareAddController.php new file mode 100644 index 00000000..8dc386b2 --- /dev/null +++ b/application/front/controller/admin/ShaareAddController.php @@ -0,0 +1,34 @@ +container->bookmarkService->bookmarksCountPerTag(); + if ($this->container->conf->get('formatter') === 'markdown') { + $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; + } + + $this->assignView( + 'pagetitle', + t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + $this->assignView('tags', $tags); + $this->assignView('default_private_links', $this->container->conf->get('privacy.default_private_links', false)); + $this->assignView('async_metadata', $this->container->conf->get('general.enable_async_metadata', true)); + + return $response->write($this->render(TemplatePage::ADDLINK)); + } +} diff --git a/application/front/controller/admin/ShaareManageController.php b/application/front/controller/admin/ShaareManageController.php new file mode 100644 index 00000000..7ceb8d8a --- /dev/null +++ b/application/front/controller/admin/ShaareManageController.php @@ -0,0 +1,202 @@ +checkToken($request); + + $ids = escape(trim($request->getParam('id') ?? '')); + if (empty($ids) || strpos($ids, ' ') !== false) { + // multiple, space-separated ids provided + $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'ctype_digit')); + } else { + $ids = [$ids]; + } + + // assert at least one id is given + if (0 === count($ids)) { + $this->saveErrorMessage(t('Invalid bookmark ID provided.')); + + return $this->redirectFromReferer($request, $response, [], ['delete-shaare']); + } + + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $count = 0; + foreach ($ids as $id) { + try { + $bookmark = $this->container->bookmarkService->get((int) $id); + } catch (BookmarkNotFoundException $e) { + $this->saveErrorMessage(sprintf( + t('Bookmark with identifier %s could not be found.'), + $id + )); + + continue; + } + + $data = $formatter->format($bookmark); + $this->executePageHooks('delete_link', $data); + $this->container->bookmarkService->remove($bookmark, false); + ++ $count; + } + + if ($count > 0) { + $this->container->bookmarkService->save(); + } + + // If we are called from the bookmarklet, we must close the popup: + if ($request->getParam('source') === 'bookmarklet') { + return $response->write(''); + } + + // Don't redirect to where we were previously because the datastore has changed. + return $this->redirect($response, '/'); + } + + /** + * GET /admin/shaare/visibility + * + * Change visibility (public/private) of one or multiple bookmarks (depending on `id` query parameter). + */ + public function changeVisibility(Request $request, Response $response): Response + { + $this->checkToken($request); + + $ids = trim(escape($request->getParam('id') ?? '')); + if (empty($ids) || strpos($ids, ' ') !== false) { + // multiple, space-separated ids provided + $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'ctype_digit')); + } else { + // only a single id provided + $ids = [$ids]; + } + + // assert at least one id is given + if (0 === count($ids)) { + $this->saveErrorMessage(t('Invalid bookmark ID provided.')); + + return $this->redirectFromReferer($request, $response, [], ['change_visibility']); + } + + // assert that the visibility is valid + $visibility = $request->getParam('newVisibility'); + if (null === $visibility || false === in_array($visibility, ['public', 'private'], true)) { + $this->saveErrorMessage(t('Invalid visibility provided.')); + + return $this->redirectFromReferer($request, $response, [], ['change_visibility']); + } else { + $isPrivate = $visibility === 'private'; + } + + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $count = 0; + + foreach ($ids as $id) { + try { + $bookmark = $this->container->bookmarkService->get((int) $id); + } catch (BookmarkNotFoundException $e) { + $this->saveErrorMessage(sprintf( + t('Bookmark with identifier %s could not be found.'), + $id + )); + + continue; + } + + $bookmark->setPrivate($isPrivate); + + // To preserve backward compatibility with 3rd parties, plugins still use arrays + $data = $formatter->format($bookmark); + $this->executePageHooks('save_link', $data); + $bookmark->fromArray($data); + + $this->container->bookmarkService->set($bookmark, false); + ++$count; + } + + if ($count > 0) { + $this->container->bookmarkService->save(); + } + + return $this->redirectFromReferer($request, $response, ['/visibility'], ['change_visibility']); + } + + /** + * GET /admin/shaare/{id}/pin - Pin or unpin a bookmark. + */ + public function pinBookmark(Request $request, Response $response, array $args): Response + { + $this->checkToken($request); + + $id = $args['id'] ?? ''; + try { + if (false === ctype_digit($id)) { + throw new BookmarkNotFoundException(); + } + $bookmark = $this->container->bookmarkService->get((int) $id); // Read database + } catch (BookmarkNotFoundException $e) { + $this->saveErrorMessage(sprintf( + t('Bookmark with identifier %s could not be found.'), + $id + )); + + return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); + } + + $formatter = $this->container->formatterFactory->getFormatter('raw'); + + $bookmark->setSticky(!$bookmark->isSticky()); + + // To preserve backward compatibility with 3rd parties, plugins still use arrays + $data = $formatter->format($bookmark); + $this->executePageHooks('save_link', $data); + $bookmark->fromArray($data); + + $this->container->bookmarkService->set($bookmark); + + return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); + } + + /** + * GET /admin/shaare/private/{hash} - Attach a private key to given bookmark, then redirect to the sharing URL. + */ + public function sharePrivate(Request $request, Response $response, array $args): Response + { + $this->checkToken($request); + + $hash = $args['hash'] ?? ''; + $bookmark = $this->container->bookmarkService->findByHash($hash); + + if ($bookmark->isPrivate() !== true) { + return $this->redirect($response, '/shaare/' . $hash); + } + + if (empty($bookmark->getAdditionalContentEntry('private_key'))) { + $privateKey = bin2hex(random_bytes(16)); + $bookmark->addAdditionalContentEntry('private_key', $privateKey); + $this->container->bookmarkService->set($bookmark); + } + + return $this->redirect( + $response, + '/shaare/' . $hash . '?key=' . $bookmark->getAdditionalContentEntry('private_key') + ); + } +} diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php new file mode 100644 index 00000000..608f79cf --- /dev/null +++ b/application/front/controller/admin/ShaarePublishController.php @@ -0,0 +1,222 @@ +getParam('post')); + $link = $this->buildLinkDataFromUrl($request, $url); + + return $this->displayForm($link, $link['linkIsNew'], $request, $response); + } + + /** + * POST /admin/shaare-batch - Displays multiple creation/edit forms from bulk add in add-link page. + */ + public function displayCreateBatchForms(Request $request, Response $response): Response + { + $urls = array_map('cleanup_url', explode(PHP_EOL, $request->getParam('urls'))); + + $links = []; + foreach ($urls as $url) { + $link = $this->buildLinkDataFromUrl($request, $url); + $data = $this->buildFormData($link, $link['linkIsNew'], $request); + $data['token'] = $this->container->sessionManager->generateToken(); + $data['source'] = 'batch'; + + $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); + + $links[] = $data; + } + + $this->assignView('links', $links); + $this->assignView('batch_mode', true); + $this->assignView('async_metadata', $this->container->conf->get('general.enable_async_metadata', true)); + + return $response->write($this->render(TemplatePage::EDIT_LINK_BATCH)); + } + + /** + * GET /admin/shaare/{id} - Displays the bookmark form in edition mode. + */ + public function displayEditForm(Request $request, Response $response, array $args): Response + { + $id = $args['id'] ?? ''; + try { + if (false === ctype_digit($id)) { + throw new BookmarkNotFoundException(); + } + $bookmark = $this->container->bookmarkService->get((int) $id); // Read database + } catch (BookmarkNotFoundException $e) { + $this->saveErrorMessage(sprintf( + t('Bookmark with identifier %s could not be found.'), + $id + )); + + return $this->redirect($response, '/'); + } + + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $link = $formatter->format($bookmark); + + return $this->displayForm($link, false, $request, $response); + } + + /** + * POST /admin/shaare + */ + public function save(Request $request, Response $response): Response + { + $this->checkToken($request); + + // lf_id should only be present if the link exists. + $id = $request->getParam('lf_id') !== null ? intval(escape($request->getParam('lf_id'))) : null; + if (null !== $id && true === $this->container->bookmarkService->exists($id)) { + // Edit + $bookmark = $this->container->bookmarkService->get($id); + } else { + // New link + $bookmark = new Bookmark(); + } + + $bookmark->setTitle($request->getParam('lf_title')); + $bookmark->setDescription($request->getParam('lf_description')); + $bookmark->setUrl($request->getParam('lf_url'), $this->container->conf->get('security.allowed_protocols', [])); + $bookmark->setPrivate(filter_var($request->getParam('lf_private'), FILTER_VALIDATE_BOOLEAN)); + $bookmark->setTagsString($request->getParam('lf_tags')); + + if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE + && true !== $this->container->conf->get('general.enable_async_metadata', true) + && $bookmark->shouldUpdateThumbnail() + ) { + $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); + } + $this->container->bookmarkService->addOrSet($bookmark, false); + + // To preserve backward compatibility with 3rd parties, plugins still use arrays + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $data = $formatter->format($bookmark); + $this->executePageHooks('save_link', $data); + + $bookmark->fromArray($data); + $this->container->bookmarkService->set($bookmark); + + // If we are called from the bookmarklet, we must close the popup: + if ($request->getParam('source') === 'bookmarklet') { + return $response->write(''); + } elseif ($request->getParam('source') === 'batch') { + return $response; + } + + if (!empty($request->getParam('returnurl'))) { + $this->container->environment['HTTP_REFERER'] = escape($request->getParam('returnurl')); + } + + return $this->redirectFromReferer( + $request, + $response, + ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'], + $bookmark->getShortUrl() + ); + } + + /** + * Helper function used to display the shaare form whether it's a new or existing bookmark. + * + * @param array $link data used in template, either from parameters or from the data store + */ + protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response + { + $data = $this->buildFormData($link, $isNew, $request); + + $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); + + foreach ($data as $key => $value) { + $this->assignView($key, $value); + } + + $editLabel = false === $isNew ? t('Edit') .' ' : ''; + $this->assignView( + 'pagetitle', + $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render(TemplatePage::EDIT_LINK)); + } + + protected function buildLinkDataFromUrl(Request $request, string $url): array + { + // Check if URL is not already in database (in this case, we will edit the existing link) + $bookmark = $this->container->bookmarkService->findByUrl($url); + if (null === $bookmark) { + // Get shaare data if it was provided in URL (e.g.: by the bookmarklet). + $title = $request->getParam('title'); + $description = $request->getParam('description'); + $tags = $request->getParam('tags'); + $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); + + // If this is an HTTP(S) link, we try go get the page to extract + // the title (otherwise we will to straight to the edit form.) + if (true !== $this->container->conf->get('general.enable_async_metadata', true) + && empty($title) + && strpos(get_url_scheme($url) ?: '', 'http') !== false + ) { + $metadata = $this->container->metadataRetriever->retrieve($url); + } + + if (empty($url)) { + $metadata['title'] = $this->container->conf->get('general.default_note_title', t('Note: ')); + } + + return [ + 'title' => $title ?? $metadata['title'] ?? '', + 'url' => $url ?? '', + 'description' => $description ?? $metadata['description'] ?? '', + 'tags' => $tags ?? $metadata['tags'] ?? '', + 'private' => $private, + 'linkIsNew' => true, + ]; + } + + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $link = $formatter->format($bookmark); + $link['linkIsNew'] = false; + + return $link; + } + + protected function buildFormData(array $link, bool $isNew, Request $request): array + { + $tags = $this->container->bookmarkService->bookmarksCountPerTag(); + if ($this->container->conf->get('formatter') === 'markdown') { + $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; + } + + return escape([ + 'link' => $link, + 'link_is_new' => $isNew, + 'http_referer' => $this->container->environment['HTTP_REFERER'] ?? '', + 'source' => $request->getParam('source') ?? '', + 'tags' => $tags, + 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), + 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true), + 'retrieve_description' => $this->container->conf->get('general.retrieve_description', false), + ]); + } +} -- cgit v1.2.3 From 25e90d8d75382721ff7473fa1686090fcfeb46ff Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 11 Oct 2020 13:34:38 +0200 Subject: Bulk creation: fix private status based on the first form --- application/front/controller/admin/ShaarePublishController.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index 608f79cf..fd680ea0 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -169,7 +169,11 @@ class ShaarePublishController extends ShaarliAdminController $title = $request->getParam('title'); $description = $request->getParam('description'); $tags = $request->getParam('tags'); - $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); + if ($request->getParam('private') !== null) { + $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); + } else { + $private = $this->container->conf->get('privacy.default_private_links', false); + } // If this is an HTTP(S) link, we try go get the page to extract // the title (otherwise we will to straight to the edit form.) -- cgit v1.2.3 From c609944cb906a2f5002cd86a808aa36d8deb2afd Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 23 Oct 2020 12:29:52 +0200 Subject: Bulk creation: improve performances using memoization Reduced additional processing time per links from ~40ms to ~5ms --- .../controller/admin/ShaarePublishController.php | 52 ++++++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index fd680ea0..65fdcdee 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Bookmark\Bookmark; use Shaarli\Bookmark\Exception\BookmarkNotFoundException; +use Shaarli\Formatter\BookmarkFormatter; use Shaarli\Formatter\BookmarkMarkdownFormatter; use Shaarli\Render\TemplatePage; use Shaarli\Thumbnailer; @@ -14,6 +15,16 @@ use Slim\Http\Response; class ShaarePublishController extends ShaarliAdminController { + /** + * @var BookmarkFormatter[] Statically cached instances of formatters + */ + protected $formatters = []; + + /** + * @var array Statically cached bookmark's tags counts + */ + protected $tags; + /** * GET /admin/shaare - Displays the bookmark form for creation. * Note that if the URL is found in existing bookmarks, then it will be in edit mode. @@ -72,7 +83,7 @@ class ShaarePublishController extends ShaarliAdminController return $this->redirect($response, '/'); } - $formatter = $this->container->formatterFactory->getFormatter('raw'); + $formatter = $this->getFormatter('raw'); $link = $formatter->format($bookmark); return $this->displayForm($link, false, $request, $response); @@ -110,7 +121,7 @@ class ShaarePublishController extends ShaarliAdminController $this->container->bookmarkService->addOrSet($bookmark, false); // To preserve backward compatibility with 3rd parties, plugins still use arrays - $formatter = $this->container->formatterFactory->getFormatter('raw'); + $formatter = $this->getFormatter('raw'); $data = $formatter->format($bookmark); $this->executePageHooks('save_link', $data); @@ -198,7 +209,7 @@ class ShaarePublishController extends ShaarliAdminController ]; } - $formatter = $this->container->formatterFactory->getFormatter('raw'); + $formatter = $this->getFormatter('raw'); $link = $formatter->format($bookmark); $link['linkIsNew'] = false; @@ -207,20 +218,43 @@ class ShaarePublishController extends ShaarliAdminController protected function buildFormData(array $link, bool $isNew, Request $request): array { - $tags = $this->container->bookmarkService->bookmarksCountPerTag(); - if ($this->container->conf->get('formatter') === 'markdown') { - $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; - } - return escape([ 'link' => $link, 'link_is_new' => $isNew, 'http_referer' => $this->container->environment['HTTP_REFERER'] ?? '', 'source' => $request->getParam('source') ?? '', - 'tags' => $tags, + 'tags' => $this->getTags(), 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true), 'retrieve_description' => $this->container->conf->get('general.retrieve_description', false), ]); } + + /** + * Memoize formatterFactory->getFormatter() calls. + */ + protected function getFormatter(string $type): BookmarkFormatter + { + if (!array_key_exists($type, $this->formatters) || $this->formatters[$type] === null) { + $this->formatters[$type] = $this->container->formatterFactory->getFormatter($type); + } + + return $this->formatters[$type]; + } + + /** + * Memoize bookmarkService->bookmarksCountPerTag() calls. + */ + protected function getTags(): array + { + if ($this->tags === null) { + $this->tags = $this->container->bookmarkService->bookmarksCountPerTag(); + + if ($this->container->conf->get('formatter') === 'markdown') { + $this->tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; + } + } + + return $this->tags; + } } -- cgit v1.2.3 From 34c8f558e595d4f90e46e3753c8455b0b515771a Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 23 Oct 2020 13:28:02 +0200 Subject: Bulk creation: ignore blank lines --- application/front/controller/admin/ShaarePublishController.php | 3 +++ 1 file changed, 3 insertions(+) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index 65fdcdee..ddcffdc7 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -46,6 +46,9 @@ class ShaarePublishController extends ShaarliAdminController $links = []; foreach ($urls as $url) { + if (empty($url)) { + continue; + } $link = $this->buildLinkDataFromUrl($request, $url); $data = $this->buildFormData($link, $link['linkIsNew'], $request); $data['token'] = $this->container->sessionManager->generateToken(); -- cgit v1.2.3 From 114a43b20e9a1f83647d4f0f7a001e80a76c75ce Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 28 Oct 2020 14:13:50 +0100 Subject: Remove unnecessary escape of referer Fixes #1611 --- application/front/controller/admin/ShaarePublishController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index ddcffdc7..18afc2d1 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -139,7 +139,7 @@ class ShaarePublishController extends ShaarliAdminController } if (!empty($request->getParam('returnurl'))) { - $this->container->environment['HTTP_REFERER'] = escape($request->getParam('returnurl')); + $this->container->environment['HTTP_REFERER'] = $request->getParam('returnurl'); } return $this->redirectFromReferer( -- cgit v1.2.3 From 330ac859fb13a3a15875f185a611bfaa6c5f5587 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 16:14:22 +0100 Subject: Fix: redirect to referrer after bookmark deletion Except if the referer points to a permalink (which has been deleted). Fixes #1622 --- application/front/controller/admin/ShaareManageController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ShaareManageController.php b/application/front/controller/admin/ShaareManageController.php index 7ceb8d8a..2ed298f5 100644 --- a/application/front/controller/admin/ShaareManageController.php +++ b/application/front/controller/admin/ShaareManageController.php @@ -66,8 +66,8 @@ class ShaareManageController extends ShaarliAdminController return $response->write(''); } - // Don't redirect to where we were previously because the datastore has changed. - return $this->redirect($response, '/'); + // Don't redirect to permalink after deletion. + return $this->redirectFromReferer($request, $response, ['shaare/']); } /** -- cgit v1.2.3 From b3bd8c3e8d367975980043e772f7cd78b7f96bc6 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 22 Oct 2020 16:21:03 +0200 Subject: Feature: support any tag separator So it allows to have multiple words tags. Breaking change: commas ',' are no longer a default separator. Fixes #594 --- .../front/controller/admin/ManageTagController.php | 33 ++++++++++++++++++++++ .../controller/admin/ShaareManageController.php | 4 +-- .../controller/admin/ShaarePublishController.php | 12 ++++++-- .../controller/visitor/BookmarkListController.php | 11 ++++++-- .../controller/visitor/TagCloudController.php | 10 ++++--- .../front/controller/visitor/TagController.php | 10 ++++--- 6 files changed, 65 insertions(+), 15 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php index 2065c3e2..22fb461c 100644 --- a/application/front/controller/admin/ManageTagController.php +++ b/application/front/controller/admin/ManageTagController.php @@ -24,6 +24,12 @@ class ManageTagController extends ShaarliAdminController $fromTag = $request->getParam('fromtag') ?? ''; $this->assignView('fromtag', escape($fromTag)); + $separator = escape($this->container->conf->get('general.tags_separator', ' ')); + if ($separator === ' ') { + $separator = ' '; + $this->assignView('tags_separator_desc', t('whitespace')); + } + $this->assignView('tags_separator', $separator); $this->assignView( 'pagetitle', t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli') @@ -85,4 +91,31 @@ class ManageTagController extends ShaarliAdminController return $this->redirect($response, $redirect); } + + /** + * POST /admin/tags/change-separator - Change tag separator + */ + public function changeSeparator(Request $request, Response $response): Response + { + $this->checkToken($request); + + $reservedCharacters = ['-', '.', '*']; + $newSeparator = $request->getParam('separator'); + if ($newSeparator === null || mb_strlen($newSeparator) !== 1) { + $this->saveErrorMessage(t('Tags separator must be a single character.')); + } elseif (in_array($newSeparator, $reservedCharacters, true)) { + $reservedCharacters = implode(' ', array_map(function (string $character) { + return '' . $character . ''; + }, $reservedCharacters)); + $this->saveErrorMessage( + t('These characters are reserved and can\'t be used as tags separator: ') . $reservedCharacters + ); + } else { + $this->container->conf->set('general.tags_separator', $newSeparator, true, true); + + $this->saveSuccessMessage('Your tags separator setting has been updated!'); + } + + return $this->redirect($response, '/admin/tags'); + } } diff --git a/application/front/controller/admin/ShaareManageController.php b/application/front/controller/admin/ShaareManageController.php index 2ed298f5..0b143172 100644 --- a/application/front/controller/admin/ShaareManageController.php +++ b/application/front/controller/admin/ShaareManageController.php @@ -125,7 +125,7 @@ class ShaareManageController extends ShaarliAdminController // To preserve backward compatibility with 3rd parties, plugins still use arrays $data = $formatter->format($bookmark); $this->executePageHooks('save_link', $data); - $bookmark->fromArray($data); + $bookmark->fromArray($data, $this->container->conf->get('general.tags_separator', ' ')); $this->container->bookmarkService->set($bookmark, false); ++$count; @@ -167,7 +167,7 @@ class ShaareManageController extends ShaarliAdminController // To preserve backward compatibility with 3rd parties, plugins still use arrays $data = $formatter->format($bookmark); $this->executePageHooks('save_link', $data); - $bookmark->fromArray($data); + $bookmark->fromArray($data, $this->container->conf->get('general.tags_separator', ' ')); $this->container->bookmarkService->set($bookmark); diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index 18afc2d1..625a5680 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -113,7 +113,10 @@ class ShaarePublishController extends ShaarliAdminController $bookmark->setDescription($request->getParam('lf_description')); $bookmark->setUrl($request->getParam('lf_url'), $this->container->conf->get('security.allowed_protocols', [])); $bookmark->setPrivate(filter_var($request->getParam('lf_private'), FILTER_VALIDATE_BOOLEAN)); - $bookmark->setTagsString($request->getParam('lf_tags')); + $bookmark->setTagsString( + $request->getParam('lf_tags'), + $this->container->conf->get('general.tags_separator', ' ') + ); if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE && true !== $this->container->conf->get('general.enable_async_metadata', true) @@ -128,7 +131,7 @@ class ShaarePublishController extends ShaarliAdminController $data = $formatter->format($bookmark); $this->executePageHooks('save_link', $data); - $bookmark->fromArray($data); + $bookmark->fromArray($data, $this->container->conf->get('general.tags_separator', ' ')); $this->container->bookmarkService->set($bookmark); // If we are called from the bookmarklet, we must close the popup: @@ -221,6 +224,11 @@ class ShaarePublishController extends ShaarliAdminController protected function buildFormData(array $link, bool $isNew, Request $request): array { + $link['tags'] = strlen($link['tags']) > 0 + ? $link['tags'] . $this->container->conf->get('general.tags_separator', ' ') + : $link['tags'] + ; + return escape([ 'link' => $link, 'link_is_new' => $isNew, diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index 78c474c9..cc3837ce 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php @@ -95,6 +95,10 @@ class BookmarkListController extends ShaarliVisitorController $next_page_url = '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl; } + $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); + $searchTagsUrlEncoded = array_map('urlencode', tags_str2array($searchTags, $tagsSeparator)); + $searchTags = !empty($searchTags) ? trim($searchTags, $tagsSeparator) . $tagsSeparator : ''; + // Fill all template fields. $data = array_merge( $this->initializeTemplateVars(), @@ -106,7 +110,7 @@ class BookmarkListController extends ShaarliVisitorController 'result_count' => count($linksToDisplay), 'search_term' => escape($searchTerm), 'search_tags' => escape($searchTags), - 'search_tags_url' => array_map('urlencode', explode(' ', $searchTags)), + 'search_tags_url' => $searchTagsUrlEncoded, 'visibility' => $visibility, 'links' => $linkDisp, ] @@ -119,8 +123,9 @@ class BookmarkListController extends ShaarliVisitorController return '[' . $tag . ']'; }; $data['pagetitle'] .= ! empty($searchTags) - ? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchTags))) . ' ' - : ''; + ? implode(' ', array_map($bracketWrap, tags_str2array($searchTags, $tagsSeparator))) . ' ' + : '' + ; $data['pagetitle'] .= '- '; } diff --git a/application/front/controller/visitor/TagCloudController.php b/application/front/controller/visitor/TagCloudController.php index 76ed7690..560cad08 100644 --- a/application/front/controller/visitor/TagCloudController.php +++ b/application/front/controller/visitor/TagCloudController.php @@ -47,13 +47,14 @@ class TagCloudController extends ShaarliVisitorController */ protected function processRequest(string $type, Request $request, Response $response): Response { + $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); if ($this->container->loginManager->isLoggedIn() === true) { $visibility = $this->container->sessionManager->getSessionParameter('visibility'); } $sort = $request->getQueryParam('sort'); $searchTags = $request->getQueryParam('searchtags'); - $filteringTags = $searchTags !== null ? explode(' ', $searchTags) : []; + $filteringTags = $searchTags !== null ? explode($tagsSeparator, $searchTags) : []; $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); @@ -71,8 +72,9 @@ class TagCloudController extends ShaarliVisitorController $tagsUrl[escape($tag)] = urlencode((string) $tag); } - $searchTags = implode(' ', escape($filteringTags)); - $searchTagsUrl = urlencode(implode(' ', $filteringTags)); + $searchTags = tags_array2str($filteringTags, $tagsSeparator); + $searchTags = !empty($searchTags) ? trim($searchTags, $tagsSeparator) . $tagsSeparator : ''; + $searchTagsUrl = urlencode($searchTags); $data = [ 'search_tags' => escape($searchTags), 'search_tags_url' => $searchTagsUrl, @@ -82,7 +84,7 @@ class TagCloudController extends ShaarliVisitorController $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); $this->assignAllView($data); - $searchTags = !empty($searchTags) ? $searchTags .' - ' : ''; + $searchTags = !empty($searchTags) ? trim(str_replace($tagsSeparator, ' ', $searchTags)) .' - ' : ''; $this->assignView( 'pagetitle', $searchTags . t('Tag '. $type) .' - '. $this->container->conf->get('general.title', 'Shaarli') diff --git a/application/front/controller/visitor/TagController.php b/application/front/controller/visitor/TagController.php index de4e7ea2..7a3377a7 100644 --- a/application/front/controller/visitor/TagController.php +++ b/application/front/controller/visitor/TagController.php @@ -45,9 +45,10 @@ class TagController extends ShaarliVisitorController unset($params['addtag']); } + $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); // Check if this tag is already in the search query and ignore it if it is. // Each tag is always separated by a space - $currentTags = isset($params['searchtags']) ? explode(' ', $params['searchtags']) : []; + $currentTags = tags_str2array($params['searchtags'] ?? '', $tagsSeparator); $addtag = true; foreach ($currentTags as $value) { @@ -62,7 +63,7 @@ class TagController extends ShaarliVisitorController $currentTags[] = trim($newTag); } - $params['searchtags'] = trim(implode(' ', $currentTags)); + $params['searchtags'] = tags_array2str($currentTags, $tagsSeparator); // We also remove page (keeping the same page has no sense, since the results are different) unset($params['page']); @@ -98,10 +99,11 @@ class TagController extends ShaarliVisitorController } if (isset($params['searchtags'])) { - $tags = explode(' ', $params['searchtags']); + $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); + $tags = tags_str2array($params['searchtags'] ?? '', $tagsSeparator); // Remove value from array $tags. $tags = array_diff($tags, [$tagToRemove]); - $params['searchtags'] = implode(' ', $tags); + $params['searchtags'] = tags_array2str($tags, $tagsSeparator); if (empty($params['searchtags'])) { unset($params['searchtags']); -- cgit v1.2.3 From cfdd2094407e61f371c02117c8c66916a6d1d807 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 19:45:41 +0100 Subject: Display error details even with dev.debug set to false It makes more sense to display the error even if it's unexpected. Only for logged in users. Fixes #1606 --- application/front/controller/visitor/ErrorController.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/ErrorController.php b/application/front/controller/visitor/ErrorController.php index 8da11172..428e8254 100644 --- a/application/front/controller/visitor/ErrorController.php +++ b/application/front/controller/visitor/ErrorController.php @@ -26,8 +26,14 @@ class ErrorController extends ShaarliVisitorController $response = $response->withStatus($throwable->getCode()); } else { // Internal error (any other Throwable) - if ($this->container->conf->get('dev.debug', false)) { - $this->assignView('message', $throwable->getMessage()); + if ($this->container->conf->get('dev.debug', false) || $this->container->loginManager->isLoggedIn()) { + $this->assignView('message', t('Error: ') . $throwable->getMessage()); + $this->assignView( + 'text', + '' + . t('Please report it on Github.') + . '' + ); $this->assignView('stacktrace', exception2text($throwable)); } else { $this->assignView('message', t('An unexpected error occurred.')); @@ -36,7 +42,6 @@ class ErrorController extends ShaarliVisitorController $response = $response->withStatus(500); } - return $response->write($this->render('error')); } } -- cgit v1.2.3 From 53054b2bf6a919fd4ff9b44b6ad1986f21f488b6 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 22 Sep 2020 20:25:47 +0200 Subject: Apply PHP Code Beautifier on source code for linter automatic fixes --- .../front/controller/admin/ConfigureController.php | 7 +++--- .../front/controller/admin/ExportController.php | 4 ++-- .../front/controller/admin/ImportController.php | 4 ++-- .../front/controller/admin/ManageTagController.php | 4 ++-- .../front/controller/admin/PasswordController.php | 4 ++-- .../front/controller/admin/PluginsController.php | 4 ++-- .../front/controller/admin/ServerController.php | 2 +- .../controller/admin/SessionFilterController.php | 2 -- .../front/controller/admin/ShaareAddController.php | 2 +- .../controller/admin/ShaareManageController.php | 2 +- .../controller/admin/ShaarePublishController.php | 13 ++++++----- .../controller/admin/ThumbnailsController.php | 2 +- .../front/controller/admin/ToolsController.php | 2 +- .../controller/visitor/BookmarkListController.php | 8 ++++--- .../front/controller/visitor/DailyController.php | 2 +- .../front/controller/visitor/FeedController.php | 2 +- .../front/controller/visitor/InstallController.php | 25 ++++++++++++---------- .../front/controller/visitor/LoginController.php | 8 ++++--- .../controller/visitor/PictureWallController.php | 2 +- .../visitor/ShaarliVisitorController.php | 5 +++-- .../controller/visitor/TagCloudController.php | 4 ++-- .../front/controller/visitor/TagController.php | 8 +++---- 22 files changed, 63 insertions(+), 53 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index 0ed7ad81..eb26ef21 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -51,7 +51,7 @@ class ConfigureController extends ShaarliAdminController $this->assignView('languages', Languages::getAvailableLanguages()); $this->assignView('gd_enabled', extension_loaded('gd')); $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); - $this->assignView('pagetitle', t('Configure') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + $this->assignView('pagetitle', t('Configure') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')); return $response->write($this->render(TemplatePage::CONFIGURE)); } @@ -95,12 +95,13 @@ class ConfigureController extends ShaarliAdminController } $thumbnailsMode = extension_loaded('gd') ? $request->getParam('enableThumbnails') : Thumbnailer::MODE_NONE; - if ($thumbnailsMode !== Thumbnailer::MODE_NONE + if ( + $thumbnailsMode !== Thumbnailer::MODE_NONE && $thumbnailsMode !== $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) ) { $this->saveWarningMessage( t('You have enabled or changed thumbnails mode.') . - '' . t('Please synchronize them.') .'' + '' . t('Please synchronize them.') . '' ); } $this->container->conf->set('thumbnails.mode', $thumbnailsMode); diff --git a/application/front/controller/admin/ExportController.php b/application/front/controller/admin/ExportController.php index 2be957fa..f01d7e9b 100644 --- a/application/front/controller/admin/ExportController.php +++ b/application/front/controller/admin/ExportController.php @@ -23,7 +23,7 @@ class ExportController extends ShaarliAdminController */ public function index(Request $request, Response $response): Response { - $this->assignView('pagetitle', t('Export') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + $this->assignView('pagetitle', t('Export') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')); return $response->write($this->render(TemplatePage::EXPORT)); } @@ -68,7 +68,7 @@ class ExportController extends ShaarliAdminController $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8'); $response = $response->withHeader( 'Content-disposition', - 'attachment; filename=bookmarks_'.$selection.'_'.$now->format(Bookmark::LINK_DATE_FORMAT).'.html' + 'attachment; filename=bookmarks_' . $selection . '_' . $now->format(Bookmark::LINK_DATE_FORMAT) . '.html' ); $this->assignView('date', $now->format(DateTime::RFC822)); diff --git a/application/front/controller/admin/ImportController.php b/application/front/controller/admin/ImportController.php index 758d5ef9..c2ad6a09 100644 --- a/application/front/controller/admin/ImportController.php +++ b/application/front/controller/admin/ImportController.php @@ -38,7 +38,7 @@ class ImportController extends ShaarliAdminController true ) ); - $this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + $this->assignView('pagetitle', t('Import') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')); return $response->write($this->render(TemplatePage::IMPORT)); } @@ -64,7 +64,7 @@ class ImportController extends ShaarliAdminController $msg = sprintf( t( 'The file you are trying to upload is probably bigger than what this webserver can accept' - .' (%s). Please upload in smaller chunks.' + . ' (%s). Please upload in smaller chunks.' ), get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')) ); diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php index 22fb461c..8675a0c5 100644 --- a/application/front/controller/admin/ManageTagController.php +++ b/application/front/controller/admin/ManageTagController.php @@ -32,7 +32,7 @@ class ManageTagController extends ShaarliAdminController $this->assignView('tags_separator', $separator); $this->assignView( 'pagetitle', - t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli') + t('Manage tags') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); return $response->write($this->render(TemplatePage::CHANGE_TAG)); @@ -87,7 +87,7 @@ class ManageTagController extends ShaarliAdminController $this->saveSuccessMessage($alert); - $redirect = true === $isDelete ? '/admin/tags' : '/?searchtags='. urlencode($toTag); + $redirect = true === $isDelete ? '/admin/tags' : '/?searchtags=' . urlencode($toTag); return $this->redirect($response, $redirect); } diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php index 5ec0d24b..4aaf1f82 100644 --- a/application/front/controller/admin/PasswordController.php +++ b/application/front/controller/admin/PasswordController.php @@ -25,7 +25,7 @@ class PasswordController extends ShaarliAdminController $this->assignView( 'pagetitle', - t('Change password') .' - '. $this->container->conf->get('general.title', 'Shaarli') + t('Change password') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); } @@ -78,7 +78,7 @@ class PasswordController extends ShaarliAdminController // Save new password // Salt renders rainbow-tables attacks useless. - $this->container->conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); + $this->container->conf->set('credentials.salt', sha1(uniqid('', true) . '_' . mt_rand())); $this->container->conf->set( 'credentials.hash', sha1( diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php index 8e059681..ae47c1af 100644 --- a/application/front/controller/admin/PluginsController.php +++ b/application/front/controller/admin/PluginsController.php @@ -42,7 +42,7 @@ class PluginsController extends ShaarliAdminController $this->assignView('disabledPlugins', $disabledPlugins); $this->assignView( 'pagetitle', - t('Plugin Administration') .' - '. $this->container->conf->get('general.title', 'Shaarli') + t('Plugin Administration') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); return $response->write($this->render(TemplatePage::PLUGINS_ADMIN)); @@ -64,7 +64,7 @@ class PluginsController extends ShaarliAdminController unset($parameters['parameters_form']); unset($parameters['token']); foreach ($parameters as $param => $value) { - $this->container->conf->set('plugins.'. $param, escape($value)); + $this->container->conf->set('plugins.' . $param, escape($value)); } } else { $this->container->conf->set('general.enabled_plugins', save_plugin_config($parameters)); diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php index bfc99422..80997940 100644 --- a/application/front/controller/admin/ServerController.php +++ b/application/front/controller/admin/ServerController.php @@ -65,7 +65,7 @@ class ServerController extends ShaarliAdminController $this->saveWarningMessage( t('Thumbnails cache has been cleared.') . ' ' . - '' . t('Please synchronize them.') .'' + '' . t('Please synchronize them.') . '' ); } else { $folders = [ diff --git a/application/front/controller/admin/SessionFilterController.php b/application/front/controller/admin/SessionFilterController.php index d9a7a2e0..0917b6d2 100644 --- a/application/front/controller/admin/SessionFilterController.php +++ b/application/front/controller/admin/SessionFilterController.php @@ -45,6 +45,4 @@ class SessionFilterController extends ShaarliAdminController return $this->redirectFromReferer($request, $response, ['visibility']); } - - } diff --git a/application/front/controller/admin/ShaareAddController.php b/application/front/controller/admin/ShaareAddController.php index 8dc386b2..ab8e7f40 100644 --- a/application/front/controller/admin/ShaareAddController.php +++ b/application/front/controller/admin/ShaareAddController.php @@ -23,7 +23,7 @@ class ShaareAddController extends ShaarliAdminController $this->assignView( 'pagetitle', - t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') + t('Shaare a new link') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); $this->assignView('tags', $tags); $this->assignView('default_private_links', $this->container->conf->get('privacy.default_private_links', false)); diff --git a/application/front/controller/admin/ShaareManageController.php b/application/front/controller/admin/ShaareManageController.php index 0b143172..35837baa 100644 --- a/application/front/controller/admin/ShaareManageController.php +++ b/application/front/controller/admin/ShaareManageController.php @@ -54,7 +54,7 @@ class ShaareManageController extends ShaarliAdminController $data = $formatter->format($bookmark); $this->executePageHooks('delete_link', $data); $this->container->bookmarkService->remove($bookmark, false); - ++ $count; + ++$count; } if ($count > 0) { diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index 625a5680..4cbfcdc5 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -118,7 +118,8 @@ class ShaarePublishController extends ShaarliAdminController $this->container->conf->get('general.tags_separator', ' ') ); - if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE + if ( + $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE && true !== $this->container->conf->get('general.enable_async_metadata', true) && $bookmark->shouldUpdateThumbnail() ) { @@ -148,7 +149,8 @@ class ShaarePublishController extends ShaarliAdminController return $this->redirectFromReferer( $request, $response, - ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'], + ['/admin/add-shaare', '/admin/shaare'], + ['addlink', 'post', 'edit_link'], $bookmark->getShortUrl() ); } @@ -168,10 +170,10 @@ class ShaarePublishController extends ShaarliAdminController $this->assignView($key, $value); } - $editLabel = false === $isNew ? t('Edit') .' ' : ''; + $editLabel = false === $isNew ? t('Edit') . ' ' : ''; $this->assignView( 'pagetitle', - $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') + $editLabel . t('Shaare') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); return $response->write($this->render(TemplatePage::EDIT_LINK)); @@ -194,7 +196,8 @@ class ShaarePublishController extends ShaarliAdminController // If this is an HTTP(S) link, we try go get the page to extract // the title (otherwise we will to straight to the edit form.) - if (true !== $this->container->conf->get('general.enable_async_metadata', true) + if ( + true !== $this->container->conf->get('general.enable_async_metadata', true) && empty($title) && strpos(get_url_scheme($url) ?: '', 'http') !== false ) { diff --git a/application/front/controller/admin/ThumbnailsController.php b/application/front/controller/admin/ThumbnailsController.php index 4dc09d38..94d97d4b 100644 --- a/application/front/controller/admin/ThumbnailsController.php +++ b/application/front/controller/admin/ThumbnailsController.php @@ -34,7 +34,7 @@ class ThumbnailsController extends ShaarliAdminController $this->assignView('ids', $ids); $this->assignView( 'pagetitle', - t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli') + t('Thumbnails update') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); return $response->write($this->render(TemplatePage::THUMBNAILS)); diff --git a/application/front/controller/admin/ToolsController.php b/application/front/controller/admin/ToolsController.php index a87f20d2..560e5e3e 100644 --- a/application/front/controller/admin/ToolsController.php +++ b/application/front/controller/admin/ToolsController.php @@ -28,7 +28,7 @@ class ToolsController extends ShaarliAdminController $this->assignView($key, $value); } - $this->assignView('pagetitle', t('Tools') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + $this->assignView('pagetitle', t('Tools') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')); return $response->write($this->render(TemplatePage::TOOLS)); } diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index cc3837ce..fe8231be 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php @@ -35,7 +35,8 @@ class BookmarkListController extends ShaarliVisitorController $formatter->addContextData('base_path', $this->container->basePath); $searchTags = normalize_spaces($request->getParam('searchtags') ?? ''); - $searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? ''));; + $searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? '')); + ; // Filter bookmarks according search parameters. $visibility = $this->container->sessionManager->getSessionParameter('visibility'); @@ -160,7 +161,7 @@ class BookmarkListController extends ShaarliVisitorController $data = array_merge( $this->initializeTemplateVars(), [ - 'pagetitle' => $bookmark->getTitle() .' - '. $this->container->conf->get('general.title', 'Shaarli'), + 'pagetitle' => $bookmark->getTitle() . ' - ' . $this->container->conf->get('general.title', 'Shaarli'), 'links' => [$formatter->format($bookmark)], ] ); @@ -185,7 +186,8 @@ class BookmarkListController extends ShaarliVisitorController $bookmark->setThumbnail(null); // Requires an update, not async retrieval, thumbnails enabled - if ($bookmark->shouldUpdateThumbnail() + if ( + $bookmark->shouldUpdateThumbnail() && true !== $this->container->conf->get('general.enable_async_metadata', true) && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE ) { diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 728bc2d8..846cfe22 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php @@ -132,7 +132,7 @@ class DailyController extends ShaarliVisitorController 'date' => $endDateTime, 'date_rss' => $endDateTime->format(DateTime::RSS), 'date_human' => DailyPageHelper::getDescriptionByType($type, $dayDateTime), - 'absolute_url' => $indexUrl . 'daily?'. $type .'=' . $day, + 'absolute_url' => $indexUrl . 'daily?' . $type . '=' . $day, 'links' => [], ]; 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 protected function processRequest(string $feedType, Request $request, Response $response): Response { - $response = $response->withHeader('Content-Type', 'application/'. $feedType .'+xml; charset=utf-8'); + $response = $response->withHeader('Content-Type', 'application/' . $feedType . '+xml; charset=utf-8'); $pageUrl = page_url($this->container->environment); $cache = $this->container->pageCacheManager->getCachePage($pageUrl); diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php index 22329294..bf965929 100644 --- a/application/front/controller/visitor/InstallController.php +++ b/application/front/controller/visitor/InstallController.php @@ -39,7 +39,8 @@ class InstallController extends ShaarliVisitorController // Before installation, we'll make sure that permissions are set properly, and sessions are working. $this->checkPermissions(); - if (static::SESSION_TEST_VALUE + if ( + static::SESSION_TEST_VALUE !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY) ) { $this->container->sessionManager->setSessionParameter(static::SESSION_TEST_KEY, static::SESSION_TEST_VALUE); @@ -75,17 +76,18 @@ class InstallController extends ShaarliVisitorController // This part makes sure sessions works correctly. // (Because on some hosts, session.save_path may not be set correctly, // or we may not have write access to it.) - if (static::SESSION_TEST_VALUE + if ( + static::SESSION_TEST_VALUE !== $this->container->sessionManager->getSessionParameter(static::SESSION_TEST_KEY) ) { // Step 2: Check if data in session is correct. $msg = t( - '
Sessions do not seem to work correctly on your server.
'. - 'Make sure the variable "session.save_path" is set correctly in your PHP config, '. - 'and that you have write access to it.
'. - 'It currently points to %s.
'. - 'On some browsers, accessing your server via a hostname like \'localhost\' '. - 'or any custom hostname without a dot causes cookie storage to fail. '. + '
Sessions do not seem to work correctly on your server.
' . + 'Make sure the variable "session.save_path" is set correctly in your PHP config, ' . + 'and that you have write access to it.
' . + 'It currently points to %s.
' . + 'On some browsers, accessing your server via a hostname like \'localhost\' ' . + 'or any custom hostname without a dot causes cookie storage to fail. ' . 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.
' ); $msg = sprintf($msg, $this->container->sessionManager->getSavePath()); @@ -104,7 +106,8 @@ class InstallController extends ShaarliVisitorController public function save(Request $request, Response $response): Response { $timezone = 'UTC'; - if (!empty($request->getParam('continent')) + if ( + !empty($request->getParam('continent')) && !empty($request->getParam('city')) && isTimeZoneValid($request->getParam('continent'), $request->getParam('city')) ) { @@ -114,7 +117,7 @@ class InstallController extends ShaarliVisitorController $login = $request->getParam('setlogin'); $this->container->conf->set('credentials.login', $login); - $salt = sha1(uniqid('', true) .'_'. mt_rand()); + $salt = sha1(uniqid('', true) . '_' . mt_rand()); $this->container->conf->set('credentials.salt', $salt); $this->container->conf->set('credentials.hash', sha1($request->getParam('setpassword') . $login . $salt)); @@ -123,7 +126,7 @@ class InstallController extends ShaarliVisitorController } else { $this->container->conf->set( 'general.title', - 'Shared bookmarks on '.escape(index_url($this->container->environment)) + 'Shared bookmarks on ' . escape(index_url($this->container->environment)) ); } diff --git a/application/front/controller/visitor/LoginController.php b/application/front/controller/visitor/LoginController.php index f5038fe3..4b881535 100644 --- a/application/front/controller/visitor/LoginController.php +++ b/application/front/controller/visitor/LoginController.php @@ -43,7 +43,7 @@ class LoginController extends ShaarliVisitorController $this ->assignView('returnurl', escape($returnUrl)) ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true)) - ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli')) + ->assignView('pagetitle', t('Login') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')) ; return $response->write($this->render(TemplatePage::LOGIN)); @@ -64,7 +64,8 @@ class LoginController extends ShaarliVisitorController return $this->redirect($response, '/'); } - if (!$this->container->loginManager->checkCredentials( + if ( + !$this->container->loginManager->checkCredentials( client_ip_id($this->container->environment), $request->getParam('login'), $request->getParam('password') @@ -101,7 +102,8 @@ class LoginController extends ShaarliVisitorController */ protected function checkLoginState(): bool { - if ($this->container->loginManager->isLoggedIn() + if ( + $this->container->loginManager->isLoggedIn() || $this->container->conf->get('security.open_shaarli', false) ) { 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 $this->assignView( 'pagetitle', - t('Picture wall') .' - '. $this->container->conf->get('general.title', 'Shaarli') + t('Picture wall') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); // Optionally filter the results: diff --git a/application/front/controller/visitor/ShaarliVisitorController.php b/application/front/controller/visitor/ShaarliVisitorController.php index 54f9fe03..ae946c59 100644 --- a/application/front/controller/visitor/ShaarliVisitorController.php +++ b/application/front/controller/visitor/ShaarliVisitorController.php @@ -144,7 +144,8 @@ abstract class ShaarliVisitorController if (null !== $referer) { $currentUrl = parse_url($referer); // If the referer is not related to Shaarli instance, redirect to default - if (isset($currentUrl['host']) + if ( + isset($currentUrl['host']) && strpos(index_url($this->container->environment), $currentUrl['host']) === false ) { return $response->withRedirect($defaultPath); @@ -173,7 +174,7 @@ abstract class ShaarliVisitorController } } - $queryString = count($params) > 0 ? '?'. http_build_query($params) : ''; + $queryString = count($params) > 0 ? '?' . http_build_query($params) : ''; $anchor = $anchor ? '#' . $anchor : ''; return $response->withRedirect($path . $queryString . $anchor); diff --git a/application/front/controller/visitor/TagCloudController.php b/application/front/controller/visitor/TagCloudController.php index 560cad08..46d62779 100644 --- a/application/front/controller/visitor/TagCloudController.php +++ b/application/front/controller/visitor/TagCloudController.php @@ -84,10 +84,10 @@ class TagCloudController extends ShaarliVisitorController $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); $this->assignAllView($data); - $searchTags = !empty($searchTags) ? trim(str_replace($tagsSeparator, ' ', $searchTags)) .' - ' : ''; + $searchTags = !empty($searchTags) ? trim(str_replace($tagsSeparator, ' ', $searchTags)) . ' - ' : ''; $this->assignView( 'pagetitle', - $searchTags . t('Tag '. $type) .' - '. $this->container->conf->get('general.title', 'Shaarli') + $searchTags . t('Tag ' . $type) . ' - ' . $this->container->conf->get('general.title', 'Shaarli') ); return $response->write($this->render('tag.' . $type)); diff --git a/application/front/controller/visitor/TagController.php b/application/front/controller/visitor/TagController.php index 7a3377a7..3aa58542 100644 --- a/application/front/controller/visitor/TagController.php +++ b/application/front/controller/visitor/TagController.php @@ -27,7 +27,7 @@ class TagController extends ShaarliVisitorController // In case browser does not send HTTP_REFERER, we search a single tag if (null === $referer) { if (null !== $newTag) { - return $this->redirect($response, '/?searchtags='. urlencode($newTag)); + return $this->redirect($response, '/?searchtags=' . urlencode($newTag)); } return $this->redirect($response, '/'); @@ -37,7 +37,7 @@ class TagController extends ShaarliVisitorController parse_str($currentUrl['query'] ?? '', $params); if (null === $newTag) { - return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params)); + return $response->withRedirect(($currentUrl['path'] ?? './') . '?' . http_build_query($params)); } // Prevent redirection loop @@ -68,7 +68,7 @@ class TagController extends ShaarliVisitorController // We also remove page (keeping the same page has no sense, since the results are different) unset($params['page']); - return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params)); + return $response->withRedirect(($currentUrl['path'] ?? './') . '?' . http_build_query($params)); } /** @@ -90,7 +90,7 @@ class TagController extends ShaarliVisitorController parse_str($currentUrl['query'] ?? '', $params); if (null === $tagToRemove) { - return $response->withRedirect(($currentUrl['path'] ?? './') .'?'. http_build_query($params)); + return $response->withRedirect(($currentUrl['path'] ?? './') . '?' . http_build_query($params)); } // Prevent redirection loop -- cgit v1.2.3 From b99e00f7cd5f7e2090f44cd97bfb426db55340c2 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 8 Nov 2020 15:02:45 +0100 Subject: Manually fix remaining PHPCS errors --- application/front/controller/admin/ConfigureController.php | 9 +++++++-- application/front/controller/admin/ServerController.php | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index eb26ef21..dc421661 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -51,7 +51,10 @@ class ConfigureController extends ShaarliAdminController $this->assignView('languages', Languages::getAvailableLanguages()); $this->assignView('gd_enabled', extension_loaded('gd')); $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); - $this->assignView('pagetitle', t('Configure') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')); + $this->assignView( + 'pagetitle', + t('Configure') . ' - ' . $this->container->conf->get('general.title', 'Shaarli') + ); return $response->write($this->render(TemplatePage::CONFIGURE)); } @@ -101,7 +104,9 @@ class ConfigureController extends ShaarliAdminController ) { $this->saveWarningMessage( t('You have enabled or changed thumbnails mode.') . - '' . t('Please synchronize them.') . '' + '' . + t('Please synchronize them.') . + '' ); } $this->container->conf->set('thumbnails.mode', $thumbnailsMode); diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php index 80997940..575a2f9d 100644 --- a/application/front/controller/admin/ServerController.php +++ b/application/front/controller/admin/ServerController.php @@ -65,7 +65,9 @@ class ServerController extends ShaarliAdminController $this->saveWarningMessage( t('Thumbnails cache has been cleared.') . ' ' . - '' . t('Please synchronize them.') . '' + '' . + t('Please synchronize them.') . + '' ); } else { $folders = [ -- cgit v1.2.3 From 80c8889bfe5151a23066188e6c74c3c1e8575e61 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 9 Nov 2020 14:37:45 +0100 Subject: Server admin: do not retrieve latest version without update_check If the setting 'updates.check_updates' is disabled, do not retrieve the latest version on server administration page. Additionally, updated default values for - updates.check_updates from false to true - updates.check_updates_branch from stable to latest --- application/front/controller/admin/ServerController.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php index bfc99422..780151dd 100644 --- a/application/front/controller/admin/ServerController.php +++ b/application/front/controller/admin/ServerController.php @@ -25,9 +25,16 @@ class ServerController extends ShaarliAdminController */ public function index(Request $request, Response $response): Response { - $latestVersion = 'v' . ApplicationUtils::getVersion( - ApplicationUtils::$GIT_RAW_URL . '/latest/' . ApplicationUtils::$VERSION_FILE - ); + $releaseUrl = ApplicationUtils::$GITHUB_URL . '/releases/'; + if ($this->container->conf->get('updates.check_updates', true)) { + $latestVersion = 'v' . ApplicationUtils::getVersion( + ApplicationUtils::$GIT_RAW_URL . '/latest/' . ApplicationUtils::$VERSION_FILE + ); + $releaseUrl .= 'tag/' . $latestVersion; + } else { + $latestVersion = t('Check disabled'); + } + $currentVersion = ApplicationUtils::getVersion('./shaarli_version.php'); $currentVersion = $currentVersion === 'dev' ? $currentVersion : 'v' . $currentVersion; $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION)); @@ -37,7 +44,7 @@ class ServerController extends ShaarliAdminController $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable()); $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement()); $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf)); - $this->assignView('release_url', ApplicationUtils::$GITHUB_URL . '/releases/tag/' . $latestVersion); + $this->assignView('release_url', $releaseUrl); $this->assignView('latest_version', $latestVersion); $this->assignView('current_version', $currentVersion); $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode')); -- cgit v1.2.3 From 2883c6d0a71db174ee8df7548178a8fbee486e25 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 15 Nov 2020 12:05:08 +0100 Subject: Daily RSS - Remove relative description (today, yesterday) It is not useful for the RSS feed, as every new entry will be 'yesterday', and it requires an update the next day. --- application/front/controller/visitor/DailyController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 846cfe22..5ae89299 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php @@ -131,7 +131,7 @@ class DailyController extends ShaarliVisitorController $dataPerDay[$day] = [ 'date' => $endDateTime, 'date_rss' => $endDateTime->format(DateTime::RSS), - 'date_human' => DailyPageHelper::getDescriptionByType($type, $dayDateTime), + 'date_human' => DailyPageHelper::getDescriptionByType($type, $dayDateTime, false), 'absolute_url' => $indexUrl . 'daily?' . $type . '=' . $day, 'links' => [], ]; -- cgit v1.2.3 From 8a6b7e96b7176e03238bbb1bcaa4c8b0c25e6358 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 24 Nov 2020 13:28:17 +0100 Subject: Fix: soft fail if the mutex is not working And display the error in server admin page Fixes #1650 --- application/front/controller/admin/ServerController.php | 7 ++++++- application/front/controller/visitor/InstallController.php | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php index fabeaf2f..4b74f4a9 100644 --- a/application/front/controller/admin/ServerController.php +++ b/application/front/controller/admin/ServerController.php @@ -39,11 +39,16 @@ class ServerController extends ShaarliAdminController $currentVersion = $currentVersion === 'dev' ? $currentVersion : 'v' . $currentVersion; $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION)); + $permissions = array_merge( + ApplicationUtils::checkResourcePermissions($this->container->conf), + ApplicationUtils::checkDatastoreMutex() + ); + $this->assignView('php_version', PHP_VERSION); $this->assignView('php_eol', format_date($phpEol, false)); $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable()); $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement()); - $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf)); + $this->assignView('permissions', $permissions); $this->assignView('release_url', $releaseUrl); $this->assignView('latest_version', $latestVersion); $this->assignView('current_version', $currentVersion); diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php index bf965929..418d4a49 100644 --- a/application/front/controller/visitor/InstallController.php +++ b/application/front/controller/visitor/InstallController.php @@ -56,11 +56,16 @@ class InstallController extends ShaarliVisitorController $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION)); + $permissions = array_merge( + ApplicationUtils::checkResourcePermissions($this->container->conf), + ApplicationUtils::checkDatastoreMutex() + ); + $this->assignView('php_version', PHP_VERSION); $this->assignView('php_eol', format_date($phpEol, false)); $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable()); $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement()); - $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf)); + $this->assignView('permissions', $permissions); $this->assignView('pagetitle', t('Install Shaarli')); -- cgit v1.2.3 From 6a3a78d023aa320138bb88505b58347db268264a Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 16 Dec 2020 14:04:32 +0100 Subject: Fix: synchronous metadata retrieval is failing in strict mode Metadata can now only be string or null. Fixes #1653 --- application/front/controller/admin/ShaarePublishController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/front/controller') diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php index 4cbfcdc5..fb9cacc2 100644 --- a/application/front/controller/admin/ShaarePublishController.php +++ b/application/front/controller/admin/ShaarePublishController.php @@ -227,7 +227,7 @@ class ShaarePublishController extends ShaarliAdminController protected function buildFormData(array $link, bool $isNew, Request $request): array { - $link['tags'] = strlen($link['tags']) > 0 + $link['tags'] = $link['tags'] !== null && strlen($link['tags']) > 0 ? $link['tags'] . $this->container->conf->get('general.tags_separator', ' ') : $link['tags'] ; -- cgit v1.2.3 From f00600a283617286c813dc902fe3a2d66938b5fc Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 17 Dec 2020 15:43:33 +0100 Subject: Daily RSS Cache: invalidate cache base on the date Currently the cache is only invalidated when the datastore changes, while it should rely on selected period of time. Fixes #1659 --- application/front/controller/visitor/DailyController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'application/front/controller') diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 5ae89299..29492a5f 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php @@ -86,9 +86,11 @@ class DailyController extends ShaarliVisitorController public function rss(Request $request, Response $response): Response { $response = $response->withHeader('Content-Type', 'application/rss+xml; charset=utf-8'); + $type = DailyPageHelper::extractRequestedType($request); + $cacheDuration = DailyPageHelper::getCacheDatePeriodByType($type); $pageUrl = page_url($this->container->environment); - $cache = $this->container->pageCacheManager->getCachePage($pageUrl); + $cache = $this->container->pageCacheManager->getCachePage($pageUrl, $cacheDuration); $cached = $cache->cachedVersion(); if (!empty($cached)) { @@ -96,7 +98,6 @@ class DailyController extends ShaarliVisitorController } $days = []; - $type = DailyPageHelper::extractRequestedType($request); $format = DailyPageHelper::getFormatByType($type); $length = DailyPageHelper::getRssLengthByType($type); foreach ($this->container->bookmarkService->search() as $bookmark) { -- cgit v1.2.3