*
* Also update the urls and ids mapping arrays.
*
- * @param string $order ASC|DESC
+ * @param string $order ASC|DESC
+ * @param bool $ignoreSticky If set to true, sticky bookmarks won't be first
*/
- public function reorder($order = 'DESC')
+ public function reorder(string $order = 'DESC', bool $ignoreSticky = false): void
{
$order = $order === 'ASC' ? -1 : 1;
// Reorder array by dates.
- usort($this->bookmarks, function ($a, $b) use ($order) {
+ usort($this->bookmarks, function ($a, $b) use ($order, $ignoreSticky) {
/** @var $a Bookmark */
/** @var $b Bookmark */
- if ($a->isSticky() !== $b->isSticky()) {
+ if (false === $ignoreSticky && $a->isSticky() !== $b->isSticky()) {
return $a->isSticky() ? -1 : 1;
}
return $a->getCreated() < $b->getCreated() ? 1 * $order : -1 * $order;
/**
* @inheritDoc
*/
- public function search($request = [], $visibility = null, $caseSensitive = false, $untaggedOnly = false)
- {
+ public function search(
+ $request = [],
+ $visibility = null,
+ $caseSensitive = false,
+ $untaggedOnly = false,
+ bool $ignoreSticky = false
+ ) {
if ($visibility === null) {
$visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC;
}
$searchtags = isset($request['searchtags']) ? $request['searchtags'] : '';
$searchterm = isset($request['searchterm']) ? $request['searchterm'] : '';
+ if ($ignoreSticky) {
+ $this->bookmarks->reorder('DESC', true);
+ }
+
return $this->bookmarkFilter->filter(
BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
[$searchtags, $searchterm],
* @param string $visibility
* @param bool $caseSensitive
* @param bool $untaggedOnly
+ * @param bool $ignoreSticky
*
* @return Bookmark[]
*/
- public function search($request = [], $visibility = null, $caseSensitive = false, $untaggedOnly = false);
+ public function search(
+ $request = [],
+ $visibility = null,
+ $caseSensitive = false,
+ $untaggedOnly = false,
+ bool $ignoreSticky = false
+ );
/**
* Get a single bookmark by its ID.
}
// Optionally filter the results:
- $linksToDisplay = $this->linkDB->search($userInput);
+ $linksToDisplay = $this->linkDB->search($userInput, null, false, false, true);
$nblinksToDisplay = $this->getNbLinks(count($linksToDisplay), $userInput);
&& $this->container->conf->get('privacy.force_login')
// and the current page isn't already the login page
// and the user is not requesting a feed (which would lead to a different content-type as expected)
- && !in_array($next->getName(), ['login', 'atom', 'rss'], true)
+ && !in_array($next->getName(), ['login', 'processLogin', 'atom', 'rss'], true)
) {
throw new UnauthorizedException();
}
public function post(Request $request, Response $response): Response
{
$parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : '';
+ $route = '/admin/shaare';
if (!$this->container->loginManager->isLoggedIn()) {
- return $this->redirect($response, '/login' . $parameters);
+ return $this->redirect($response, '/login?returnurl='. $this->getBasePath() . $route . $parameters);
}
- return $this->redirect($response, '/admin/shaare' . $parameters);
+ return $this->redirect($response, $route . $parameters);
}
/** Legacy route: ?addlink= */
protected function addlink(Request $request, Response $response): Response
{
+ $route = '/admin/add-shaare';
+
if (!$this->container->loginManager->isLoggedIn()) {
- return $this->redirect($response, '/login');
+ return $this->redirect($response, '/login?returnurl=' . $this->getBasePath() . $route);
}
- return $this->redirect($response, '/admin/add-shaare');
+ return $this->redirect($response, $route);
}
/** Legacy route: ?do=login */
protected function login(Request $request, Response $response): Response
{
- return $this->redirect($response, '/login');
+ $returnUrl = $request->getQueryParam('returnurl');
+
+ return $this->redirect($response, '/login' . ($returnUrl ? '?returnurl=' . $returnUrl : ''));
}
/** Legacy route: ?do=logout */
return $this->redirect($response, '/feed/' . $feedType . $parameters);
}
+
+ /** Legacy route: ?do=configure */
+ protected function configure(Request $request, Response $response): Response
+ {
+ $route = '/admin/configure';
+
+ if (!$this->container->loginManager->isLoggedIn()) {
+ return $this->redirect($response, '/login?returnurl=' . $this->getBasePath() . $route);
+ }
+
+ return $this->redirect($response, $route);
+ }
+
+ protected function getBasePath(): string
+ {
+ return $this->container->basePath ?: '';
+ }
}
unset($this->session['expires_on']);
unset($this->session['username']);
unset($this->session['visibility']);
- unset($this->session['untaggedonly']);
}
}
/**
* Ajax request to refresh the CSRF token.
*/
-function refreshToken(basePath) {
- console.log('refresh');
+function refreshToken(basePath, callback) {
const xhr = new XMLHttpRequest();
xhr.open('GET', `${basePath}/admin/token`);
xhr.onload = () => {
const elements = document.querySelectorAll('input[name="token"]');
[...elements].forEach((element) => {
- console.log(element);
element.setAttribute('value', xhr.responseText);
});
+
+ if (callback) {
+ callback(xhr.response);
+ }
};
xhr.send();
}
[...autocompleteFields].forEach((autocompleteField) => {
awesomepletes.push(createAwesompleteInstance(autocompleteField));
});
+
+ const exportForm = document.querySelector('#exportform');
+ if (exportForm != null) {
+ exportForm.addEventListener('submit', (event) => {
+ event.preventDefault();
+
+ refreshToken(basePath, () => {
+ event.target.submit();
+ });
+ });
+ }
})();
$this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\Visitor\TagController:addTag');
$this->get('/remove-tag/{tag}', '\Shaarli\Front\Controller\Visitor\TagController:removeTag');
$this->get('/links-per-page', '\Shaarli\Front\Controller\Visitor\PublicSessionFilterController:linksPerPage');
- $this->get('/untagged-only', '\Shaarli\Front\Controller\Admin\PublicSessionFilterController:untaggedOnly');
+ $this->get('/untagged-only', '\Shaarli\Front\Controller\Visitor\PublicSessionFilterController:untaggedOnly');
})->add('\Shaarli\Front\ShaarliMiddleware');
$app->group('/admin', function () {
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
// Test first not pinned link (note link)
- $link = $data['links'][array_keys($data['links'])[2]];
+ $link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals(41, $link['id']);
$this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
$this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
- $link = $data['links'][array_keys($data['links'])[2]];
+ $link = $data['links'][array_keys($data['links'])[0]];
$this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
}
$feedBuilder->setLocale(self::$LOCALE);
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
$this->assertEquals(3, count($data['links']));
- $link = $data['links'][array_keys($data['links'])[2]];
+ $link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals(41, $link['id']);
$this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
}
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
$this->assertTrue($data['usepermalinks']);
// First link is a permalink
- $link = $data['links'][array_keys($data['links'])[2]];
+ $link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals(41, $link['id']);
$this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
$this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
$this->assertContains('Direct link', $link['description']);
$this->assertContains('http://host.tld/shaare/WDWyig', $link['description']);
// Second link is a direct link
- $link = $data['links'][array_keys($data['links'])[3]];
+ $link = $data['links'][array_keys($data['links'])[1]];
$this->assertEquals(8, $link['id']);
$this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
$this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']);
);
// Test first link (note link)
- $link = $data['links'][array_keys($data['links'])[2]];
+ $link = $data['links'][array_keys($data['links'])[0]];
$this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']);
$this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']);
$this->assertContains('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
{
return [
['post', [], '/admin/shaare', true],
- ['post', [], '/login', false],
+ ['post', [], '/login?returnurl=/subfolder/admin/shaare', false],
['post', ['title' => 'test'], '/admin/shaare?title=test', true],
- ['post', ['title' => 'test'], '/login?title=test', false],
+ ['post', ['title' => 'test'], '/login?returnurl=/subfolder/admin/shaare?title=test', false],
['addlink', [], '/admin/add-shaare', true],
- ['addlink', [], '/login', false],
+ ['addlink', [], '/login?returnurl=/subfolder/admin/add-shaare', false],
['login', [], '/login', true],
['login', [], '/login', false],
['logout', [], '/admin/logout', true],
['opensearch', [], '/open-search', true],
['dailyrss', [], '/daily-rss', false],
['dailyrss', [], '/daily-rss', true],
+ ['configure', [], '/login?returnurl=/subfolder/admin/configure', false],
+ ['configure', [], '/admin/configure', true],
];
}
}
'expires_on' => time() + 1000,
'username' => 'johndoe',
'visibility' => 'public',
- 'untaggedonly' => false,
+ 'untaggedonly' => true,
];
$this->sessionManager->logout();
- $this->assertFalse(isset($this->session['ip']));
- $this->assertFalse(isset($this->session['expires_on']));
- $this->assertFalse(isset($this->session['username']));
- $this->assertFalse(isset($this->session['visibility']));
- $this->assertFalse(isset($this->session['untaggedonly']));
+ $this->assertArrayNotHasKey('ip', $this->session);
+ $this->assertArrayNotHasKey('expires_on', $this->session);
+ $this->assertArrayNotHasKey('username', $this->session);
+ $this->assertArrayNotHasKey('visibility', $this->session);
+ $this->assertArrayHasKey('untaggedonly', $this->session);
+ $this->assertTrue($this->session['untaggedonly']);
}
/**
<div class="linklist-paging">
<div class="paging pure-g">
<div class="linklist-filters pure-u-1-3">
- {if="$is_logged_in or !empty($action_plugin)"}
- <span class="linklist-filters-text pure-u-0 pure-u-lg-visible">
- {'Filters'|t}
- </span>
- {if="$is_logged_in"}
- <a href="{$base_path}/admin/visibility/private" aria-label="{'Only display private links'|t}" title="{'Only display private links'|t}"
- class="{if="$visibility==='private'"}filter-on{else}filter-off{/if}"
- ><i class="fa fa-user-secret" aria-hidden="true"></i></a>
- <a href="{$base_path}/admin/visibility/public" aria-label="{'Only display public links'|t}" title="{'Only display public links'|t}"
- class="{if="$visibility==='public'"}filter-on{else}filter-off{/if}"
- ><i class="fa fa-globe" aria-hidden="true"></i></a>
- {/if}
- <a href="{$base_path}/untagged-only" aria-label="{'Filter untagged links'|t}" title="{'Filter untagged links'|t}"
- class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if}
- ><i class="fa fa-tag" aria-hidden="true"></i></a>
- <a href="#" aria-label="{'Select all'|t}" title="{'Select all'|t}"
- class="filter-off select-all-button pure-u-0 pure-u-lg-visible"
- ><i class="fa fa-check-square-o" aria-hidden="true"></i></a>
- <a href="#" class="filter-off fold-all pure-u-lg-0" aria-label="{'Fold all'|t}" title="{'Fold all'|t}">
- <i class="fa fa-chevron-up" aria-hidden="true"></i>
- </a>
+ <span class="linklist-filters-text pure-u-0 pure-u-lg-visible">
+ {'Filters'|t}
+ </span>
+ {if="$is_logged_in"}
+ <a href="{$base_path}/admin/visibility/private" aria-label="{'Only display private links'|t}" title="{'Only display private links'|t}"
+ class="{if="$visibility==='private'"}filter-on{else}filter-off{/if}"
+ ><i class="fa fa-user-secret" aria-hidden="true"></i></a>
+ <a href="{$base_path}/admin/visibility/public" aria-label="{'Only display public links'|t}" title="{'Only display public links'|t}"
+ class="{if="$visibility==='public'"}filter-on{else}filter-off{/if}"
+ ><i class="fa fa-globe" aria-hidden="true"></i></a>
+ {/if}
+ <a href="{$base_path}/untagged-only" aria-label="{'Filter untagged links'|t}" title="{'Filter untagged links'|t}"
+ class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if}
+ ><i class="fa fa-tag" aria-hidden="true"></i></a>
+ {if="$is_logged_in"}
+ <a href="#" aria-label="{'Select all'|t}" title="{'Select all'|t}"
+ class="filter-off select-all-button pure-u-0 pure-u-lg-visible"
+ ><i class="fa fa-check-square-o" aria-hidden="true"></i></a>
+ {/if}
+ <a href="#" class="filter-off fold-all pure-u-lg-0" aria-label="{'Fold all'|t}" title="{'Fold all'|t}">
+ <i class="fa fa-chevron-up" aria-hidden="true"></i>
+ </a>
+ {if="!empty($action_plugin)"}
{loop="$action_plugin"}
{$value.attr.class=isset($value.attr.class) ? $value.attr.class : ''}
{$value.attr.class=!empty($value.on) ? $value.attr.class .' filter-on' : $value.attr.class .' filter-off'}
inherits "~2.0.0"
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.8"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
- integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+ version "4.11.9"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
+ integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
brace-expansion@^1.1.7:
version "1.1.11"
integrity sha512-xXLNstRdVsisPF3pL3H9TVZo2XkMILfqtD6RiWIUmDK2sFX1Bjwqmd8LBp0Kuo2FgKO63JXPoEVGm8WyYdwP0Q==
elliptic@^6.0.0:
- version "6.4.1"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a"
- integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
+ integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
- integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
+inherits@2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+ integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.3.0, lodash@~4.17.10:
- version "4.17.15"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
- integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+ version "4.17.19"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
+ integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
longest@^1.0.1:
version "1.0.1"