--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Admin;
+
+use Shaarli\Container\ShaarliContainer;
+use Shaarli\Front\Exception\OpenShaarliPasswordException;
+use Shaarli\Front\Exception\ShaarliFrontException;
+use Slim\Http\Request;
+use Slim\Http\Response;
+use Throwable;
+
+/**
+ * Class PasswordController
+ *
+ * Slim controller used to handle passwords update.
+ */
+class PasswordController extends ShaarliAdminController
+{
+ public function __construct(ShaarliContainer $container)
+ {
+ parent::__construct($container);
+
+ $this->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'));
+ }
+}
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)
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);
+ }
}
use Slim\Http\Request;
use Slim\Http\Response;
+/**
+ * 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
+ */
abstract class ShaarliVisitorController
{
/** @var ShaarliContainer */
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Exception;
+
+/**
+ * Class OpenShaarliPasswordException
+ *
+ * Raised if the user tries to change the admin password on an open shaarli instance.
+ */
+class OpenShaarliPasswordException extends ShaarliFrontException
+{
+ public function __construct()
+ {
+ parent::__construct(t('You are not supposed to change a password on an Open Shaarli.'), 403);
+ }
+}
/**
* Class ShaarliException
*
- * Abstract exception class used to defined any custom exception thrown during front rendering.
+ * Exception class used to defined any custom exception thrown during front rendering.
*
* @package Front\Exception
*/
-abstract class ShaarliFrontException extends \Exception
+class ShaarliFrontException extends \Exception
{
/** Override parent constructor to force $message and $httpCode parameters to be set. */
public function __construct(string $message, int $httpCode, Throwable $previous = null)
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Exception;
+
+/**
+ * Class OpenShaarliPasswordException
+ *
+ * Raised if the user tries to perform an action with an invalid XSRF token.
+ */
+class WrongTokenException extends ShaarliFrontException
+{
+ public function __construct()
+ {
+ parent::__construct(t('Wrong token.'), 403);
+ }
+}
use Shaarli\ApplicationUtils;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
+use Shaarli\Security\SessionManager;
use Shaarli\Thumbnailer;
/**
$this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
$this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));
- if (!empty($_SESSION['warnings'])) {
- $this->tpl->assign('global_warnings', $_SESSION['warnings']);
- unset($_SESSION['warnings']);
- }
-
$this->tpl->assign('formatter', $this->conf->get('formatter', 'default'));
// To be removed with a proper theme configuration.
$this->tpl->assign('conf', $this->conf);
}
+ protected function finalize(): void
+ {
+ // TODO: use the SessionManager
+ $messageKeys = [
+ SessionManager::KEY_SUCCESS_MESSAGES,
+ SessionManager::KEY_WARNING_MESSAGES,
+ SessionManager::KEY_ERROR_MESSAGES
+ ];
+ foreach ($messageKeys as $messageKey) {
+ if (!empty($_SESSION[$messageKey])) {
+ $this->tpl->assign('global_' . $messageKey, $_SESSION[$messageKey]);
+ unset($_SESSION[$messageKey]);
+ }
+ }
+ }
+
/**
* The following assign() method is basically the same as RainTPL (except lazy loading)
*
$this->initialize();
}
+ $this->finalize();
+
$this->tpl->draw($page);
}
$this->initialize();
}
+ $this->finalize();
+
return $this->tpl->draw($page, true);
}
public const KEY_VISIBILITY = 'visibility';
public const KEY_UNTAGGED_ONLY = 'untaggedonly';
+ public const KEY_SUCCESS_MESSAGES = 'successes';
+ public const KEY_WARNING_MESSAGES = 'warnings';
+ public const KEY_ERROR_MESSAGES = 'errors';
+
/** @var int Session expiration timeout, in seconds */
public static $SHORT_TIMEOUT = 3600; // 1 hour
// -------- User wants to change his/her password.
if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
- if ($conf->get('security.open_shaarli')) {
- die(t('You are not supposed to change a password on an Open Shaarli.'));
- }
-
- if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
- if (!$sessionManager->checkToken($_POST['token'])) {
- die(t('Wrong token.')); // Go away!
- }
-
- // Make sure old password is correct.
- $oldhash = sha1(
- $_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')
- );
- if ($oldhash != $conf->get('credentials.hash')) {
- echo '<script>alert("'
- . t('The old password is not correct.')
- .'");document.location=\'./?do=changepasswd\';</script>';
- exit;
- }
- // Save new password
- // Salt renders rainbow-tables attacks useless.
- $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
- $conf->set(
- 'credentials.hash',
- sha1(
- $_POST['setpassword']
- . $conf->get('credentials.login')
- . $conf->get('credentials.salt')
- )
- );
- try {
- $conf->write($loginManager->isLoggedIn());
- } catch (Exception $e) {
- error_log(
- 'ERROR while writing config file after changing password.' . PHP_EOL .
- $e->getMessage()
- );
-
- // TODO: do not handle exceptions/errors in JS.
- echo '<script>alert("'. $e->getMessage() .'");document.location=\'./tools\';</script>';
- exit;
- }
- echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'./tools\';</script>';
- exit;
- } else {
- // show the change password form.
- $PAGE->assign('pagetitle', t('Change password') .' - '. $conf->get('general.title', 'Shaarli'));
- $PAGE->renderPage('changepassword');
- exit;
- }
+ header('Location: ./password');
+ exit;
}
// -------- User wants to change configuration
/* -- LOGGED IN -- */
$this->get('/logout', '\Shaarli\Front\Controller\Admin\LogoutController:index')->setName('logout');
$this->get('/tools', '\Shaarli\Front\Controller\Admin\ToolsController:index')->setName('tools');
+ $this->get('/password', '\Shaarli\Front\Controller\Admin\PasswordController:index')->setName('password');
+ $this->post('/password', '\Shaarli\Front\Controller\Admin\PasswordController:change')->setName('changePassword');
$this
->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage')
use Shaarli\Container\ShaarliTestContainer;
use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper;
-use Shaarli\Security\LoginManager;
/**
* Trait FrontControllerMockHelper
{
$this->parentCreateContainer();
- $this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
+ $this->container->sessionManager->method('checkToken')->willReturn(true);
}
}
public function testValidControllerInvoke(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Admin;
+
+use PHPUnit\Framework\TestCase;
+use Shaarli\Config\ConfigManager;
+use Shaarli\Front\Exception\WrongTokenException;
+use Shaarli\Security\SessionManager;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+class PasswordControllerTest extends TestCase
+{
+ use FrontAdminControllerMockHelper;
+
+ /** @var PasswordController */
+ protected $controller;
+
+ /** @var mixed[] Variables assigned to the template */
+ protected $assignedVariables = [];
+
+ public function setUp(): void
+ {
+ $this->createContainer();
+ $this->assignTemplateVars($this->assignedVariables);
+
+ $this->controller = new PasswordController($this->container);
+ }
+
+ /**
+ * Test displaying the change password page.
+ */
+ public function testGetPage(): void
+ {
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ $result = $this->controller->index($request, $response);
+
+ static::assertSame(200, $result->getStatusCode());
+ static::assertSame('changepassword', (string) $result->getBody());
+ static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
+ }
+
+ /**
+ * Change the password with valid parameters
+ */
+ public function testPostNewPasswordDefault(): void
+ {
+ $request = $this->createMock(Request::class);
+ $request->method('getParam')->willReturnCallback(function (string $key): string {
+ if ('oldpassword' === $key) {
+ return 'old';
+ }
+ if ('setpassword' === $key) {
+ return 'new';
+ }
+
+ return $key;
+ });
+ $response = new Response();
+
+ $this->container->conf = $this->createMock(ConfigManager::class);
+ $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
+ if ('credentials.hash' === $key) {
+ return sha1('old' . 'credentials.login' . 'credentials.salt');
+ }
+
+ return strpos($key, 'credentials') !== false ? $key : $default;
+ });
+ $this->container->conf->expects(static::once())->method('write')->with(true);
+
+ $this->container->conf
+ ->method('set')
+ ->willReturnCallback(function (string $key, string $value) {
+ if ('credentials.hash' === $key) {
+ static::assertSame(sha1('new' . 'credentials.login' . 'credentials.salt'), $value);
+ }
+ })
+ ;
+
+ $result = $this->controller->change($request, $response);
+
+ static::assertSame(200, $result->getStatusCode());
+ static::assertSame('changepassword', (string) $result->getBody());
+ static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
+ }
+
+ /**
+ * Change the password with a wrong existing password
+ */
+ public function testPostNewPasswordWrongOldPassword(): void
+ {
+ $request = $this->createMock(Request::class);
+ $request->method('getParam')->willReturnCallback(function (string $key): string {
+ if ('oldpassword' === $key) {
+ return 'wrong';
+ }
+ if ('setpassword' === $key) {
+ return 'new';
+ }
+
+ return $key;
+ });
+ $response = new Response();
+
+ $this->container->conf = $this->createMock(ConfigManager::class);
+ $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
+ if ('credentials.hash' === $key) {
+ return sha1('old' . 'credentials.login' . 'credentials.salt');
+ }
+
+ return strpos($key, 'credentials') !== false ? $key : $default;
+ });
+
+ $this->container->conf->expects(static::never())->method('set');
+ $this->container->conf->expects(static::never())->method('write');
+
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->with(SessionManager::KEY_ERROR_MESSAGES, ['The old password is not correct.'])
+ ;
+
+ $result = $this->controller->change($request, $response);
+
+ static::assertSame(400, $result->getStatusCode());
+ static::assertSame('changepassword', (string) $result->getBody());
+ static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
+ }
+
+ /**
+ * Change the password with a wrong existing password
+ */
+ public function testPostNewPasswordWrongToken(): void
+ {
+ $this->container->sessionManager = $this->createMock(SessionManager::class);
+ $this->container->sessionManager->method('checkToken')->willReturn(false);
+
+ $this->container->conf->expects(static::never())->method('set');
+ $this->container->conf->expects(static::never())->method('write');
+
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ $this->expectException(WrongTokenException::class);
+
+ $this->controller->change($request, $response);
+ }
+
+ /**
+ * Change the password with an empty new password
+ */
+ public function testPostNewEmptyPassword(): void
+ {
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->with(SessionManager::KEY_ERROR_MESSAGES, ['You must provide the current and new password to change it.'])
+ ;
+
+ $this->container->conf->expects(static::never())->method('set');
+ $this->container->conf->expects(static::never())->method('write');
+
+ $request = $this->createMock(Request::class);
+ $request->method('getParam')->willReturnCallback(function (string $key): string {
+ if ('oldpassword' === $key) {
+ return 'old';
+ }
+ if ('setpassword' === $key) {
+ return '';
+ }
+
+ return $key;
+ });
+ $response = new Response();
+
+ $result = $this->controller->change($request, $response);
+
+ static::assertSame(400, $result->getStatusCode());
+ static::assertSame('changepassword', (string) $result->getBody());
+ static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
+ }
+}
*/
public function testLinksPerPage(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
$request = $this->createMock(Request::class);
*/
public function testLinksPerPageNotValid(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
*/
public function testVisibility(): void
{
- $this->createValidContainerMockSet();
-
$arg = ['visibility' => 'private'];
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
*/
public function testVisibilityToggleOff(): void
{
- $this->createValidContainerMockSet();
-
$arg = ['visibility' => 'private'];
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
*/
public function testVisibilitySwitch(): void
{
- $this->createValidContainerMockSet();
-
$arg = ['visibility' => 'private'];
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
*/
public function testVisibilityInvalidValue(): void
{
- $this->createValidContainerMockSet();
-
$arg = ['visibility' => 'test'];
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
*/
public function testVisibilityLoggedOut(): void
{
- $this->createValidContainerMockSet();
-
$arg = ['visibility' => 'test'];
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
*/
public function testUntaggedOnly(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
$request = $this->createMock(Request::class);
*/
public function testUntaggedOnlyToggleOff(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc'];
$request = $this->createMock(Request::class);
public function testDefaultInvokeWithHttps(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testDefaultInvokeWithoutHttps(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testValidIndexControllerInvokeDefault(): void
{
- $this->createValidContainerMockSet();
-
$currentDay = new \DateTimeImmutable('2020-05-13');
$request = $this->createMock(Request::class);
*/
public function testValidIndexControllerInvokeNoFutureOrPast(): void
{
- $this->createValidContainerMockSet();
-
$currentDay = new \DateTimeImmutable('2020-05-13');
$request = $this->createMock(Request::class);
*/
public function testValidIndexControllerInvokeHeightAdjustment(): void
{
- $this->createValidContainerMockSet();
-
$currentDay = new \DateTimeImmutable('2020-05-13');
$request = $this->createMock(Request::class);
*/
public function testValidIndexControllerInvokeNoBookmark(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
*/
public function testValidRssControllerInvokeDefault(): void
{
- $this->createValidContainerMockSet();
-
$dates = [
new \DateTimeImmutable('2020-05-17'),
new \DateTimeImmutable('2020-05-15'),
*/
public function testValidRssControllerInvokeTriggerCache(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
*/
public function testValidRssControllerInvokeNoBookmark(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
*/
public function testDefaultRssController(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
*/
public function testDefaultAtomController(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
*/
public function testAtomControllerWithParameters(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request->method('getParams')->willReturn(['parameter' => 'value']);
$response = new Response();
protected $container;
/**
- * Mock the container instance
+ * Mock the container instance and initialize container's services used by tests
*/
protected function createContainer(): void
{
$this->container = $this->createMock(ShaarliTestContainer::class);
- }
- /**
- * Initialize container's services used by tests
- */
- protected function createValidContainerMockSet(): void
- {
$this->container->loginManager = $this->createMock(LoginManager::class);
// Config
public function testValidControllerInvoke(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request->expects(static::once())->method('getServerParam')->willReturn('> referer');
$response = new Response();
public function testValidControllerInvokeWithUserName(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request->expects(static::once())->method('getServerParam')->willReturn('> referer');
$request->expects(static::exactly(2))->method('getParam')->willReturn('myUser>');
public function testLoginControllerWhileLoggedIn(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testLoginControllerOpenShaarli(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testLoginControllerWhileBanned(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testOpenSearchController(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testValidControllerInvokeDefault(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request->expects(static::once())->method('getQueryParams')->willReturn([]);
$response = new Response();
{
$this->expectException(ThumbnailsDisabledException::class);
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testAssignView(): void
{
- $this->createValidContainerMockSet();
-
$this->assignTemplateVars($this->assignedValues);
$self = $this->controller->assignView('variableName', 'variableValue');
public function testRender(): void
{
- $this->createValidContainerMockSet();
-
$this->assignTemplateVars($this->assignedValues);
$this->container->bookmarkService
*/
public function testRedirectFromRefererDefault(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testRedirectFromRefererWithUnmatchedLoopTerm(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testRedirectFromRefererWithMatchingLoopTermInPath(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testRedirectFromRefererWithMatchingLoopTermInQueryParam(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testRedirectFromRefererWithMatchingLoopTermInQueryValue(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testRedirectFromRefererWithLoopTermInDomain(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testRedirectFromRefererWithMatchingClearedParam(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
$response = new Response();
*/
public function testValidCloudControllerInvokeDefault(): void
{
- $this->createValidContainerMockSet();
-
$allTags = [
'ghi' => 1,
'abc' => 3,
*/
public function testValidCloudControllerInvokeWithParameters(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request
->method('getQueryParam')
*/
public function testEmptyCloud(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
*/
public function testValidListControllerInvokeDefault(): void
{
- $this->createValidContainerMockSet();
-
$allTags = [
'def' => 12,
'abc' => 3,
*/
public function testValidListControllerInvokeWithParameters(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$request
->method('getQueryParam')
*/
public function testEmptyList(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testAddTagWithReferer(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
$request = $this->createMock(Request::class);
public function testAddTagWithRefererAndExistingSearch(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
$request = $this->createMock(Request::class);
public function testAddTagWithoutRefererAndExistingSearch(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testAddTagRemoveLegacyQueryParam(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc'];
$request = $this->createMock(Request::class);
public function testAddTagResetPagination(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12'];
$request = $this->createMock(Request::class);
public function testAddTagWithRefererAndEmptySearch(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags='];
$request = $this->createMock(Request::class);
public function testAddTagWithoutNewTagWithReferer(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
$request = $this->createMock(Request::class);
public function testAddTagWithoutNewTagWithoutReferer(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testRemoveTagWithoutMatchingTag(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
$request = $this->createMock(Request::class);
public function testRemoveTagWithoutTagsearch(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
$request = $this->createMock(Request::class);
public function testRemoveTagWithoutReferer(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
public function testRemoveTagWithoutTag(): void
{
- $this->createValidContainerMockSet();
-
$this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtag=abc'];
$request = $this->createMock(Request::class);
public function testRemoveTagWithoutTagWithoutReferer(): void
{
- $this->createValidContainerMockSet();
-
$request = $this->createMock(Request::class);
$response = new Response();
</div>
{/if}
+{if="!empty($global_errors) && $is_logged_in"}
+ <div class="pure-g new-version-message pure-alert pure-alert-error pure-alert-closable" id="shaarli-errors-alert">
+ <div class="pure-u-2-24"></div>
+ <div class="pure-u-20-24">
+ {loop="$global_errors"}
+ <p>{$value}</p>
+ {/loop}
+ </div>
+ <div class="pure-u-2-24">
+ <i class="fa fa-times pure-alert-close"></i>
+ </div>
+ </div>
+{/if}
+
{if="!empty($global_warnings) && $is_logged_in"}
<div class="pure-g pure-alert pure-alert-warning pure-alert-closable" id="shaarli-warnings-alert">
<div class="pure-u-2-24"></div>
</div>
{/if}
+{if="!empty($global_successes) && $is_logged_in"}
+ <div class="pure-g new-version-message pure-alert pure-alert-success pure-alert-closable" id="shaarli-success-alert">
+ <div class="pure-u-2-24"></div>
+ <div class="pure-u-20-24">
+ {loop="$global_successes"}
+ <p>{$value}</p>
+ {/loop}
+ </div>
+ <div class="pure-u-2-24">
+ <i class="fa fa-times pure-alert-close"></i>
+ </div>
+ </div>
+{/if}
+
<div class="clear"></div>