New basePath: fix officiel plugin paths and vintage template

This commit is contained in:
ArthurHoaro 2020-07-26 14:43:10 +02:00
parent bc583903ad
commit 9fbc42294e
39 changed files with 169 additions and 244 deletions

View file

@ -1,6 +1,7 @@
<?php <?php
use Shaarli\Config\Exception\PluginConfigOrderException; use Shaarli\Config\Exception\PluginConfigOrderException;
use Shaarli\Plugin\PluginManager;
/** /**
* Plugin configuration helper functions. * Plugin configuration helper functions.
@ -19,6 +20,20 @@
*/ */
function save_plugin_config($formData) function save_plugin_config($formData)
{ {
// We can only save existing plugins
$directories = str_replace(
PluginManager::$PLUGINS_PATH . '/',
'',
glob(PluginManager::$PLUGINS_PATH . '/*')
);
$formData = array_filter(
$formData,
function ($value, string $key) use ($directories) {
return startsWith($key, 'order') || in_array($key, $directories);
},
ARRAY_FILTER_USE_BOTH
);
// Make sure there are no duplicates in orders. // Make sure there are no duplicates in orders.
if (!validate_plugin_order($formData)) { if (!validate_plugin_order($formData)) {
throw new PluginConfigOrderException(); throw new PluginConfigOrderException();
@ -69,7 +84,7 @@ function validate_plugin_order($formData)
$orders = array(); $orders = array();
foreach ($formData as $key => $value) { foreach ($formData as $key => $value) {
// No duplicate order allowed. // No duplicate order allowed.
if (in_array($value, $orders)) { if (in_array($value, $orders, true)) {
return false; return false;
} }

View file

@ -60,7 +60,7 @@ public function __invoke(Request $request, Response $response, callable $next):
$response = $response->withStatus($e->getCode()); $response = $response->withStatus($e->getCode());
return $response->write($this->container->pageBuilder->render('error')); return $response->write($this->container->pageBuilder->render('error', $this->container->basePath));
} catch (UnauthorizedException $e) { } catch (UnauthorizedException $e) {
$returnUrl = urlencode($this->container->environment['REQUEST_URI']); $returnUrl = urlencode($this->container->environment['REQUEST_URI']);
@ -80,7 +80,7 @@ public function __invoke(Request $request, Response $response, callable $next):
$response = $response->withStatus(500); $response = $response->withStatus(500);
return $response->write($this->container->pageBuilder->render('error')); return $response->write($this->container->pageBuilder->render('error', $this->container->basePath));
} }
} }

View file

@ -152,7 +152,7 @@ public function save(Request $request, Response $response): Response
// To preserve backward compatibility with 3rd parties, plugins still use arrays // To preserve backward compatibility with 3rd parties, plugins still use arrays
$formatter = $this->container->formatterFactory->getFormatter('raw'); $formatter = $this->container->formatterFactory->getFormatter('raw');
$data = $formatter->format($bookmark); $data = $formatter->format($bookmark);
$data = $this->executeHooks('save_link', $data); $this->executePageHooks('save_link', $data);
$bookmark->fromArray($data); $bookmark->fromArray($data);
$this->container->bookmarkService->set($bookmark); $this->container->bookmarkService->set($bookmark);
@ -211,7 +211,7 @@ public function deleteBookmark(Request $request, Response $response): Response
} }
$data = $formatter->format($bookmark); $data = $formatter->format($bookmark);
$this->container->pluginManager->executeHooks('delete_link', $data); $this->executePageHooks('delete_link', $data);
$this->container->bookmarkService->remove($bookmark, false); $this->container->bookmarkService->remove($bookmark, false);
++ $count; ++ $count;
} }
@ -283,7 +283,7 @@ public function changeVisibility(Request $request, Response $response): Response
// To preserve backward compatibility with 3rd parties, plugins still use arrays // To preserve backward compatibility with 3rd parties, plugins still use arrays
$data = $formatter->format($bookmark); $data = $formatter->format($bookmark);
$this->container->pluginManager->executeHooks('save_link', $data); $this->executePageHooks('save_link', $data);
$bookmark->fromArray($data); $bookmark->fromArray($data);
$this->container->bookmarkService->set($bookmark, false); $this->container->bookmarkService->set($bookmark, false);
@ -325,7 +325,7 @@ public function pinBookmark(Request $request, Response $response, array $args):
// To preserve backward compatibility with 3rd parties, plugins still use arrays // To preserve backward compatibility with 3rd parties, plugins still use arrays
$data = $formatter->format($bookmark); $data = $formatter->format($bookmark);
$this->container->pluginManager->executeHooks('save_link', $data); $this->executePageHooks('save_link', $data);
$bookmark->fromArray($data); $bookmark->fromArray($data);
$this->container->bookmarkService->set($bookmark); $this->container->bookmarkService->set($bookmark);
@ -354,7 +354,7 @@ protected function displayForm(array $link, bool $isNew, Request $request, Respo
'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), '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) { foreach ($data as $key => $value) {
$this->assignView($key, $value); $this->assignView($key, $value);
@ -368,19 +368,4 @@ protected function displayForm(array $link, bool $isNew, Request $request, Respo
return $response->write($this->render(TemplatePage::EDIT_LINK)); 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;
}
} }

View file

@ -58,7 +58,7 @@ public function save(Request $request, Response $response): Response
try { try {
$parameters = $request->getParams() ?? []; $parameters = $request->getParams() ?? [];
$this->executeHooks($parameters); $this->executePageHooks('save_plugin_parameters', $parameters);
if (isset($parameters['parameters_form'])) { if (isset($parameters['parameters_form'])) {
unset($parameters['parameters_form']); unset($parameters['parameters_form']);
@ -81,19 +81,4 @@ public function save(Request $request, Response $response): Response
return $this->redirect($response, '/admin/plugins'); 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;
}
} }

View file

@ -22,7 +22,7 @@ public function index(Request $request, Response $response): Response
'sslenabled' => is_https($this->container->environment), 'sslenabled' => is_https($this->container->environment),
]; ];
$data = $this->executeHooks($data); $this->executePageHooks('render_tools', $data, TemplatePage::TOOLS);
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$this->assignView($key, $value); $this->assignView($key, $value);
@ -32,19 +32,4 @@ public function index(Request $request, Response $response): Response
return $response->write($this->render(TemplatePage::TOOLS)); 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;
}
} }

View file

@ -124,7 +124,7 @@ public function index(Request $request, Response $response): Response
$data['pagetitle'] = ($data['pagetitle'] ?? '') . $this->container->conf->get('general.title', 'Shaarli'); $data['pagetitle'] = ($data['pagetitle'] ?? '') . $this->container->conf->get('general.title', 'Shaarli');
$this->executeHooks($data); $this->executePageHooks('render_linklist', $data, TemplatePage::LINKLIST);
$this->assignAllView($data); $this->assignAllView($data);
return $response->write($this->render(TemplatePage::LINKLIST)); return $response->write($this->render(TemplatePage::LINKLIST));
@ -153,7 +153,7 @@ public function permalink(Request $request, Response $response, array $args): Re
] ]
); );
$this->executeHooks($data); $this->executePageHooks('render_linklist', $data, TemplatePage::LINKLIST);
$this->assignAllView($data); $this->assignAllView($data);
return $response->write($this->render(TemplatePage::LINKLIST)); return $response->write($this->render(TemplatePage::LINKLIST));
@ -182,18 +182,6 @@ protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = tr
return false; return false;
} }
/**
* @param mixed[] $data Template vars to process in plugins, passed as reference.
*/
protected function executeHooks(array &$data): void
{
$this->container->pluginManager->executeHooks(
'render_linklist',
$data,
['loggedin' => $this->container->loginManager->isLoggedIn()]
);
}
/** /**
* @return string[] Default template variables without values. * @return string[] Default template variables without values.
*/ */

View file

@ -72,13 +72,11 @@ public function index(Request $request, Response $response): Response
]; ];
// Hooks are called before column construction so that plugins don't have to deal with columns. // Hooks are called before column construction so that plugins don't have to deal with columns.
$data = $this->executeHooks($data); $this->executePageHooks('render_daily', $data, TemplatePage::DAILY);
$data['cols'] = $this->calculateColumns($data['linksToDisplay']); $data['cols'] = $this->calculateColumns($data['linksToDisplay']);
foreach ($data as $key => $value) { $this->assignAllView($data);
$this->assignView($key, $value);
}
$mainTitle = $this->container->conf->get('general.title', 'Shaarli'); $mainTitle = $this->container->conf->get('general.title', 'Shaarli');
$this->assignView( $this->assignView(
@ -190,20 +188,4 @@ protected function calculateColumns(array $links): array
return $columns; return $columns;
} }
/**
* @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_daily',
$data,
['loggedin' => $this->container->loginManager->isLoggedIn()]
);
return $data;
}
} }

View file

@ -46,7 +46,7 @@ protected function processRequest(string $feedType, Request $request, Response $
$data = $this->container->feedBuilder->buildData($feedType, $request->getParams()); $data = $this->container->feedBuilder->buildData($feedType, $request->getParams());
$data = $this->executeHooks($data, $feedType); $this->executePageHooks('render_feed', $data, $feedType);
$this->assignAllView($data); $this->assignAllView($data);
$content = $this->render('feed.'. $feedType); $content = $this->render('feed.'. $feedType);
@ -55,23 +55,4 @@ protected function processRequest(string $feedType, Request $request, Response $
return $response->write($content); return $response->write($content);
} }
/**
* @param mixed[] $data Template data
*
* @return mixed[] Template data after active plugins hook execution.
*/
protected function executeHooks(array $data, string $feedType): array
{
$this->container->pluginManager->executeHooks(
'render_feed',
$data,
[
'loggedin' => $this->container->loginManager->isLoggedIn(),
'target' => $feedType,
]
);
return $data;
}
} }

View file

@ -42,30 +42,13 @@ public function index(Request $request, Response $response): Response
} }
} }
$data = $this->executeHooks($linksToDisplay); $data = ['linksToDisplay' => $linksToDisplay];
$this->executePageHooks('render_picwall', $data, TemplatePage::PICTURE_WALL);
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$this->assignView($key, $value); $this->assignView($key, $value);
} }
return $response->write($this->render(TemplatePage::PICTURE_WALL)); return $response->write($this->render(TemplatePage::PICTURE_WALL));
} }
/**
* @param mixed[] $linksToDisplay List of formatted bookmarks
*
* @return mixed[] Template data after active plugins render_picwall hook execution.
*/
protected function executeHooks(array $linksToDisplay): array
{
$data = [
'linksToDisplay' => $linksToDisplay,
];
$this->container->pluginManager->executeHooks(
'render_picwall',
$data,
['loggedin' => $this->container->loginManager->isLoggedIn()]
);
return $data;
}
} }

View file

@ -60,22 +60,9 @@ protected function render(string $template): string
$this->assignView('privateLinkcount', $this->container->bookmarkService->count(BookmarkFilter::$PRIVATE)); $this->assignView('privateLinkcount', $this->container->bookmarkService->count(BookmarkFilter::$PRIVATE));
$this->assignView('plugin_errors', $this->container->pluginManager->getErrors()); $this->assignView('plugin_errors', $this->container->pluginManager->getErrors());
/*
* Define base path (if Shaarli is installed in a domain's subfolder, e.g. `/shaarli`)
* and the asset path (subfolder/tpl/default for default theme).
* These MUST be used to create an internal link or to include an asset in templates.
*/
$this->assignView('base_path', $this->container->basePath);
$this->assignView(
'asset_path',
$this->container->basePath . '/' .
rtrim($this->container->conf->get('resource.raintpl_tpl', 'tpl'), '/') . '/' .
$this->container->conf->get('resource.theme', 'default')
);
$this->executeDefaultHooks($template); $this->executeDefaultHooks($template);
return $this->container->pageBuilder->render($template); return $this->container->pageBuilder->render($template, $this->container->basePath);
} }
/** /**
@ -97,13 +84,29 @@ protected function executeDefaultHooks(string $template): void
$pluginData, $pluginData,
[ [
'target' => $template, 'target' => $template,
'loggedin' => $this->container->loginManager->isLoggedIn() 'loggedin' => $this->container->loginManager->isLoggedIn(),
'basePath' => $this->container->basePath,
] ]
); );
$this->assignView('plugins_' . $name, $pluginData); $this->assignView('plugins_' . $name, $pluginData);
} }
} }
protected function executePageHooks(string $hook, array &$data, string $template = null): void
{
$params = [
'target' => $template,
'loggedin' => $this->container->loginManager->isLoggedIn(),
'basePath' => $this->container->basePath,
];
$this->container->pluginManager->executeHooks(
$hook,
$data,
$params
);
}
/** /**
* Simple helper which prepend the base path to redirect path. * Simple helper which prepend the base path to redirect path.
* *

View file

@ -71,10 +71,8 @@ protected function processRequest(string $type, Request $request, Response $resp
'search_tags' => $searchTags, 'search_tags' => $searchTags,
'tags' => $tags, 'tags' => $tags,
]; ];
$data = $this->executeHooks('tag' . $type, $data); $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type);
foreach ($data as $key => $value) { $this->assignAllView($data);
$this->assignView($key, $value);
}
$searchTags = !empty($searchTags) ? $searchTags .' - ' : ''; $searchTags = !empty($searchTags) ? $searchTags .' - ' : '';
$this->assignView( $this->assignView(
@ -112,20 +110,4 @@ protected function formatTagsForCloud(array $tags): array
return $tagList; return $tagList;
} }
/**
* @param mixed[] $data Template data
*
* @return mixed[] Template data after active plugins hook execution.
*/
protected function executeHooks(string $template, array $data): array
{
$this->container->pluginManager->executeHooks(
'render_'. $template,
$data,
['loggedin' => $this->container->loginManager->isLoggedIn()]
);
return $data;
}
} }

View file

@ -108,6 +108,10 @@ public function executeHooks($hook, &$data, $params = array())
$data['_LOGGEDIN_'] = $params['loggedin']; $data['_LOGGEDIN_'] = $params['loggedin'];
} }
if (isset($params['basePath'])) {
$data['_BASE_PATH_'] = $params['basePath'];
}
foreach ($this->loadedPlugins as $plugin) { foreach ($this->loadedPlugins as $plugin) {
$hookFunction = $this->buildHookName($hook, $plugin); $hookFunction = $this->buildHookName($hook, $plugin);

View file

@ -3,6 +3,7 @@
namespace Shaarli\Render; namespace Shaarli\Render;
use Exception; use Exception;
use exceptions\MissingBasePathException;
use RainTPL; use RainTPL;
use Shaarli\ApplicationUtils; use Shaarli\ApplicationUtils;
use Shaarli\Bookmark\BookmarkServiceInterface; use Shaarli\Bookmark\BookmarkServiceInterface;
@ -156,7 +157,7 @@ private function initialize()
* Affect variable after controller processing. * Affect variable after controller processing.
* Used for alert messages. * Used for alert messages.
*/ */
protected function finalize(): void protected function finalize(string $basePath): void
{ {
// TODO: use the SessionManager // TODO: use the SessionManager
$messageKeys = [ $messageKeys = [
@ -170,6 +171,14 @@ protected function finalize(): void
unset($_SESSION[$messageKey]); unset($_SESSION[$messageKey]);
} }
} }
$this->assign('base_path', $basePath);
$this->assign(
'asset_path',
$basePath . '/' .
rtrim($this->conf->get('resource.raintpl_tpl', 'tpl'), '/') . '/' .
$this->conf->get('resource.theme', 'default')
);
} }
/** /**
@ -209,23 +218,6 @@ public function assignAll($data)
return true; return true;
} }
/**
* Render a specific page (using a template file).
* e.g. $pb->renderPage('picwall');
*
* @param string $page Template filename (without extension).
*/
public function renderPage($page)
{
if ($this->tpl === false) {
$this->initialize();
}
$this->finalize();
$this->tpl->draw($page);
}
/** /**
* Render a specific page as string (using a template file). * Render a specific page as string (using a template file).
* e.g. $pb->render('picwall'); * e.g. $pb->render('picwall');
@ -234,13 +226,13 @@ public function renderPage($page)
* *
* @return string Processed template content * @return string Processed template content
*/ */
public function render(string $page): string public function render(string $page, string $basePath): string
{ {
if ($this->tpl === false) { if ($this->tpl === false) {
$this->initialize(); $this->initialize();
} }
$this->finalize(); $this->finalize($basePath);
return $this->tpl->draw($page, true); return $this->tpl->draw($page, true);
} }

View file

@ -20,7 +20,7 @@ function hook_addlink_toolbar_render_header($data)
$form = array( $form = array(
'attr' => array( 'attr' => array(
'method' => 'GET', 'method' => 'GET',
'action' => '', 'action' => $data['_BASE_PATH_'] . '/admin/shaare',
'name' => 'addform', 'name' => 'addform',
'class' => 'addform', 'class' => 'addform',
), ),

View file

@ -1,5 +1,5 @@
<span> <span>
<a href="https://web.archive.org/web/%s"> <a href="https://web.archive.org/web/%s">
<img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="%s" alt="archive.org" /> <img class="linklist-plugin-icon" src="%s/archiveorg/internetarchive.png" title="%s" alt="archive.org" />
</a> </a>
</span> </span>

View file

@ -17,12 +17,13 @@
function hook_archiveorg_render_linklist($data) function hook_archiveorg_render_linklist($data)
{ {
$archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html'); $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html');
$path = ($data['_BASE_PATH_'] ?? '') . '/' . PluginManager::$PLUGINS_PATH;
foreach ($data['links'] as &$value) { foreach ($data['links'] as &$value) {
if ($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) { if ($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
continue; continue;
} }
$archive = sprintf($archive_html, $value['url'], t('View on archive.org')); $archive = sprintf($archive_html, $value['url'], $path, t('View on archive.org'));
$value['link_plugin'][] = $archive; $value['link_plugin'][] = $archive;
} }

View file

@ -118,7 +118,7 @@ function hook_demo_plugin_render_header($data)
$form = array( $form = array(
'attr' => array( 'attr' => array(
'method' => 'GET', 'method' => 'GET',
'action' => '?', 'action' => $data['_BASE_PATH_'] . '/',
'class' => 'addform', 'class' => 'addform',
), ),
'inputs' => array( 'inputs' => array(

View file

@ -1,5 +0,0 @@
<span>
<a href="?%s#isso-thread">
<img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="%s" alt="archive.org" />
</a>
</span>

View file

@ -19,11 +19,12 @@ function hook_qrcode_render_linklist($data)
{ {
$qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html'); $qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html');
$path = ($data['_BASE_PATH_'] ?? '') . '/' . PluginManager::$PLUGINS_PATH;
foreach ($data['links'] as &$value) { foreach ($data['links'] as &$value) {
$qrcode = sprintf( $qrcode = sprintf(
$qrcode_html, $qrcode_html,
$value['url'], $value['url'],
PluginManager::$PLUGINS_PATH $path
); );
$value['link_plugin'][] = $qrcode; $value['link_plugin'][] = $qrcode;
} }

View file

@ -34,8 +34,9 @@ function showQrCode(caller,loading)
{ {
if (!loading) // If javascript lib is still loading, do not append script to body. if (!loading) // If javascript lib is still loading, do not append script to body.
{ {
var basePath = document.querySelector('input[name="js_base_path"]').value;
var element = document.createElement("script"); var element = document.createElement("script");
element.src = "plugins/qrcode/qr-1.1.3.min.js"; element.src = basePath + "/plugins/qrcode/qr-1.1.3.min.js";
document.body.appendChild(element); document.body.appendChild(element);
} }
setTimeout(function() { showQrCode(caller,true);}, 200); // Retry in 200 milliseconds. setTimeout(function() { showQrCode(caller,true);}, 200); // Retry in 200 milliseconds.

View file

@ -45,12 +45,14 @@ function hook_wallabag_render_linklist($data, $conf)
$wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html'); $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html');
$linkTitle = t('Save to wallabag'); $linkTitle = t('Save to wallabag');
$path = ($data['_BASE_PATH_'] ?? '') . '/' . PluginManager::$PLUGINS_PATH;
foreach ($data['links'] as &$value) { foreach ($data['links'] as &$value) {
$wallabag = sprintf( $wallabag = sprintf(
$wallabagHtml, $wallabagHtml,
$wallabagInstance->getWallabagUrl(), $wallabagInstance->getWallabagUrl(),
urlencode($value['url']), urlencode($value['url']),
PluginManager::$PLUGINS_PATH, $path,
$linkTitle $linkTitle
); );
$value['link_plugin'][] = $wallabag; $value['link_plugin'][] = $wallabag;

View file

@ -2,6 +2,7 @@
namespace Shaarli\Config; namespace Shaarli\Config;
use Shaarli\Config\Exception\PluginConfigOrderException; use Shaarli\Config\Exception\PluginConfigOrderException;
use Shaarli\Plugin\PluginManager;
require_once 'application/config/ConfigPlugin.php'; require_once 'application/config/ConfigPlugin.php';
@ -17,23 +18,30 @@ class ConfigPluginTest extends \PHPUnit\Framework\TestCase
*/ */
public function testSavePluginConfigValid() public function testSavePluginConfigValid()
{ {
$data = array( $data = [
'order_plugin1' => 2, // no plugin related 'order_plugin1' => 2, // no plugin related
'plugin2' => 0, // new - at the end 'plugin2' => 0, // new - at the end
'plugin3' => 0, // 2nd 'plugin3' => 0, // 2nd
'order_plugin3' => 8, 'order_plugin3' => 8,
'plugin4' => 0, // 1st 'plugin4' => 0, // 1st
'order_plugin4' => 5, 'order_plugin4' => 5,
); ];
$expected = array( $expected = [
'plugin3', 'plugin3',
'plugin4', 'plugin4',
'plugin2', 'plugin2',
); ];
mkdir($path = __DIR__ . '/folder');
PluginManager::$PLUGINS_PATH = $path;
array_map(function (string $plugin) use ($path) { touch($path . '/' . $plugin); }, $expected);
$out = save_plugin_config($data); $out = save_plugin_config($data);
$this->assertEquals($expected, $out); $this->assertEquals($expected, $out);
array_map(function (string $plugin) use ($path) { unlink($path . '/' . $plugin); }, $expected);
rmdir($path);
} }
/** /**

View file

@ -59,8 +59,12 @@ public function testDeleteSingleBookmark(): void
->with('raw') ->with('raw')
->willReturnCallback(function () use ($bookmark): BookmarkFormatter { ->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class); $formatter = $this->createMock(BookmarkFormatter::class);
$formatter
$formatter->expects(static::once())->method('format')->with($bookmark); ->expects(static::once())
->method('format')
->with($bookmark)
->willReturn(['formatted' => $bookmark])
;
return $formatter; return $formatter;
}) })
@ -70,7 +74,7 @@ public function testDeleteSingleBookmark(): void
$this->container->pluginManager $this->container->pluginManager
->expects(static::once()) ->expects(static::once())
->method('executeHooks') ->method('executeHooks')
->with('delete_link') ->with('delete_link', ['formatted' => $bookmark])
; ;
$result = $this->controller->deleteBookmark($request, $response); $result = $this->controller->deleteBookmark($request, $response);
@ -129,6 +133,9 @@ public function testDeleteMultipleBookmarks(): void
->withConsecutive(...array_map(function (Bookmark $bookmark): array { ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark]; return [$bookmark];
}, $bookmarks)) }, $bookmarks))
->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array {
return ['formatted' => $bookmark];
}, $bookmarks))
; ;
return $formatter; return $formatter;
@ -254,6 +261,9 @@ public function testDeleteMultipleBookmarksOneNotFound(): void
->withConsecutive(...array_map(function (Bookmark $bookmark): array { ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
return [$bookmark]; return [$bookmark];
}, $bookmarks)) }, $bookmarks))
->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array {
return ['formatted' => $bookmark];
}, $bookmarks))
; ;
return $formatter; return $formatter;
@ -350,7 +360,12 @@ public function testDeleteBookmarkFromBookmarklet(): void
$this->container->formatterFactory $this->container->formatterFactory
->expects(static::once()) ->expects(static::once())
->method('getFormatter') ->method('getFormatter')
->willReturn($this->createMock(BookmarkFormatter::class)) ->willReturnCallback(function (): BookmarkFormatter {
$formatter = $this->createMock(BookmarkFormatter::class);
$formatter->method('format')->willReturn(['formatted']);
return $formatter;
})
; ;
$result = $this->controller->deleteBookmark($request, $response); $result = $this->controller->deleteBookmark($request, $response);

View file

@ -7,6 +7,7 @@
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Shaarli\Config\ConfigManager; use Shaarli\Config\ConfigManager;
use Shaarli\Front\Exception\WrongTokenException; use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Plugin\PluginManager;
use Shaarli\Security\SessionManager; use Shaarli\Security\SessionManager;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
@ -15,6 +16,8 @@ class PluginsControllerTest extends TestCase
{ {
use FrontAdminControllerMockHelper; use FrontAdminControllerMockHelper;
const PLUGIN_NAMES = ['plugin1', 'plugin2', 'plugin3', 'plugin4'];
/** @var PluginsController */ /** @var PluginsController */
protected $controller; protected $controller;
@ -23,6 +26,17 @@ public function setUp(): void
$this->createContainer(); $this->createContainer();
$this->controller = new PluginsController($this->container); $this->controller = new PluginsController($this->container);
mkdir($path = __DIR__ . '/folder');
PluginManager::$PLUGINS_PATH = $path;
array_map(function (string $plugin) use ($path) { touch($path . '/' . $plugin); }, static::PLUGIN_NAMES);
}
public function tearDown()
{
$path = __DIR__ . '/folder';
array_map(function (string $plugin) use ($path) { unlink($path . '/' . $plugin); }, static::PLUGIN_NAMES);
rmdir($path);
} }
/** /**

View file

@ -96,8 +96,6 @@ public function testRender(): void
static::assertSame(10, $this->assignedValues['linkcount']); static::assertSame(10, $this->assignedValues['linkcount']);
static::assertSame(5, $this->assignedValues['privateLinkcount']); static::assertSame(5, $this->assignedValues['privateLinkcount']);
static::assertSame(['error'], $this->assignedValues['plugin_errors']); static::assertSame(['error'], $this->assignedValues['plugin_errors']);
static::assertSame('/subfolder', $this->assignedValues['base_path']);
static::assertSame('/subfolder/tpl/default', $this->assignedValues['asset_path']);
static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']); static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']);
static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']); static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']);

View file

@ -28,6 +28,7 @@ public function testAddlinkHeaderLoggedIn()
$data = array($str => $str); $data = array($str => $str);
$data['_PAGE_'] = TemplatePage::LINKLIST; $data['_PAGE_'] = TemplatePage::LINKLIST;
$data['_LOGGEDIN_'] = true; $data['_LOGGEDIN_'] = true;
$data['_BASE_PATH_'] = '/subfolder';
$data = hook_addlink_toolbar_render_header($data); $data = hook_addlink_toolbar_render_header($data);
$this->assertEquals($str, $data[$str]); $this->assertEquals($str, $data[$str]);
@ -36,6 +37,8 @@ public function testAddlinkHeaderLoggedIn()
$data = array($str => $str); $data = array($str => $str);
$data['_PAGE_'] = $str; $data['_PAGE_'] = $str;
$data['_LOGGEDIN_'] = true; $data['_LOGGEDIN_'] = true;
$data['_BASE_PATH_'] = '/subfolder';
$data = hook_addlink_toolbar_render_header($data); $data = hook_addlink_toolbar_render_header($data);
$this->assertEquals($str, $data[$str]); $this->assertEquals($str, $data[$str]);
$this->assertArrayNotHasKey('fields_toolbar', $data); $this->assertArrayNotHasKey('fields_toolbar', $data);
@ -50,6 +53,7 @@ public function testAddlinkHeaderLoggedOut()
$data = array($str => $str); $data = array($str => $str);
$data['_PAGE_'] = TemplatePage::LINKLIST; $data['_PAGE_'] = TemplatePage::LINKLIST;
$data['_LOGGEDIN_'] = false; $data['_LOGGEDIN_'] = false;
$data['_BASE_PATH_'] = '/subfolder';
$data = hook_addlink_toolbar_render_header($data); $data = hook_addlink_toolbar_render_header($data);
$this->assertEquals($str, $data[$str]); $this->assertEquals($str, $data[$str]);

View file

@ -4,7 +4,7 @@
<body onload="document.changepasswordform.oldpassword.focus();"> <body onload="document.changepasswordform.oldpassword.focus();">
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<form method="POST" action="#" name="changepasswordform" id="changepasswordform"> <form method="POST" action="{$base_path}/admin/password" name="changepasswordform" id="changepasswordform">
Old password: <input type="password" name="oldpassword">&nbsp; &nbsp; Old password: <input type="password" name="oldpassword">&nbsp; &nbsp;
New password: <input type="password" name="setpassword"> New password: <input type="password" name="setpassword">
<input type="hidden" name="token" value="{$token}"> <input type="hidden" name="token" value="{$token}">

View file

@ -5,7 +5,7 @@
<body onload="document.changetag.fromtag.focus();"> <body onload="document.changetag.fromtag.focus();">
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<form method="POST" action="" name="changetag" id="changetag"> <form method="POST" action="{$base_path}/admin/tags" name="changetag" id="changetag">
<input type="hidden" name="token" value="{$token}"> <input type="hidden" name="token" value="{$token}">
<div> <div>
<label for="fromtag">Tag:</label> <label for="fromtag">Tag:</label>

View file

@ -4,7 +4,7 @@
<body onload="document.configform.title.focus();"> <body onload="document.configform.title.focus();">
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<form method="POST" action="#" name="configform" id="configform"> <form method="POST" action="{$base_path}/admin/configure" name="configform" id="configform">
<input type="hidden" name="token" value="{$token}"> <input type="hidden" name="token" value="{$token}">
<table id="configuration_table"> <table id="configuration_table">

View file

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>{include="includes"} <head>{include="includes"}
<link type="text/css" rel="stylesheet" href="inc/awesomplete.css#" />
</head> </head>
<body <body
{if="$link.title==''"}onload="document.linkform.lf_title.focus();" {if="$link.title==''"}onload="document.linkform.lf_title.focus();"
@ -10,9 +9,8 @@
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<div id="shaarli_title"><a href="{$titleLink}">{$shaarlititle}</a></div> <div id="shaarli_title"><a href="{$titleLink}">{$shaarlititle}</a></div>
{/if}
<div id="editlinkform"> <div id="editlinkform">
<form method="post" name="linkform"> <form method="post" name="linkform" action="{$base_path}/admin/shaare">
<input type="hidden" name="lf_linkdate" value="{$link.linkdate}"> <input type="hidden" name="lf_linkdate" value="{$link.linkdate}">
{if="isset($link.id)"} {if="isset($link.id)"}
<input type="hidden" name="lf_id" value="{$link.id}"> <input type="hidden" name="lf_id" value="{$link.id}">

View file

@ -10,6 +10,8 @@
<input type="radio" name="selection" value="all" checked="true"> All<br> <input type="radio" name="selection" value="all" checked="true"> All<br>
<input type="radio" name="selection" value="private"> Private<br> <input type="radio" name="selection" value="private"> Private<br>
<input type="radio" name="selection" value="public"> Public<br> <input type="radio" name="selection" value="public"> Public<br>
<input type="hidden" name="token" value="{$token}">
<br> <br>
<input type="checkbox" name="prepend_note_url" id="prepend_note_url"> <input type="checkbox" name="prepend_note_url" id="prepend_note_url">
<label for="prepend_note_url"> <label for="prepend_note_url">

View file

@ -6,14 +6,14 @@
<link rel="alternate" type="application/rss+xml" href="{$feedurl}feed/rss?{$searchcrits}#" title="RSS Feed" /> <link rel="alternate" type="application/rss+xml" href="{$feedurl}feed/rss?{$searchcrits}#" title="RSS Feed" />
<link rel="alternate" type="application/atom+xml" href="{$feedurl}feed/atom?{$searchcrits}#" title="ATOM Feed" /> <link rel="alternate" type="application/atom+xml" href="{$feedurl}feed/atom?{$searchcrits}#" title="ATOM Feed" />
<link href="img/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <link href="img/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<link type="text/css" rel="stylesheet" href="css/shaarli.min.css" /> <link type="text/css" rel="stylesheet" href="{$asset_path}/css/shaarli.min.css#" />
{if="$formatter==='markdown'"} {if="$formatter==='markdown'"}
<link type="text/css" rel="stylesheet" href="{$asset_path}/css/markdown.min.css?v={$version_hash}#" /> <link type="text/css" rel="stylesheet" href="{$asset_path}/css/markdown.min.css?v={$version_hash}#" />
{/if} {/if}
{loop="$plugins_includes.css_files"} {loop="$plugins_includes.css_files"}
<link type="text/css" rel="stylesheet" href="{$value}#"/> <link type="text/css" rel="stylesheet" href="{$base_path}/{$value}#"/>
{/loop} {/loop}
{if="is_file('data/user.css')"}<link type="text/css" rel="stylesheet" href="data/user.css#" />{/if} {if="is_file('data/user.css')"}<link type="text/css" rel="stylesheet" href="{$base_path}/data/user.css#" />{/if}
<link rel="search" type="application/opensearchdescription+xml" href="{$base_path}/open-search#" <link rel="search" type="application/opensearchdescription+xml" href="{$base_path}/open-search#"
title="Shaarli search - {$shaarlititle|htmlspecialchars}" /> title="Shaarli search - {$shaarlititle|htmlspecialchars}" />
{if="! empty($links) && count($links) === 1"} {if="! empty($links) && count($links) === 1"}

View file

@ -5,7 +5,7 @@
<div id="install"> <div id="install">
<h1>Shaarli</h1> <h1>Shaarli</h1>
It looks like it's the first time you run Shaarli. Please configure it:<br> It looks like it's the first time you run Shaarli. Please configure it:<br>
<form method="POST" action="#" name="installform" id="installform"> <form method="POST" action="{$base_path}/install" name="installform" id="installform">
<table> <table>
<tr><td><b>Login:</b></td><td><input type="text" name="setlogin" size="30"></td></tr> <tr><td><b>Login:</b></td><td><input type="text" name="setlogin" size="30"></td></tr>
<tr><td><b>Password:</b></td><td><input type="password" name="setpassword" size="30"></td></tr> <tr><td><b>Password:</b></td><td><input type="password" name="setpassword" size="30"></td></tr>

View file

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<link type="text/css" rel="stylesheet" href="inc/awesomplete.css#" />
{include="includes"} {include="includes"}
</head> </head>
<body> <body>
@ -84,7 +83,7 @@
<div class="thumbnail"> <div class="thumbnail">
<a href="{$value.real_url}"> <a href="{$value.real_url}">
{ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore} {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
<img data-src="{$value.thumbnail}#" class="b-lazy" <img data-src="{$base_path}/{$value.thumbnail}#" class="b-lazy"
src="" src=""
alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" /> alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
</a> </a>
@ -93,17 +92,16 @@
<div class="linkcontainer"> <div class="linkcontainer">
{if="$is_logged_in"} {if="$is_logged_in"}
<div class="linkeditbuttons"> <div class="linkeditbuttons">
<form method="GET" class="buttoneditform"> <a href="{$base_path}/admin/shaare/{$value.id}" title="Edit" class="button_edit">
<input type="hidden" name="edit_link" value="{$value.id}"> <img src="{$asset_path}/img/edit_icon.png#">
<input type="image" alt="Edit" src="{$asset_path}/img/edit_icon.png#" title="Edit" class="button_edit"> </a>
</form><br> <br>
<form method="GET" class="buttoneditform"> <a href="{$base_path}/admin/shaare/delete?id={$value.id}&amp;token={$token}" label="Delete"
<input type="hidden" name="lf_linkdate" value="{$value.id}"> onClick="return confirmDeleteLink();"
<input type="hidden" name="token" value="{$token}"> class="button_delete"
<input type="hidden" name="delete_link"> >
<input type="image" alt="Delete" src="{$asset_path}/img/delete_icon.png#" title="Delete" <img src="{$asset_path}/img/delete_icon.png#">
class="button_delete" onClick="return confirmDeleteLink();"> </a>
</form>
</div> </div>
{/if} {/if}
<span class="linktitle"> <span class="linktitle">
@ -114,7 +112,7 @@
{if="!$hide_timestamps || $is_logged_in"} {if="!$hide_timestamps || $is_logged_in"}
{$updated=$value.updated_timestamp ? 'Edited: '. format_date($value.updated) : 'Permalink'} {$updated=$value.updated_timestamp ? 'Edited: '. format_date($value.updated) : 'Permalink'}
<span class="linkdate" title="Permalink"> <span class="linkdate" title="Permalink">
<a href="{$base_path}/?{$value.shorturl}"> <a href="{$base_path}/shaare/{$value.shorturl}">
<span title="{$updated}"> <span title="{$updated}">
{$value.created|format_date} {$value.created|format_date}
{if="$value.updated_timestamp"}*{/if} {if="$value.updated_timestamp"}*{/if}
@ -123,7 +121,7 @@
</a> - </a> -
</span> </span>
{else} {else}
<span class="linkdate" title="Short link here"><a href="{$base_path}/?{$value.shorturl}">permalink</a> - </span> <span class="linkdate" title="Short link here"><a href="{$base_path}/shaare/{$value.shorturl}">permalink</a> - </span>
{/if} {/if}
{loop="$value.link_plugin"} {loop="$value.link_plugin"}

View file

@ -11,7 +11,7 @@
{include="page.header"} {include="page.header"}
<div id="headerform"> <div id="headerform">
<form method="post" name="loginform"> <form method="post" name="loginform" action="{$base_path}/login">
<label for="login">Login: <input type="text" id="login" name="login" tabindex="1" <label for="login">Login: <input type="text" id="login" name="login" tabindex="1"
{if="!empty($username)"}value="{$username}"{/if}> {if="!empty($username)"}value="{$username}"{/if}>
</label> </label>

View file

@ -30,5 +30,7 @@
{/if} {/if}
{loop="$plugins_footer.js_files"} {loop="$plugins_footer.js_files"}
<script src="{$value}#"></script> <script src="{$base_path}/{$value}#"></script>
{/loop} {/loop}
<input type="hidden" name="js_base_path" value="{$base_path}" />

View file

@ -86,6 +86,7 @@ <h1>Disabled Plugins</h1>
<input type="submit" value="Save"/> <input type="submit" value="Save"/>
</div> </div>
</section> </section>
<input type="hidden" name="token" value="{$token}">
</form> </form>
<form action="{$base_path}/admin/plugins" method="POST"> <form action="{$base_path}/admin/plugins" method="POST">
@ -124,6 +125,7 @@ <h2>{function="str_replace('_', ' ', $key)"}</h2>
</div> </div>
</div> </div>
</section> </section>
<input type="hidden" name="token" value="{$token}">
</form> </form>
</div> </div>

View file

@ -23,7 +23,6 @@
<input type="hidden" name="ids" value="{function="implode(',', $ids)"}" /> <input type="hidden" name="ids" value="{function="implode(',', $ids)"}" />
{include="page.footer"} {include="page.footer"}
<input type="hidden" name="js_base_path" value="{$base_path}" />
<script src="{$asset_path}/js/thumbnails_update.min.js?v={$version_hash}#"></script> <script src="{$asset_path}/js/thumbnails_update.min.js?v={$version_hash}#"></script>
</body> </body>
</html> </html>