From 2899ebb5b5e82890c877151f5c02045266ac9973 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 22 May 2020 13:20:31 +0200 Subject: Initialize admin Slim controllers - Reorganize visitor controllers - Fix redirection with Slim's requests base path - Fix daily links --- .../front/controller/admin/LogoutController.php | 29 ++++++++ .../controller/admin/SessionFilterController.php | 79 ++++++++++++++++++++++ .../controller/admin/ShaarliAdminController.php | 21 ++++++ 3 files changed, 129 insertions(+) create mode 100644 application/front/controller/admin/LogoutController.php create mode 100644 application/front/controller/admin/SessionFilterController.php create mode 100644 application/front/controller/admin/ShaarliAdminController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/LogoutController.php b/application/front/controller/admin/LogoutController.php new file mode 100644 index 00000000..41e81984 --- /dev/null +++ b/application/front/controller/admin/LogoutController.php @@ -0,0 +1,29 @@ +container->pageCacheManager->invalidateCaches(); + $this->container->sessionManager->logout(); + + // TODO: switch to a simple Cookie manager allowing to check the session, and create mocks. + setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->webPath); + + return $response->withRedirect('./'); + } +} diff --git a/application/front/controller/admin/SessionFilterController.php b/application/front/controller/admin/SessionFilterController.php new file mode 100644 index 00000000..69a16ec3 --- /dev/null +++ b/application/front/controller/admin/SessionFilterController.php @@ -0,0 +1,79 @@ +getParam('nb') ?? null; + if (null === $linksPerPage || false === is_numeric($linksPerPage)) { + $linksPerPage = $this->container->conf->get('general.links_per_page', 20); + } + + $this->container->sessionManager->setSessionParameter( + SessionManager::KEY_LINKS_PER_PAGE, + abs(intval($linksPerPage)) + ); + + return $this->redirectFromReferer($request, $response, ['linksperpage'], ['nb']); + } + + /** + * GET /visibility: allows to display only public or only private bookmarks in linklist + */ + public function visibility(Request $request, Response $response, array $args): Response + { + if (false === $this->container->loginManager->isLoggedIn()) { + return $this->redirectFromReferer($request, $response, ['visibility']); + } + + $newVisibility = $args['visibility'] ?? null; + if (false === in_array($newVisibility, [BookmarkFilter::$PRIVATE, BookmarkFilter::$PUBLIC], true)) { + $newVisibility = null; + } + + $currentVisibility = $this->container->sessionManager->getSessionParameter(SessionManager::KEY_VISIBILITY); + + // Visibility not set or not already expected value, set expected value, otherwise reset it + if ($newVisibility !== null && (null === $currentVisibility || $currentVisibility !== $newVisibility)) { + // See only public bookmarks + $this->container->sessionManager->setSessionParameter( + SessionManager::KEY_VISIBILITY, + $newVisibility + ); + } else { + $this->container->sessionManager->deleteSessionParameter(SessionManager::KEY_VISIBILITY); + } + + return $this->redirectFromReferer($request, $response, ['visibility']); + } + + /** + * GET /untagged-only: allows to display only bookmarks without any tag + */ + public function untaggedOnly(Request $request, Response $response): Response + { + $this->container->sessionManager->setSessionParameter( + SessionManager::KEY_UNTAGGED_ONLY, + empty($this->container->sessionManager->getSessionParameter(SessionManager::KEY_UNTAGGED_ONLY)) + ); + + return $this->redirectFromReferer($request, $response, ['untaggedonly', 'untagged-only']); + } +} diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php new file mode 100644 index 00000000..ea703f62 --- /dev/null +++ b/application/front/controller/admin/ShaarliAdminController.php @@ -0,0 +1,21 @@ +container->loginManager->isLoggedIn()) { + throw new UnauthorizedException(); + } + } +} -- cgit v1.2.3 From ba43064ddb7771fc97df135a32f9b0d5e373dd36 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 22 May 2020 13:47:02 +0200 Subject: Process tools page through Slim controller --- .../front/controller/admin/ToolsController.php | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 application/front/controller/admin/ToolsController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ToolsController.php b/application/front/controller/admin/ToolsController.php new file mode 100644 index 00000000..66db5ad9 --- /dev/null +++ b/application/front/controller/admin/ToolsController.php @@ -0,0 +1,49 @@ + index_url($this->container->environment), + 'sslenabled' => is_https($this->container->environment), + ]; + + $this->executeHooks($data); + + foreach ($data as $key => $value) { + $this->assignView($key, $value); + } + + $this->assignView('pagetitle', t('Tools') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + + return $response->write($this->render('tools')); + } + + /** + * @param mixed[] $data Variables passed to the template engine + * + * @return mixed[] Template data after active plugins render_picwall hook execution. + */ + protected function executeHooks(array $data): array + { + $this->container->pluginManager->executeHooks( + 'render_tools', + $data + ); + + return $data; + } +} -- cgit v1.2.3 From ef00f9d2033f6de11e71bf3a909399cae6f73a9f Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 27 May 2020 13:35:48 +0200 Subject: Process password change controller through Slim --- .../front/controller/admin/PasswordController.php | 100 +++++++++++++++++++++ .../controller/admin/ShaarliAdminController.php | 59 ++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 application/front/controller/admin/PasswordController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php new file mode 100644 index 00000000..6e8f0bcb --- /dev/null +++ b/application/front/controller/admin/PasswordController.php @@ -0,0 +1,100 @@ +assignView( + 'pagetitle', + t('Change password') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + } + + /** + * GET /password - Displays the change password template + */ + public function index(Request $request, Response $response): Response + { + return $response->write($this->render('changepassword')); + } + + /** + * POST /password - Change admin password - existing and new passwords need to be provided. + */ + public function change(Request $request, Response $response): Response + { + $this->checkToken($request); + + if ($this->container->conf->get('security.open_shaarli', false)) { + throw new OpenShaarliPasswordException(); + } + + $oldPassword = $request->getParam('oldpassword'); + $newPassword = $request->getParam('setpassword'); + + if (empty($newPassword) || empty($oldPassword)) { + $this->saveErrorMessage(t('You must provide the current and new password to change it.')); + + return $response + ->withStatus(400) + ->write($this->render('changepassword')) + ; + } + + // Make sure old password is correct. + $oldHash = sha1( + $oldPassword . + $this->container->conf->get('credentials.login') . + $this->container->conf->get('credentials.salt') + ); + + if ($oldHash !== $this->container->conf->get('credentials.hash')) { + $this->saveErrorMessage(t('The old password is not correct.')); + + return $response + ->withStatus(400) + ->write($this->render('changepassword')) + ; + } + + // 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.hash', + sha1( + $newPassword + . $this->container->conf->get('credentials.login') + . $this->container->conf->get('credentials.salt') + ) + ); + + try { + $this->container->conf->write($this->container->loginManager->isLoggedIn()); + } catch (Throwable $e) { + throw new ShaarliFrontException($e->getMessage(), 500, $e); + } + + $this->saveSuccessMessage(t('Your password has been changed')); + + return $response->write($this->render('changepassword')); + } +} diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php index ea703f62..3385006c 100644 --- a/application/front/controller/admin/ShaarliAdminController.php +++ b/application/front/controller/admin/ShaarliAdminController.php @@ -7,7 +7,19 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Container\ShaarliContainer; use Shaarli\Front\Controller\Visitor\ShaarliVisitorController; use Shaarli\Front\Exception\UnauthorizedException; +use Shaarli\Front\Exception\WrongTokenException; +use Shaarli\Security\SessionManager; +use Slim\Http\Request; +/** + * Class ShaarliAdminController + * + * All admin controllers (for logged in users) MUST extend this abstract class. + * It makes sure that the user is properly logged in, and otherwise throw an exception + * which will redirect to the login page. + * + * @package Shaarli\Front\Controller\Admin + */ abstract class ShaarliAdminController extends ShaarliVisitorController { public function __construct(ShaarliContainer $container) @@ -18,4 +30,51 @@ abstract class ShaarliAdminController extends ShaarliVisitorController throw new UnauthorizedException(); } } + + /** + * Any persistent action to the config or data store must check the XSRF token validity. + */ + protected function checkToken(Request $request): void + { + if (!$this->container->sessionManager->checkToken($request->getParam('token'))) { + throw new WrongTokenException(); + } + } + + /** + * Save a SUCCESS message in user session, which will be displayed on any template page. + */ + protected function saveSuccessMessage(string $message): void + { + $this->saveMessage(SessionManager::KEY_SUCCESS_MESSAGES, $message); + } + + /** + * Save a WARNING message in user session, which will be displayed on any template page. + */ + protected function saveWarningMessage(string $message): void + { + $this->saveMessage(SessionManager::KEY_WARNING_MESSAGES, $message); + } + + /** + * Save an ERROR message in user session, which will be displayed on any template page. + */ + protected function saveErrorMessage(string $message): void + { + $this->saveMessage(SessionManager::KEY_ERROR_MESSAGES, $message); + } + + /** + * Use the sessionManager to save the provided message using the proper type. + * + * @param string $type successed/warnings/errors + */ + protected function saveMessage(string $type, string $message): void + { + $messages = $this->container->sessionManager->getSessionParameter($type) ?? []; + $messages[] = $message; + + $this->container->sessionManager->setSessionParameter($type, $messages); + } } -- cgit v1.2.3 From fdedbfd4a7fb547da0e0ce65c6180f74aad90691 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 27 May 2020 14:13:49 +0200 Subject: Test ShaarliAdminController --- application/front/controller/admin/ShaarliAdminController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php index 3385006c..3bc5bb6b 100644 --- a/application/front/controller/admin/ShaarliAdminController.php +++ b/application/front/controller/admin/ShaarliAdminController.php @@ -34,11 +34,13 @@ abstract class ShaarliAdminController extends ShaarliVisitorController /** * Any persistent action to the config or data store must check the XSRF token validity. */ - protected function checkToken(Request $request): void + protected function checkToken(Request $request): bool { if (!$this->container->sessionManager->checkToken($request->getParam('token'))) { throw new WrongTokenException(); } + + return true; } /** -- cgit v1.2.3 From 66063ed1a18d739b1a60bfb163d8656417a4c529 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 30 May 2020 14:00:06 +0200 Subject: Process configure page through Slim controller --- .../front/controller/admin/ConfigureController.php | 120 +++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 application/front/controller/admin/ConfigureController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php new file mode 100644 index 00000000..b1d32270 --- /dev/null +++ b/application/front/controller/admin/ConfigureController.php @@ -0,0 +1,120 @@ +assignView('title', $this->container->conf->get('general.title', 'Shaarli')); + $this->assignView('theme', $this->container->conf->get('resource.theme')); + $this->assignView( + 'theme_available', + ThemeUtils::getThemes($this->container->conf->get('resource.raintpl_tpl')) + ); + $this->assignView('formatter_available', ['default', 'markdown']); + list($continents, $cities) = generateTimeZoneData( + timezone_identifiers_list(), + $this->container->conf->get('general.timezone') + ); + $this->assignView('continents', $continents); + $this->assignView('cities', $cities); + $this->assignView('retrieve_description', $this->container->conf->get('general.retrieve_description', false)); + $this->assignView('private_links_default', $this->container->conf->get('privacy.default_private_links', false)); + $this->assignView( + 'session_protection_disabled', + $this->container->conf->get('security.session_protection_disabled', false) + ); + $this->assignView('enable_rss_permalinks', $this->container->conf->get('feed.rss_permalinks', false)); + $this->assignView('enable_update_check', $this->container->conf->get('updates.check_updates', true)); + $this->assignView('hide_public_links', $this->container->conf->get('privacy.hide_public_links', false)); + $this->assignView('api_enabled', $this->container->conf->get('api.enabled', true)); + $this->assignView('api_secret', $this->container->conf->get('api.secret')); + $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')); + + return $response->write($this->render('configure')); + } + + /** + * POST /configure - Update Shaarli's configuration + */ + public function save(Request $request, Response $response): Response + { + $this->checkToken($request); + + $continent = $request->getParam('continent'); + $city = $request->getParam('city'); + $tz = 'UTC'; + if (null !== $continent && null !== $city && isTimeZoneValid($continent, $city)) { + $tz = $continent . '/' . $city; + } + + $this->container->conf->set('general.timezone', $tz); + $this->container->conf->set('general.title', escape($request->getParam('title'))); + $this->container->conf->set('general.header_link', escape($request->getParam('titleLink'))); + $this->container->conf->set('general.retrieve_description', !empty($request->getParam('retrieveDescription'))); + $this->container->conf->set('resource.theme', escape($request->getParam('theme'))); + $this->container->conf->set( + 'security.session_protection_disabled', + !empty($request->getParam('disablesessionprotection')) + ); + $this->container->conf->set( + 'privacy.default_private_links', + !empty($request->getParam('privateLinkByDefault')) + ); + $this->container->conf->set('feed.rss_permalinks', !empty($request->getParam('enableRssPermalinks'))); + $this->container->conf->set('updates.check_updates', !empty($request->getParam('updateCheck'))); + $this->container->conf->set('privacy.hide_public_links', !empty($request->getParam('hidePublicLinks'))); + $this->container->conf->set('api.enabled', !empty($request->getParam('enableApi'))); + $this->container->conf->set('api.secret', escape($request->getParam('apiSecret'))); + $this->container->conf->set('formatter', escape($request->getParam('formatter'))); + + if (!empty($request->getParam('language'))) { + $this->container->conf->set('translation.language', escape($request->getParam('language'))); + } + + $thumbnailsMode = extension_loaded('gd') ? $request->getParam('enableThumbnails') : 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. ' + .'Please synchronize them.' + )); + } + $this->container->conf->set('thumbnails.mode', $thumbnailsMode); + + try { + $this->container->conf->write($this->container->loginManager->isLoggedIn()); + $this->container->history->updateSettings(); + $this->container->pageCacheManager->invalidateCaches(); + } catch (Throwable $e) { + // TODO: translation + stacktrace + $this->saveErrorMessage('ERROR while writing config file after configuration update.'); + } + + $this->saveSuccessMessage(t('Configuration was saved.')); + + return $response->withRedirect('./configure'); + } +} -- cgit v1.2.3 From 8eac2e54882d8adae8cbb45386dca1b465242632 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 30 May 2020 15:51:14 +0200 Subject: Process manage tags page through Slim controller --- .../front/controller/admin/ConfigureController.php | 2 +- .../front/controller/admin/ManageTagController.php | 87 ++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 application/front/controller/admin/ManageTagController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index b1d32270..5a482d8e 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -12,7 +12,7 @@ use Slim\Http\Response; use Throwable; /** - * Class PasswordController + * Class ConfigureController * * Slim controller used to handle Shaarli configuration page (display + save new config). */ diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php new file mode 100644 index 00000000..e015e613 --- /dev/null +++ b/application/front/controller/admin/ManageTagController.php @@ -0,0 +1,87 @@ +getParam('fromtag') ?? ''; + + $this->assignView('fromtag', escape($fromTag)); + $this->assignView( + 'pagetitle', + t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render('changetag')); + } + + /** + * POST /manage-tags - Update or delete provided tag + */ + public function save(Request $request, Response $response): Response + { + $this->checkToken($request); + + $isDelete = null !== $request->getParam('deletetag') && null === $request->getParam('renametag'); + + $fromTag = escape(trim($request->getParam('fromtag') ?? '')); + $toTag = escape(trim($request->getParam('totag') ?? '')); + + if (0 === strlen($fromTag) || false === $isDelete && 0 === strlen($toTag)) { + $this->saveWarningMessage(t('Invalid tags provided.')); + + return $response->withRedirect('./manage-tags'); + } + + // TODO: move this to bookmark service + $count = 0; + $bookmarks = $this->container->bookmarkService->search(['searchtags' => $fromTag], BookmarkFilter::$ALL, true); + foreach ($bookmarks as $bookmark) { + if (false === $isDelete) { + $bookmark->renameTag($fromTag, $toTag); + } else { + $bookmark->deleteTag($fromTag); + } + + $this->container->bookmarkService->set($bookmark, false); + $this->container->history->updateLink($bookmark); + $count++; + } + + $this->container->bookmarkService->save(); + + if (true === $isDelete) { + $alert = sprintf( + t('The tag was removed from %d bookmark.', 'The tag was removed from %d bookmarks.', $count), + $count + ); + } else { + $alert = sprintf( + t('The tag was renamed in %d bookmark.', 'The tag was renamed in %d bookmarks.', $count), + $count + ); + } + + $this->saveSuccessMessage($alert); + + $redirect = true === $isDelete ? './manage-tags' : './?searchtags='. urlencode($toTag); + + return $response->withRedirect($redirect); + } +} -- cgit v1.2.3 From c22fa57a5505fe95fd01860e3d3dfbb089f869cd Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 6 Jun 2020 14:01:03 +0200 Subject: Handle shaare creation/edition/deletion through Slim controllers --- .../controller/admin/PostBookmarkController.php | 258 +++++++++++++++++++++ .../front/controller/admin/ToolsController.php | 2 +- 2 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 application/front/controller/admin/PostBookmarkController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/PostBookmarkController.php b/application/front/controller/admin/PostBookmarkController.php new file mode 100644 index 00000000..dbe570e2 --- /dev/null +++ b/application/front/controller/admin/PostBookmarkController.php @@ -0,0 +1,258 @@ +assignView( + 'pagetitle', + t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render('addlink')); + } + + /** + * GET /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 (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') { + $title = mb_convert_encoding($title, 'utf-8', $charset); + } + } + + if (empty($url) && empty($title)) { + $title = $this->container->conf->get('general.default_note_title', t('Note: ')); + } + + $link = escape([ + 'title' => $title, + 'url' => $url ?? '', + 'description' => $description ?? '', + 'tags' => $tags ?? '', + 'private' => $private, + ]); + } else { + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $link = $formatter->format($bookmark); + } + + return $this->displayForm($link, $linkIsNew, $request, $response); + } + + /** + * GET /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($id); // Read database + } catch (BookmarkNotFoundException $e) { + $this->saveErrorMessage(t('Bookmark not found')); + + return $response->withRedirect('./'); + } + + $formatter = $this->container->formatterFactory->getFormatter('raw'); + $link = $formatter->format($bookmark); + + return $this->displayForm($link, false, $request, $response); + } + + /** + * POST /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') ? 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 + && false === $bookmark->isNote() + ) { + $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); + $data = $this->executeHooks('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, + ['add-shaare', 'shaare'], ['addlink', 'post', 'edit_link'], + $bookmark->getShortUrl() + ); + } + + public function deleteBookmark(Request $request, Response $response): Response + { + $this->checkToken($request); + + $ids = escape(trim($request->getParam('lf_linkdate'))); + if (strpos($ids, ' ') !== false) { + // multiple, space-separated ids provided + $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'strlen')); + } 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'); + foreach ($ids as $id) { + $id = (int) $id; + // TODO: check if it exists + $bookmark = $this->container->bookmarkService->get($id); + $data = $formatter->format($bookmark); + $this->container->pluginManager->executeHooks('delete_link', $data); + $this->container->bookmarkService->remove($bookmark, false); + } + + $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 $response->withRedirect('./'); + } + + 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 = [ + 'link' => $link, + 'link_is_new' => $isNew, + 'http_referer' => escape($this->container->environment['HTTP_REFERER'] ?? ''), + 'source' => $request->getParam('source') ?? '', + 'tags' => $tags, + 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), + ]; + + $data = $this->executeHooks('render_editlink', $data); + + 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('editlink')); + } + + /** + * @param mixed[] $data Variables passed to the template engine + * + * @return mixed[] Template data after active plugins render_picwall hook execution. + */ + protected function executeHooks(string $hook, array $data): array + { + $this->container->pluginManager->executeHooks( + $hook, + $data + ); + + return $data; + } +} diff --git a/application/front/controller/admin/ToolsController.php b/application/front/controller/admin/ToolsController.php index 66db5ad9..d087f2cd 100644 --- a/application/front/controller/admin/ToolsController.php +++ b/application/front/controller/admin/ToolsController.php @@ -21,7 +21,7 @@ class ToolsController extends ShaarliAdminController 'sslenabled' => is_https($this->container->environment), ]; - $this->executeHooks($data); + $data = $this->executeHooks($data); foreach ($data as $key => $value) { $this->assignView($key, $value); -- cgit v1.2.3 From 9c75f877935fa6adec951a4d8d32b328aaab314f Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 13 Jun 2020 13:08:01 +0200 Subject: Use multi-level routes for existing controllers instead of 1 level everywhere Also prefix most admin routes with /admin/ --- .../front/controller/admin/ConfigureController.php | 6 +++--- application/front/controller/admin/LogoutController.php | 4 ++-- .../front/controller/admin/ManageTagController.php | 10 +++++----- .../front/controller/admin/PasswordController.php | 4 ++-- .../front/controller/admin/PostBookmarkController.php | 17 ++++++++++------- 5 files changed, 22 insertions(+), 19 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index 5a482d8e..44971c43 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -19,7 +19,7 @@ use Throwable; class ConfigureController extends ShaarliAdminController { /** - * GET /configure - Displays the configuration page + * GET /admin/configure - Displays the configuration page */ public function index(Request $request, Response $response): Response { @@ -56,7 +56,7 @@ class ConfigureController extends ShaarliAdminController } /** - * POST /configure - Update Shaarli's configuration + * POST /admin/configure - Update Shaarli's configuration */ public function save(Request $request, Response $response): Response { @@ -115,6 +115,6 @@ class ConfigureController extends ShaarliAdminController $this->saveSuccessMessage(t('Configuration was saved.')); - return $response->withRedirect('./configure'); + return $this->redirect($response, '/admin/configure'); } } diff --git a/application/front/controller/admin/LogoutController.php b/application/front/controller/admin/LogoutController.php index 41e81984..c5984814 100644 --- a/application/front/controller/admin/LogoutController.php +++ b/application/front/controller/admin/LogoutController.php @@ -22,8 +22,8 @@ class LogoutController extends ShaarliAdminController $this->container->sessionManager->logout(); // TODO: switch to a simple Cookie manager allowing to check the session, and create mocks. - setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->webPath); + setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->basePath . '/'); - return $response->withRedirect('./'); + return $this->redirect($response, '/'); } } diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php index e015e613..7dab288a 100644 --- a/application/front/controller/admin/ManageTagController.php +++ b/application/front/controller/admin/ManageTagController.php @@ -16,7 +16,7 @@ use Slim\Http\Response; class ManageTagController extends ShaarliAdminController { /** - * GET /manage-tags - Displays the manage tags page + * GET /admin/tags - Displays the manage tags page */ public function index(Request $request, Response $response): Response { @@ -32,7 +32,7 @@ class ManageTagController extends ShaarliAdminController } /** - * POST /manage-tags - Update or delete provided tag + * POST /admin/tags - Update or delete provided tag */ public function save(Request $request, Response $response): Response { @@ -46,7 +46,7 @@ class ManageTagController extends ShaarliAdminController if (0 === strlen($fromTag) || false === $isDelete && 0 === strlen($toTag)) { $this->saveWarningMessage(t('Invalid tags provided.')); - return $response->withRedirect('./manage-tags'); + return $this->redirect($response, '/admin/tags'); } // TODO: move this to bookmark service @@ -80,8 +80,8 @@ class ManageTagController extends ShaarliAdminController $this->saveSuccessMessage($alert); - $redirect = true === $isDelete ? './manage-tags' : './?searchtags='. urlencode($toTag); + $redirect = true === $isDelete ? '/admin/tags' : '/?searchtags='. urlencode($toTag); - return $response->withRedirect($redirect); + return $this->redirect($response, $redirect); } } diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php index 6e8f0bcb..bcce01a6 100644 --- a/application/front/controller/admin/PasswordController.php +++ b/application/front/controller/admin/PasswordController.php @@ -29,7 +29,7 @@ class PasswordController extends ShaarliAdminController } /** - * GET /password - Displays the change password template + * GET /admin/password - Displays the change password template */ public function index(Request $request, Response $response): Response { @@ -37,7 +37,7 @@ class PasswordController extends ShaarliAdminController } /** - * POST /password - Change admin password - existing and new passwords need to be provided. + * POST /admin/password - Change admin password - existing and new passwords need to be provided. */ public function change(Request $request, Response $response): Response { diff --git a/application/front/controller/admin/PostBookmarkController.php b/application/front/controller/admin/PostBookmarkController.php index dbe570e2..f3ee5dea 100644 --- a/application/front/controller/admin/PostBookmarkController.php +++ b/application/front/controller/admin/PostBookmarkController.php @@ -19,7 +19,7 @@ use Slim\Http\Response; class PostBookmarkController extends ShaarliAdminController { /** - * GET /add-shaare - Displays the form used to create a new bookmark from an URL + * GET /admin/add-shaare - Displays the form used to create a new bookmark from an URL */ public function addShaare(Request $request, Response $response): Response { @@ -32,7 +32,7 @@ class PostBookmarkController extends ShaarliAdminController } /** - * GET /shaare - Displays the bookmark form for creation. + * 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 @@ -93,7 +93,7 @@ class PostBookmarkController extends ShaarliAdminController } /** - * GET /shaare-{id} - Displays the bookmark form in edition mode. + * GET /admin/shaare/{id} - Displays the bookmark form in edition mode. */ public function displayEditForm(Request $request, Response $response, array $args): Response { @@ -106,7 +106,7 @@ class PostBookmarkController extends ShaarliAdminController } catch (BookmarkNotFoundException $e) { $this->saveErrorMessage(t('Bookmark not found')); - return $response->withRedirect('./'); + return $this->redirect($response, '/'); } $formatter = $this->container->formatterFactory->getFormatter('raw'); @@ -116,7 +116,7 @@ class PostBookmarkController extends ShaarliAdminController } /** - * POST /shaare + * POST /admin/shaare */ public function save(Request $request, Response $response): Response { @@ -170,11 +170,14 @@ class PostBookmarkController extends ShaarliAdminController ); } + /** + * GET /admin/shaare/delete + */ public function deleteBookmark(Request $request, Response $response): Response { $this->checkToken($request); - $ids = escape(trim($request->getParam('lf_linkdate'))); + $ids = escape(trim($request->getParam('id'))); if (strpos($ids, ' ') !== false) { // multiple, space-separated ids provided $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'strlen')); @@ -207,7 +210,7 @@ class PostBookmarkController extends ShaarliAdminController } // Don't redirect to where we were previously because the datastore has changed. - return $response->withRedirect('./'); + return $this->redirect($response, '/'); } protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response -- cgit v1.2.3 From baa6979194573855b260593094983c33ec338dc7 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 13 Jun 2020 15:37:02 +0200 Subject: Improve ManageTagController coverage and error handling --- .../controller/admin/ManageShaareController.php | 281 +++++++++++++++++++++ .../controller/admin/PostBookmarkController.php | 261 ------------------- 2 files changed, 281 insertions(+), 261 deletions(-) create mode 100644 application/front/controller/admin/ManageShaareController.php delete mode 100644 application/front/controller/admin/PostBookmarkController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php new file mode 100644 index 00000000..620bbc40 --- /dev/null +++ b/application/front/controller/admin/ManageShaareController.php @@ -0,0 +1,281 @@ +assignView( + 'pagetitle', + t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render('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 (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') { + $title = mb_convert_encoding($title, 'utf-8', $charset); + } + } + + if (empty($url) && empty($title)) { + $title = $this->container->conf->get('general.default_note_title', t('Note: ')); + } + + $link = escape([ + 'title' => $title, + 'url' => $url ?? '', + 'description' => $description ?? '', + 'tags' => $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') ? 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 + && false === $bookmark->isNote() + ) { + $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); + $data = $this->executeHooks('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, + ['add-shaare', 'shaare'], ['addlink', 'post', 'edit_link'], + $bookmark->getShortUrl() + ); + } + + /** + * GET /admin/shaare/delete + */ + 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->container->pluginManager->executeHooks('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, '/'); + } + + /** + * 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 = [ + 'link' => $link, + 'link_is_new' => $isNew, + 'http_referer' => escape($this->container->environment['HTTP_REFERER'] ?? ''), + 'source' => $request->getParam('source') ?? '', + 'tags' => $tags, + 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), + ]; + + $data = $this->executeHooks('render_editlink', $data); + + 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('editlink')); + } + + /** + * @param mixed[] $data Variables passed to the template engine + * + * @return mixed[] Template data after active plugins render_picwall hook execution. + */ + protected function executeHooks(string $hook, array $data): array + { + $this->container->pluginManager->executeHooks( + $hook, + $data + ); + + return $data; + } +} diff --git a/application/front/controller/admin/PostBookmarkController.php b/application/front/controller/admin/PostBookmarkController.php deleted file mode 100644 index f3ee5dea..00000000 --- a/application/front/controller/admin/PostBookmarkController.php +++ /dev/null @@ -1,261 +0,0 @@ -assignView( - 'pagetitle', - t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') - ); - - return $response->write($this->render('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 (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') { - $title = mb_convert_encoding($title, 'utf-8', $charset); - } - } - - if (empty($url) && empty($title)) { - $title = $this->container->conf->get('general.default_note_title', t('Note: ')); - } - - $link = escape([ - 'title' => $title, - 'url' => $url ?? '', - 'description' => $description ?? '', - 'tags' => $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($id); // Read database - } catch (BookmarkNotFoundException $e) { - $this->saveErrorMessage(t('Bookmark not found')); - - 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') ? 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 - && false === $bookmark->isNote() - ) { - $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); - $data = $this->executeHooks('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, - ['add-shaare', 'shaare'], ['addlink', 'post', 'edit_link'], - $bookmark->getShortUrl() - ); - } - - /** - * GET /admin/shaare/delete - */ - public function deleteBookmark(Request $request, Response $response): Response - { - $this->checkToken($request); - - $ids = escape(trim($request->getParam('id'))); - if (strpos($ids, ' ') !== false) { - // multiple, space-separated ids provided - $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'strlen')); - } 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'); - foreach ($ids as $id) { - $id = (int) $id; - // TODO: check if it exists - $bookmark = $this->container->bookmarkService->get($id); - $data = $formatter->format($bookmark); - $this->container->pluginManager->executeHooks('delete_link', $data); - $this->container->bookmarkService->remove($bookmark, false); - } - - $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, '/'); - } - - 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 = [ - 'link' => $link, - 'link_is_new' => $isNew, - 'http_referer' => escape($this->container->environment['HTTP_REFERER'] ?? ''), - 'source' => $request->getParam('source') ?? '', - 'tags' => $tags, - 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), - ]; - - $data = $this->executeHooks('render_editlink', $data); - - 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('editlink')); - } - - /** - * @param mixed[] $data Variables passed to the template engine - * - * @return mixed[] Template data after active plugins render_picwall hook execution. - */ - protected function executeHooks(string $hook, array $data): array - { - $this->container->pluginManager->executeHooks( - $hook, - $data - ); - - return $data; - } -} -- cgit v1.2.3 From 7b8a6f2858248601d43c1b8247deb91b74392d2e Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 13 Jun 2020 19:40:32 +0200 Subject: Process change visibility action through Slim controller --- .../controller/admin/ManageShaareController.php | 70 +++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index 620bbc40..ff330a99 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -174,7 +174,7 @@ class ManageShaareController extends ShaarliAdminController } /** - * GET /admin/shaare/delete + * GET /admin/shaare/delete - Delete one or multiple bookmarks (depending on `id` query parameter). */ public function deleteBookmark(Request $request, Response $response): Response { @@ -228,6 +228,74 @@ class ManageShaareController extends ShaarliAdminController 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->container->pluginManager->executeHooks('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']); + } + /** * Helper function used to display the shaare form whether it's a new or existing bookmark. * -- cgit v1.2.3 From 3447d888d7881eed437117a6de2450abb96f6a76 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 15 Jun 2020 08:15:40 +0200 Subject: Pin bookmarks through Slim controller --- .../controller/admin/ManageShaareController.php | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index ff330a99..bdfc5ca7 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -296,6 +296,42 @@ class ManageShaareController extends ShaarliAdminController 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->container->pluginManager->executeHooks('save_link', $data); + $bookmark->fromArray($data); + + $this->container->bookmarkService->set($bookmark); + + return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); + } + /** * Helper function used to display the shaare form whether it's a new or existing bookmark. * -- cgit v1.2.3 From c70ff64a61d62cc8d35a62f30596ecc2a3c578a3 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 17 Jun 2020 16:04:18 +0200 Subject: Process bookmark exports through Slim controllers --- .../front/controller/admin/ExportController.php | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 application/front/controller/admin/ExportController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ExportController.php b/application/front/controller/admin/ExportController.php new file mode 100644 index 00000000..8e0e5a56 --- /dev/null +++ b/application/front/controller/admin/ExportController.php @@ -0,0 +1,92 @@ +assignView('pagetitle', t('Export') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + + return $response->write($this->render('export')); + } + + /** + * POST /admin/export - Process export, and serve download file named + * bookmarks_(all|private|public)_datetime.html + */ + public function export(Request $request, Response $response): Response + { + $selection = $request->getParam('selection'); + + if (empty($selection)) { + $this->saveErrorMessage(t('Please select an export mode.')); + + return $this->redirect($response, '/admin/export'); + } + + $prependNoteUrl = filter_var($request->getParam('prepend_note_url') ?? false, FILTER_VALIDATE_BOOLEAN); + + try { + $formatter = $this->container->formatterFactory->getFormatter('raw'); + + $this->assignView( + 'links', + $this->container->netscapeBookmarkUtils->filterAndFormat( + $formatter, + $selection, + $prependNoteUrl, + index_url($this->container->environment) + ) + ); + } catch (\Exception $exc) { + $this->saveErrorMessage($exc->getMessage()); + + return $this->redirect($response, '/admin/export'); + } + + $now = new DateTime(); + $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' + ); + + $this->assignView('date', $now->format(DateTime::RFC822)); + $this->assignView('eol', PHP_EOL); + $this->assignView('selection', $selection); + + return $response->write($this->render('export.bookmarks')); + } + + /** + * @param mixed[] $data Variables passed to the template engine + * + * @return mixed[] Template data after active plugins render_picwall hook execution. + */ + protected function executeHooks(array $data): array + { + $this->container->pluginManager->executeHooks( + 'render_tools', + $data + ); + + return $data; + } +} -- cgit v1.2.3 From 78657347c5b463d7c22bfc8c87b7db39fe058833 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 17 Jun 2020 19:08:02 +0200 Subject: Process bookmarks import through Slim controller --- .../front/controller/admin/ExportController.php | 17 +---- .../front/controller/admin/ImportController.php | 81 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 application/front/controller/admin/ImportController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ExportController.php b/application/front/controller/admin/ExportController.php index 8e0e5a56..7afbfc23 100644 --- a/application/front/controller/admin/ExportController.php +++ b/application/front/controller/admin/ExportController.php @@ -33,6 +33,8 @@ class ExportController extends ShaarliAdminController */ public function export(Request $request, Response $response): Response { + $this->checkToken($request); + $selection = $request->getParam('selection'); if (empty($selection)) { @@ -74,19 +76,4 @@ class ExportController extends ShaarliAdminController return $response->write($this->render('export.bookmarks')); } - - /** - * @param mixed[] $data Variables passed to the template engine - * - * @return mixed[] Template data after active plugins render_picwall hook execution. - */ - protected function executeHooks(array $data): array - { - $this->container->pluginManager->executeHooks( - 'render_tools', - $data - ); - - return $data; - } } diff --git a/application/front/controller/admin/ImportController.php b/application/front/controller/admin/ImportController.php new file mode 100644 index 00000000..8c5305b9 --- /dev/null +++ b/application/front/controller/admin/ImportController.php @@ -0,0 +1,81 @@ +assignView( + 'maxfilesize', + get_max_upload_size( + ini_get('post_max_size'), + ini_get('upload_max_filesize'), + false + ) + ); + $this->assignView( + 'maxfilesizeHuman', + get_max_upload_size( + ini_get('post_max_size'), + ini_get('upload_max_filesize'), + true + ) + ); + $this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli')); + + return $response->write($this->render('import')); + } + + /** + * POST /admin/import - Process import file provided and create bookmarks + */ + public function import(Request $request, Response $response): Response + { + $this->checkToken($request); + + $file = ($request->getUploadedFiles() ?? [])['filetoupload'] ?? null; + if (!$file instanceof UploadedFileInterface) { + $this->saveErrorMessage(t('No import file provided.')); + + return $this->redirect($response, '/admin/import'); + } + + + // Import bookmarks from an uploaded file + if (0 === $file->getSize()) { + // The file is too big or some form field may be missing. + $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.' + ), + get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')) + ); + $this->saveErrorMessage($msg); + + return $this->redirect($response, '/admin/import'); + } + + $status = $this->container->netscapeBookmarkUtils->import($request->getParams(), $file); + + $this->saveSuccessMessage($status); + + return $this->redirect($response, '/admin/import'); + } +} -- cgit v1.2.3 From 1b8620b1ad4e2c647ff2d032c8e7c6687b6647a1 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 20 Jun 2020 15:14:24 +0200 Subject: Process plugins administration page through Slim controllers --- .../front/controller/admin/PluginsController.php | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 application/front/controller/admin/PluginsController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php new file mode 100644 index 00000000..d5ec91f0 --- /dev/null +++ b/application/front/controller/admin/PluginsController.php @@ -0,0 +1,98 @@ +container->pluginManager->getPluginsMeta(); + + // Split plugins into 2 arrays: ordered enabled plugins and disabled. + $enabledPlugins = array_filter($pluginMeta, function ($v) { + return ($v['order'] ?? false) !== false; + }); + $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $this->container->conf->get('plugins', [])); + uasort( + $enabledPlugins, + function ($a, $b) { + return $a['order'] - $b['order']; + } + ); + $disabledPlugins = array_filter($pluginMeta, function ($v) { + return ($v['order'] ?? false) === false; + }); + + $this->assignView('enabledPlugins', $enabledPlugins); + $this->assignView('disabledPlugins', $disabledPlugins); + $this->assignView( + 'pagetitle', + t('Plugin Administration') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render('pluginsadmin')); + } + + /** + * POST /admin/plugins - Update Shaarli's configuration + */ + public function save(Request $request, Response $response): Response + { + $this->checkToken($request); + + try { + $parameters = $request->getParams() ?? []; + + $this->executeHooks($parameters); + + if (isset($parameters['parameters_form'])) { + unset($parameters['parameters_form']); + foreach ($parameters as $param => $value) { + $this->container->conf->set('plugins.'. $param, escape($value)); + } + } else { + $this->container->conf->set('general.enabled_plugins', save_plugin_config($parameters)); + } + + $this->container->conf->write($this->container->loginManager->isLoggedIn()); + $this->container->history->updateSettings(); + + $this->saveSuccessMessage(t('Setting successfully saved.')); + } catch (Exception $e) { + $this->saveErrorMessage( + t('ERROR while saving plugin configuration: ') . PHP_EOL . $e->getMessage() + ); + } + + return $this->redirect($response, '/admin/plugins'); + } + + /** + * @param mixed[] $data Variables passed to the template engine + * + * @return mixed[] Template data after active plugins render_picwall hook execution. + */ + protected function executeHooks(array $data): array + { + $this->container->pluginManager->executeHooks( + 'save_plugin_parameters', + $data + ); + + return $data; + } +} -- cgit v1.2.3 From 764d34a7d347d653414e5f5c632e02499edaef04 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 21 Jun 2020 12:21:31 +0200 Subject: Process token retrieve through Slim controller --- .../front/controller/admin/TokenController.php | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 application/front/controller/admin/TokenController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/TokenController.php b/application/front/controller/admin/TokenController.php new file mode 100644 index 00000000..08d68d0a --- /dev/null +++ b/application/front/controller/admin/TokenController.php @@ -0,0 +1,26 @@ +withHeader('Content-Type', 'text/plain'); + + return $response->write($this->container->sessionManager->generateToken()); + } +} -- cgit v1.2.3 From 6132d64748dfc6806ed25f71d2e078a5ed29d071 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 27 Jun 2020 12:08:26 +0200 Subject: Process thumbnail synchronize page through Slim controllers --- .../front/controller/admin/ConfigureController.php | 2 +- .../controller/admin/ThumbnailsController.php | 79 ++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 application/front/controller/admin/ThumbnailsController.php (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index 44971c43..201a859b 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -99,7 +99,7 @@ class ConfigureController extends ShaarliAdminController ) { $this->saveWarningMessage(t( 'You have enabled or changed thumbnails mode. ' - .'Please synchronize them.' + .'Please synchronize them.' )); } $this->container->conf->set('thumbnails.mode', $thumbnailsMode); diff --git a/application/front/controller/admin/ThumbnailsController.php b/application/front/controller/admin/ThumbnailsController.php new file mode 100644 index 00000000..e5308510 --- /dev/null +++ b/application/front/controller/admin/ThumbnailsController.php @@ -0,0 +1,79 @@ +container->bookmarkService->search() as $bookmark) { + // A note or not HTTP(S) + if ($bookmark->isNote() || !startsWith(strtolower($bookmark->getUrl()), 'http')) { + continue; + } + + $ids[] = $bookmark->getId(); + } + + $this->assignView('ids', $ids); + $this->assignView( + 'pagetitle', + t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + + return $response->write($this->render('thumbnails')); + } + + /** + * PATCH /admin/shaare/{id}/thumbnail-update - Route for AJAX calls + */ + public function ajaxUpdate(Request $request, Response $response, array $args): Response + { + $id = $args['id'] ?? null; + + if (false === ctype_digit($id)) { + return $response->withStatus(400); + } + + try { + $bookmark = $this->container->bookmarkService->get($id); + } catch (BookmarkNotFoundException $e) { + return $response->withStatus(404); + } + + $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); + $this->container->bookmarkService->set($bookmark); + + return $response->withJson($this->container->formatterFactory->getFormatter('raw')->format($bookmark)); + } + + /** + * @param mixed[] $data Variables passed to the template engine + * + * @return mixed[] Template data after active plugins render_picwall hook execution. + */ + protected function executeHooks(array $data): array + { + $this->container->pluginManager->executeHooks( + 'render_tools', + $data + ); + + return $data; + } +} -- cgit v1.2.3 From 1a8ac737e52cb25a5c346232ee398f5908cee7d7 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 6 Jul 2020 08:04:35 +0200 Subject: Process main page (linklist) through Slim controller Including a bunch of improvements on the container, and helper used across new controllers. --- .../front/controller/admin/ConfigureController.php | 3 ++- .../front/controller/admin/ExportController.php | 5 +++-- .../front/controller/admin/ImportController.php | 3 ++- .../front/controller/admin/ManageShaareController.php | 5 +++-- .../front/controller/admin/ManageTagController.php | 3 ++- .../front/controller/admin/PasswordController.php | 9 +++++---- .../front/controller/admin/PluginsController.php | 3 ++- .../front/controller/admin/ThumbnailsController.php | 18 ++---------------- application/front/controller/admin/ToolsController.php | 3 ++- 9 files changed, 23 insertions(+), 29 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index 201a859b..865fc2b0 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; use Shaarli\Languages; +use Shaarli\Render\TemplatePage; use Shaarli\Render\ThemeUtils; use Shaarli\Thumbnailer; use Slim\Http\Request; @@ -52,7 +53,7 @@ class ConfigureController extends ShaarliAdminController $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')); - return $response->write($this->render('configure')); + return $response->write($this->render(TemplatePage::CONFIGURE)); } /** diff --git a/application/front/controller/admin/ExportController.php b/application/front/controller/admin/ExportController.php index 7afbfc23..2be957fa 100644 --- a/application/front/controller/admin/ExportController.php +++ b/application/front/controller/admin/ExportController.php @@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Admin; use DateTime; use Shaarli\Bookmark\Bookmark; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -24,7 +25,7 @@ class ExportController extends ShaarliAdminController { $this->assignView('pagetitle', t('Export') .' - '. $this->container->conf->get('general.title', 'Shaarli')); - return $response->write($this->render('export')); + return $response->write($this->render(TemplatePage::EXPORT)); } /** @@ -74,6 +75,6 @@ class ExportController extends ShaarliAdminController $this->assignView('eol', PHP_EOL); $this->assignView('selection', $selection); - return $response->write($this->render('export.bookmarks')); + return $response->write($this->render(TemplatePage::NETSCAPE_EXPORT_BOOKMARKS)); } } diff --git a/application/front/controller/admin/ImportController.php b/application/front/controller/admin/ImportController.php index 8c5305b9..758d5ef9 100644 --- a/application/front/controller/admin/ImportController.php +++ b/application/front/controller/admin/ImportController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; use Psr\Http\Message\UploadedFileInterface; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -39,7 +40,7 @@ class ImportController extends ShaarliAdminController ); $this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli')); - return $response->write($this->render('import')); + return $response->write($this->render(TemplatePage::IMPORT)); } /** diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index bdfc5ca7..3aa48423 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Bookmark\Bookmark; use Shaarli\Bookmark\Exception\BookmarkNotFoundException; use Shaarli\Formatter\BookmarkMarkdownFormatter; +use Shaarli\Render\TemplatePage; use Shaarli\Thumbnailer; use Slim\Http\Request; use Slim\Http\Response; @@ -28,7 +29,7 @@ class ManageShaareController extends ShaarliAdminController t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') ); - return $response->write($this->render('addlink')); + return $response->write($this->render(TemplatePage::ADDLINK)); } /** @@ -365,7 +366,7 @@ class ManageShaareController extends ShaarliAdminController $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') ); - return $response->write($this->render('editlink')); + return $response->write($this->render(TemplatePage::EDIT_LINK)); } /** diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php index 7dab288a..0380ef1f 100644 --- a/application/front/controller/admin/ManageTagController.php +++ b/application/front/controller/admin/ManageTagController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; use Shaarli\Bookmark\BookmarkFilter; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -28,7 +29,7 @@ class ManageTagController extends ShaarliAdminController t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli') ); - return $response->write($this->render('changetag')); + return $response->write($this->render(TemplatePage::CHANGE_TAG)); } /** diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php index bcce01a6..5ec0d24b 100644 --- a/application/front/controller/admin/PasswordController.php +++ b/application/front/controller/admin/PasswordController.php @@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Container\ShaarliContainer; use Shaarli\Front\Exception\OpenShaarliPasswordException; use Shaarli\Front\Exception\ShaarliFrontException; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; use Throwable; @@ -33,7 +34,7 @@ class PasswordController extends ShaarliAdminController */ public function index(Request $request, Response $response): Response { - return $response->write($this->render('changepassword')); + return $response->write($this->render(TemplatePage::CHANGE_PASSWORD)); } /** @@ -55,7 +56,7 @@ class PasswordController extends ShaarliAdminController return $response ->withStatus(400) - ->write($this->render('changepassword')) + ->write($this->render(TemplatePage::CHANGE_PASSWORD)) ; } @@ -71,7 +72,7 @@ class PasswordController extends ShaarliAdminController return $response ->withStatus(400) - ->write($this->render('changepassword')) + ->write($this->render(TemplatePage::CHANGE_PASSWORD)) ; } @@ -95,6 +96,6 @@ class PasswordController extends ShaarliAdminController $this->saveSuccessMessage(t('Your password has been changed')); - return $response->write($this->render('changepassword')); + return $response->write($this->render(TemplatePage::CHANGE_PASSWORD)); } } diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php index d5ec91f0..44025395 100644 --- a/application/front/controller/admin/PluginsController.php +++ b/application/front/controller/admin/PluginsController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; use Exception; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -44,7 +45,7 @@ class PluginsController extends ShaarliAdminController t('Plugin Administration') .' - '. $this->container->conf->get('general.title', 'Shaarli') ); - return $response->write($this->render('pluginsadmin')); + return $response->write($this->render(TemplatePage::PLUGINS_ADMIN)); } /** diff --git a/application/front/controller/admin/ThumbnailsController.php b/application/front/controller/admin/ThumbnailsController.php index e5308510..81c87ed0 100644 --- a/application/front/controller/admin/ThumbnailsController.php +++ b/application/front/controller/admin/ThumbnailsController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; use Shaarli\Bookmark\Exception\BookmarkNotFoundException; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -36,7 +37,7 @@ class ThumbnailsController extends ShaarliAdminController t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli') ); - return $response->write($this->render('thumbnails')); + return $response->write($this->render(TemplatePage::THUMBNAILS)); } /** @@ -61,19 +62,4 @@ class ThumbnailsController extends ShaarliAdminController return $response->withJson($this->container->formatterFactory->getFormatter('raw')->format($bookmark)); } - - /** - * @param mixed[] $data Variables passed to the template engine - * - * @return mixed[] Template data after active plugins render_picwall hook execution. - */ - protected function executeHooks(array $data): array - { - $this->container->pluginManager->executeHooks( - 'render_tools', - $data - ); - - return $data; - } } diff --git a/application/front/controller/admin/ToolsController.php b/application/front/controller/admin/ToolsController.php index d087f2cd..a476e898 100644 --- a/application/front/controller/admin/ToolsController.php +++ b/application/front/controller/admin/ToolsController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; +use Shaarli\Render\TemplatePage; use Slim\Http\Request; use Slim\Http\Response; @@ -29,7 +30,7 @@ class ToolsController extends ShaarliAdminController $this->assignView('pagetitle', t('Tools') .' - '. $this->container->conf->get('general.title', 'Shaarli')); - return $response->write($this->render('tools')); + return $response->write($this->render(TemplatePage::TOOLS)); } /** -- cgit v1.2.3 From c4ad3d4f061d05a01db25aa54dda830ba776792d Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 7 Jul 2020 10:15:56 +0200 Subject: Process Shaarli install through Slim controller --- application/front/controller/admin/LogoutController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/LogoutController.php b/application/front/controller/admin/LogoutController.php index c5984814..28165129 100644 --- a/application/front/controller/admin/LogoutController.php +++ b/application/front/controller/admin/LogoutController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Admin; +use Shaarli\Security\CookieManager; use Shaarli\Security\LoginManager; use Slim\Http\Request; use Slim\Http\Response; @@ -20,9 +21,12 @@ class LogoutController extends ShaarliAdminController { $this->container->pageCacheManager->invalidateCaches(); $this->container->sessionManager->logout(); - - // TODO: switch to a simple Cookie manager allowing to check the session, and create mocks. - setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->basePath . '/'); + $this->container->cookieManager->setCookieParameter( + CookieManager::STAY_SIGNED_IN, + 'false', + 0, + $this->container->basePath . '/' + ); return $this->redirect($response, '/'); } -- cgit v1.2.3 From 3ee8351e438f13ccf36062ce956e0b4a4d5f4a29 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 23 Jul 2020 16:41:32 +0200 Subject: Multiple small fixes --- .../front/controller/admin/ConfigureController.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php index 865fc2b0..e675fcca 100644 --- a/application/front/controller/admin/ConfigureController.php +++ b/application/front/controller/admin/ConfigureController.php @@ -98,10 +98,10 @@ class ConfigureController extends ShaarliAdminController 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. ' - .'Please synchronize them.' - )); + $this->saveWarningMessage( + t('You have enabled or changed thumbnails mode.') . + '' . t('Please synchronize them.') .'' + ); } $this->container->conf->set('thumbnails.mode', $thumbnailsMode); @@ -110,8 +110,13 @@ class ConfigureController extends ShaarliAdminController $this->container->history->updateSettings(); $this->container->pageCacheManager->invalidateCaches(); } catch (Throwable $e) { - // TODO: translation + stacktrace - $this->saveErrorMessage('ERROR while writing config file after configuration update.'); + $this->assignView('message', t('Error while writing config file after configuration update.')); + + if ($this->container->conf->get('dev.debug', false)) { + $this->assignView('stacktrace', $e->getMessage() . PHP_EOL . $e->getTraceAsString()); + } + + return $response->write($this->render('error')); } $this->saveSuccessMessage(t('Configuration was saved.')); -- cgit v1.2.3 From 8e9169cebaf5697344cb373d69fe429e8e0efd5d Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 23 Jul 2020 17:13:22 +0200 Subject: Update French translation --- application/front/controller/admin/PluginsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php index 44025395..1eb7e635 100644 --- a/application/front/controller/admin/PluginsController.php +++ b/application/front/controller/admin/PluginsController.php @@ -75,7 +75,7 @@ class PluginsController extends ShaarliAdminController $this->saveSuccessMessage(t('Setting successfully saved.')); } catch (Exception $e) { $this->saveErrorMessage( - t('ERROR while saving plugin configuration: ') . PHP_EOL . $e->getMessage() + t('Error while saving plugin configuration: ') . PHP_EOL . $e->getMessage() ); } -- cgit v1.2.3 From 204035bd3c91b9a5c39fcb6fc470e108b032dbd9 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 24 Jul 2020 12:48:53 +0200 Subject: Fix: visitor are allowed to chose nb of links per page --- .../controller/admin/SessionFilterController.php | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/SessionFilterController.php b/application/front/controller/admin/SessionFilterController.php index 69a16ec3..081c0ba0 100644 --- a/application/front/controller/admin/SessionFilterController.php +++ b/application/front/controller/admin/SessionFilterController.php @@ -12,28 +12,10 @@ use Slim\Http\Response; /** * Class SessionFilterController * - * Slim controller used to handle filters stored in the user session, such as visibility, links per page, etc. + * Slim controller used to handle filters stored in the user session, such as visibility, etc. */ class SessionFilterController extends ShaarliAdminController { - /** - * GET /links-per-page: set the number of bookmarks to display per page in homepage - */ - public function linksPerPage(Request $request, Response $response): Response - { - $linksPerPage = $request->getParam('nb') ?? null; - if (null === $linksPerPage || false === is_numeric($linksPerPage)) { - $linksPerPage = $this->container->conf->get('general.links_per_page', 20); - } - - $this->container->sessionManager->setSessionParameter( - SessionManager::KEY_LINKS_PER_PAGE, - abs(intval($linksPerPage)) - ); - - return $this->redirectFromReferer($request, $response, ['linksperpage'], ['nb']); - } - /** * GET /visibility: allows to display only public or only private bookmarks in linklist */ -- cgit v1.2.3 From 9fbc42294e7667c5ef19cafa0d1fcfbc1c0f36a9 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 26 Jul 2020 14:43:10 +0200 Subject: New basePath: fix officiel plugin paths and vintage template --- .../controller/admin/ManageShaareController.php | 25 +++++----------------- .../front/controller/admin/PluginsController.php | 17 +-------------- .../front/controller/admin/ToolsController.php | 17 +-------------- 3 files changed, 7 insertions(+), 52 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index 3aa48423..33e1188e 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php @@ -152,7 +152,7 @@ class ManageShaareController extends ShaarliAdminController // To preserve backward compatibility with 3rd parties, plugins still use arrays $formatter = $this->container->formatterFactory->getFormatter('raw'); $data = $formatter->format($bookmark); - $data = $this->executeHooks('save_link', $data); + $this->executePageHooks('save_link', $data); $bookmark->fromArray($data); $this->container->bookmarkService->set($bookmark); @@ -211,7 +211,7 @@ class ManageShaareController extends ShaarliAdminController } $data = $formatter->format($bookmark); - $this->container->pluginManager->executeHooks('delete_link', $data); + $this->executePageHooks('delete_link', $data); $this->container->bookmarkService->remove($bookmark, false); ++ $count; } @@ -283,7 +283,7 @@ class ManageShaareController extends ShaarliAdminController // To preserve backward compatibility with 3rd parties, plugins still use arrays $data = $formatter->format($bookmark); - $this->container->pluginManager->executeHooks('save_link', $data); + $this->executePageHooks('save_link', $data); $bookmark->fromArray($data); $this->container->bookmarkService->set($bookmark, false); @@ -325,7 +325,7 @@ class ManageShaareController extends ShaarliAdminController // To preserve backward compatibility with 3rd parties, plugins still use arrays $data = $formatter->format($bookmark); - $this->container->pluginManager->executeHooks('save_link', $data); + $this->executePageHooks('save_link', $data); $bookmark->fromArray($data); $this->container->bookmarkService->set($bookmark); @@ -354,7 +354,7 @@ class ManageShaareController extends ShaarliAdminController 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), ]; - $data = $this->executeHooks('render_editlink', $data); + $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); foreach ($data as $key => $value) { $this->assignView($key, $value); @@ -368,19 +368,4 @@ class ManageShaareController extends ShaarliAdminController return $response->write($this->render(TemplatePage::EDIT_LINK)); } - - /** - * @param mixed[] $data Variables passed to the template engine - * - * @return mixed[] Template data after active plugins render_picwall hook execution. - */ - protected function executeHooks(string $hook, array $data): array - { - $this->container->pluginManager->executeHooks( - $hook, - $data - ); - - return $data; - } } diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php index 1eb7e635..0e09116e 100644 --- a/application/front/controller/admin/PluginsController.php +++ b/application/front/controller/admin/PluginsController.php @@ -58,7 +58,7 @@ class PluginsController extends ShaarliAdminController try { $parameters = $request->getParams() ?? []; - $this->executeHooks($parameters); + $this->executePageHooks('save_plugin_parameters', $parameters); if (isset($parameters['parameters_form'])) { unset($parameters['parameters_form']); @@ -81,19 +81,4 @@ class PluginsController extends ShaarliAdminController return $this->redirect($response, '/admin/plugins'); } - - /** - * @param mixed[] $data Variables passed to the template engine - * - * @return mixed[] Template data after active plugins render_picwall hook execution. - */ - protected function executeHooks(array $data): array - { - $this->container->pluginManager->executeHooks( - 'save_plugin_parameters', - $data - ); - - return $data; - } } diff --git a/application/front/controller/admin/ToolsController.php b/application/front/controller/admin/ToolsController.php index a476e898..a87f20d2 100644 --- a/application/front/controller/admin/ToolsController.php +++ b/application/front/controller/admin/ToolsController.php @@ -22,7 +22,7 @@ class ToolsController extends ShaarliAdminController 'sslenabled' => is_https($this->container->environment), ]; - $data = $this->executeHooks($data); + $this->executePageHooks('render_tools', $data, TemplatePage::TOOLS); foreach ($data as $key => $value) { $this->assignView($key, $value); @@ -32,19 +32,4 @@ class ToolsController extends ShaarliAdminController return $response->write($this->render(TemplatePage::TOOLS)); } - - /** - * @param mixed[] $data Variables passed to the template engine - * - * @return mixed[] Template data after active plugins render_picwall hook execution. - */ - protected function executeHooks(array $data): array - { - $this->container->pluginManager->executeHooks( - 'render_tools', - $data - ); - - return $data; - } } -- cgit v1.2.3 From bedbb845eec20363b928b424143787dbe988eefe Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 13 Aug 2020 11:08:13 +0200 Subject: Move all admin controller into a dedicated group Also handle authentication check in a new middleware for the admin group. --- .../front/controller/admin/SessionFilterController.php | 13 +------------ .../front/controller/admin/ShaarliAdminController.php | 9 --------- 2 files changed, 1 insertion(+), 21 deletions(-) (limited to 'application/front/controller/admin') diff --git a/application/front/controller/admin/SessionFilterController.php b/application/front/controller/admin/SessionFilterController.php index 081c0ba0..d9a7a2e0 100644 --- a/application/front/controller/admin/SessionFilterController.php +++ b/application/front/controller/admin/SessionFilterController.php @@ -17,7 +17,7 @@ use Slim\Http\Response; class SessionFilterController extends ShaarliAdminController { /** - * GET /visibility: allows to display only public or only private bookmarks in linklist + * GET /admin/visibility: allows to display only public or only private bookmarks in linklist */ public function visibility(Request $request, Response $response, array $args): Response { @@ -46,16 +46,5 @@ class SessionFilterController extends ShaarliAdminController return $this->redirectFromReferer($request, $response, ['visibility']); } - /** - * GET /untagged-only: allows to display only bookmarks without any tag - */ - public function untaggedOnly(Request $request, Response $response): Response - { - $this->container->sessionManager->setSessionParameter( - SessionManager::KEY_UNTAGGED_ONLY, - empty($this->container->sessionManager->getSessionParameter(SessionManager::KEY_UNTAGGED_ONLY)) - ); - return $this->redirectFromReferer($request, $response, ['untaggedonly', 'untagged-only']); - } } diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php index 3bc5bb6b..3b5939bb 100644 --- a/application/front/controller/admin/ShaarliAdminController.php +++ b/application/front/controller/admin/ShaarliAdminController.php @@ -22,15 +22,6 @@ use Slim\Http\Request; */ abstract class ShaarliAdminController extends ShaarliVisitorController { - public function __construct(ShaarliContainer $container) - { - parent::__construct($container); - - if (true !== $this->container->loginManager->isLoggedIn()) { - throw new UnauthorizedException(); - } - } - /** * Any persistent action to the config or data store must check the XSRF token validity. */ -- cgit v1.2.3