2020-01-18 17:50:11 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
2020-05-22 13:20:31 +02:00
|
|
|
namespace Shaarli\Front\Controller\Visitor;
|
2020-01-18 17:50:11 +01:00
|
|
|
|
2020-01-23 20:06:32 +01:00
|
|
|
use Shaarli\Bookmark\BookmarkFilter;
|
2020-01-18 17:50:11 +01:00
|
|
|
use Shaarli\Container\ShaarliContainer;
|
2020-05-22 13:20:31 +02:00
|
|
|
use Slim\Http\Request;
|
2020-05-22 11:02:56 +02:00
|
|
|
use Slim\Http\Response;
|
2020-01-18 17:50:11 +01:00
|
|
|
|
2020-05-27 13:35:48 +02:00
|
|
|
/**
|
|
|
|
* Class ShaarliVisitorController
|
|
|
|
*
|
|
|
|
* All controllers accessible by visitors (non logged in users) should extend this abstract class.
|
|
|
|
* Contains a few helper function for template rendering, plugins, etc.
|
|
|
|
*
|
|
|
|
* @package Shaarli\Front\Controller\Visitor
|
|
|
|
*/
|
2020-05-22 13:20:31 +02:00
|
|
|
abstract class ShaarliVisitorController
|
2020-01-18 17:50:11 +01:00
|
|
|
{
|
|
|
|
/** @var ShaarliContainer */
|
2020-01-26 09:06:13 +01:00
|
|
|
protected $container;
|
2020-01-18 17:50:11 +01:00
|
|
|
|
2020-01-26 09:06:13 +01:00
|
|
|
/** @param ShaarliContainer $container Slim container (extended for attribute completion). */
|
|
|
|
public function __construct(ShaarliContainer $container)
|
2020-01-18 17:50:11 +01:00
|
|
|
{
|
2020-01-26 09:06:13 +01:00
|
|
|
$this->container = $container;
|
2020-01-18 17:50:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assign variables to RainTPL template through the PageBuilder.
|
|
|
|
*
|
|
|
|
* @param mixed $value Value to assign to the template
|
|
|
|
*/
|
|
|
|
protected function assignView(string $name, $value): self
|
|
|
|
{
|
2020-01-26 09:06:13 +01:00
|
|
|
$this->container->pageBuilder->assign($name, $value);
|
2020-01-18 17:50:11 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
2020-01-23 20:06:32 +01:00
|
|
|
|
2020-05-18 17:17:36 +02:00
|
|
|
/**
|
|
|
|
* Assign variables to RainTPL template through the PageBuilder.
|
|
|
|
*
|
|
|
|
* @param mixed $data Values to assign to the template and their keys
|
|
|
|
*/
|
|
|
|
protected function assignAllView(array $data): self
|
|
|
|
{
|
|
|
|
foreach ($data as $key => $value) {
|
|
|
|
$this->assignView($key, $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2020-01-23 20:06:32 +01:00
|
|
|
protected function render(string $template): string
|
|
|
|
{
|
2021-01-19 10:34:11 +01:00
|
|
|
// Legacy key that used to be injected by PluginManager
|
|
|
|
$this->assignView('_PAGE_', $template);
|
|
|
|
$this->assignView('template', $template);
|
|
|
|
|
2020-01-26 09:06:13 +01:00
|
|
|
$this->assignView('linkcount', $this->container->bookmarkService->count(BookmarkFilter::$ALL));
|
|
|
|
$this->assignView('privateLinkcount', $this->container->bookmarkService->count(BookmarkFilter::$PRIVATE));
|
2020-01-23 20:06:32 +01:00
|
|
|
|
|
|
|
$this->executeDefaultHooks($template);
|
|
|
|
|
2020-08-27 12:04:36 +02:00
|
|
|
$this->assignView('plugin_errors', $this->container->pluginManager->getErrors());
|
|
|
|
|
2020-07-26 14:43:10 +02:00
|
|
|
return $this->container->pageBuilder->render($template, $this->container->basePath);
|
2020-01-23 20:06:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call plugin hooks for header, footer and includes, specifying which page will be rendered.
|
|
|
|
* Then assign generated data to RainTPL.
|
|
|
|
*/
|
|
|
|
protected function executeDefaultHooks(string $template): void
|
|
|
|
{
|
|
|
|
$common_hooks = [
|
|
|
|
'includes',
|
|
|
|
'header',
|
|
|
|
'footer',
|
|
|
|
];
|
|
|
|
|
2020-09-01 13:33:50 +02:00
|
|
|
$parameters = $this->buildPluginParameters($template);
|
|
|
|
|
2020-01-23 20:06:32 +01:00
|
|
|
foreach ($common_hooks as $name) {
|
2020-06-06 14:01:03 +02:00
|
|
|
$pluginData = [];
|
2020-01-26 09:06:13 +01:00
|
|
|
$this->container->pluginManager->executeHooks(
|
2020-01-23 20:06:32 +01:00
|
|
|
'render_' . $name,
|
2020-06-06 14:01:03 +02:00
|
|
|
$pluginData,
|
2020-09-01 13:33:50 +02:00
|
|
|
$parameters
|
2020-01-23 20:06:32 +01:00
|
|
|
);
|
2020-06-06 14:01:03 +02:00
|
|
|
$this->assignView('plugins_' . $name, $pluginData);
|
2020-01-23 20:06:32 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-22 11:02:56 +02:00
|
|
|
|
2020-07-26 14:43:10 +02:00
|
|
|
protected function executePageHooks(string $hook, array &$data, string $template = null): void
|
|
|
|
{
|
|
|
|
$this->container->pluginManager->executeHooks(
|
|
|
|
$hook,
|
|
|
|
$data,
|
2020-09-01 13:33:50 +02:00
|
|
|
$this->buildPluginParameters($template)
|
2020-07-26 14:43:10 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:33:50 +02:00
|
|
|
protected function buildPluginParameters(?string $template): array
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'target' => $template,
|
|
|
|
'loggedin' => $this->container->loginManager->isLoggedIn(),
|
|
|
|
'basePath' => $this->container->basePath,
|
2020-10-16 13:06:06 +02:00
|
|
|
'rootPath' => preg_replace('#/index\.php$#', '', $this->container->basePath),
|
2020-09-01 13:33:50 +02:00
|
|
|
'bookmarkService' => $this->container->bookmarkService
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2020-06-13 13:08:01 +02:00
|
|
|
/**
|
|
|
|
* Simple helper which prepend the base path to redirect path.
|
|
|
|
*
|
|
|
|
* @param Response $response
|
|
|
|
* @param string $path Absolute path, e.g.: `/`, or `/admin/shaare/123` regardless of install directory
|
|
|
|
*
|
|
|
|
* @return Response updated
|
|
|
|
*/
|
|
|
|
protected function redirect(Response $response, string $path): Response
|
|
|
|
{
|
|
|
|
return $response->withRedirect($this->container->basePath . $path);
|
|
|
|
}
|
|
|
|
|
2020-05-22 11:02:56 +02:00
|
|
|
/**
|
|
|
|
* Generates a redirection to the previous page, based on the HTTP_REFERER.
|
|
|
|
* It fails back to the home page.
|
|
|
|
*
|
|
|
|
* @param array $loopTerms Terms to remove from path and query string to prevent direction loop.
|
|
|
|
* @param array $clearParams List of parameter to remove from the query string of the referrer.
|
|
|
|
*/
|
2020-05-22 13:20:31 +02:00
|
|
|
protected function redirectFromReferer(
|
|
|
|
Request $request,
|
|
|
|
Response $response,
|
|
|
|
array $loopTerms = [],
|
2020-06-06 14:01:03 +02:00
|
|
|
array $clearParams = [],
|
|
|
|
string $anchor = null
|
2020-05-22 13:20:31 +02:00
|
|
|
): Response {
|
2020-06-13 11:22:14 +02:00
|
|
|
$defaultPath = $this->container->basePath . '/';
|
2020-05-22 11:02:56 +02:00
|
|
|
$referer = $this->container->environment['HTTP_REFERER'] ?? null;
|
|
|
|
|
|
|
|
if (null !== $referer) {
|
|
|
|
$currentUrl = parse_url($referer);
|
2020-09-22 15:17:13 +02:00
|
|
|
// If the referer is not related to Shaarli instance, redirect to default
|
2020-09-22 20:25:47 +02:00
|
|
|
if (
|
|
|
|
isset($currentUrl['host'])
|
2020-09-22 15:17:13 +02:00
|
|
|
&& strpos(index_url($this->container->environment), $currentUrl['host']) === false
|
|
|
|
) {
|
|
|
|
return $response->withRedirect($defaultPath);
|
|
|
|
}
|
|
|
|
|
2020-05-22 11:02:56 +02:00
|
|
|
parse_str($currentUrl['query'] ?? '', $params);
|
|
|
|
$path = $currentUrl['path'] ?? $defaultPath;
|
|
|
|
} else {
|
|
|
|
$params = [];
|
|
|
|
$path = $defaultPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prevent redirection loop
|
|
|
|
if (isset($currentUrl)) {
|
|
|
|
foreach ($clearParams as $value) {
|
|
|
|
unset($params[$value]);
|
|
|
|
}
|
|
|
|
|
|
|
|
$checkQuery = implode('', array_keys($params));
|
|
|
|
foreach ($loopTerms as $value) {
|
|
|
|
if (strpos($path . $checkQuery, $value) !== false) {
|
|
|
|
$params = [];
|
|
|
|
$path = $defaultPath;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 20:25:47 +02:00
|
|
|
$queryString = count($params) > 0 ? '?' . http_build_query($params) : '';
|
2020-06-06 14:01:03 +02:00
|
|
|
$anchor = $anchor ? '#' . $anchor : '';
|
2020-05-22 11:02:56 +02:00
|
|
|
|
2020-06-06 14:01:03 +02:00
|
|
|
return $response->withRedirect($path . $queryString . $anchor);
|
2020-05-22 11:02:56 +02:00
|
|
|
}
|
2020-01-18 17:50:11 +01:00
|
|
|
}
|