Add a button to set links as sticky
Meaning that they always appear on top of all links Fixes #186
This commit is contained in:
parent
10a7b5cee9
commit
4154c25b5f
12 changed files with 199 additions and 26 deletions
|
@ -537,6 +537,9 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
$order = $order === 'ASC' ? -1 : 1;
|
||||
// Reorder array by dates.
|
||||
usort($this->links, function($a, $b) use ($order) {
|
||||
if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
|
||||
return $a['sticky'] ? -1 : 1;
|
||||
}
|
||||
return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
|
||||
});
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ class Router
|
|||
|
||||
public static $PAGE_DELETELINK = 'delete_link';
|
||||
|
||||
public static $PAGE_PINLINK = 'pin';
|
||||
|
||||
public static $PAGE_EXPORT = 'export';
|
||||
|
||||
public static $PAGE_IMPORT = 'import';
|
||||
|
@ -146,6 +148,10 @@ class Router
|
|||
return self::$PAGE_DELETELINK;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_PINLINK)) {
|
||||
return self::$PAGE_PINLINK;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_EXPORT)) {
|
||||
return self::$PAGE_EXPORT;
|
||||
}
|
||||
|
|
|
@ -517,6 +517,26 @@ class Updater
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sticky = false on all links
|
||||
*
|
||||
* @return bool true if the update is successful, false otherwise.
|
||||
*/
|
||||
public function updateMethodSetSticky()
|
||||
{
|
||||
foreach ($this->linkDB as $key => $link) {
|
||||
if (isset($link['sticky'])) {
|
||||
return true;
|
||||
}
|
||||
$link['sticky'] = false;
|
||||
$this->linkDB[$key] = $link;
|
||||
}
|
||||
|
||||
$this->linkDB->save($this->conf->get('resource.page_cache'));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -755,6 +755,14 @@ body,
|
|||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.pin-link {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.pinned-link {
|
||||
color: #0b5ea6 !important;
|
||||
}
|
||||
|
||||
.linklist-item-description {
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
|
@ -848,6 +856,10 @@ body,
|
|||
margin: 0 7px;
|
||||
}
|
||||
|
||||
.ctrl-delete {
|
||||
margin: 0 7px 0 0;
|
||||
}
|
||||
|
||||
// 64em -> lg
|
||||
@media screen and (max-width: 64em) {
|
||||
.linklist-item-infos-url {
|
||||
|
|
19
index.php
19
index.php
|
@ -1353,6 +1353,25 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
|
|||
exit;
|
||||
}
|
||||
|
||||
if ($targetPage == Router::$PAGE_PINLINK) {
|
||||
if (! isset($_GET['id']) || empty($LINKSDB[$_GET['id']])) {
|
||||
// FIXME! Use a proper error system.
|
||||
$msg = t('Invalid link ID provided');
|
||||
echo '<script>alert("'. $msg .'");document.location=\''. index_url($_SERVER) .'\';</script>';
|
||||
exit;
|
||||
}
|
||||
if (! $sessionManager->checkToken($_GET['token'])) {
|
||||
die('Wrong token.');
|
||||
}
|
||||
|
||||
$link = $LINKSDB[$_GET['id']];
|
||||
$link['sticky'] = ! $link['sticky'];
|
||||
$LINKSDB[(int) $_GET['id']] = $link;
|
||||
$LINKSDB->save($conf->get('resource.page_cache'));
|
||||
header('Location: '.index_url($_SERVER));
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($targetPage == Router::$PAGE_EXPORT) {
|
||||
// Export links as a Netscape Bookmarks file
|
||||
|
||||
|
|
|
@ -82,8 +82,8 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertFalse($data['usepermalinks']);
|
||||
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
|
||||
|
||||
// Test first link (note link)
|
||||
$link = reset($data['links']);
|
||||
// Test first not pinned link (note link)
|
||||
$link = $data['links'][array_keys($data['links'])[2]];
|
||||
$this->assertEquals(41, $link['id']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
|
||||
$this->assertEquals('http://host.tld/?WDWyig', $link['guid']);
|
||||
|
@ -119,7 +119,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
|
|||
$data = $feedBuilder->buildData();
|
||||
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
|
||||
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
|
||||
$link = reset($data['links']);
|
||||
$link = $data['links'][array_keys($data['links'])[2]];
|
||||
$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']);
|
||||
}
|
||||
|
@ -148,13 +148,13 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
|
|||
public function testBuildDataCount()
|
||||
{
|
||||
$criteria = array(
|
||||
'nb' => '1',
|
||||
'nb' => '3',
|
||||
);
|
||||
$feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, $criteria, false);
|
||||
$feedBuilder->setLocale(self::$LOCALE);
|
||||
$data = $feedBuilder->buildData();
|
||||
$this->assertEquals(1, count($data['links']));
|
||||
$link = array_shift($data['links']);
|
||||
$this->assertEquals(3, count($data['links']));
|
||||
$link = $data['links'][array_keys($data['links'])[2]];
|
||||
$this->assertEquals(41, $link['id']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
|
||||
$this->assertTrue($data['usepermalinks']);
|
||||
// First link is a permalink
|
||||
$link = array_shift($data['links']);
|
||||
$link = $data['links'][array_keys($data['links'])[2]];
|
||||
$this->assertEquals(41, $link['id']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
|
||||
$this->assertEquals('http://host.tld/?WDWyig', $link['guid']);
|
||||
|
@ -179,7 +179,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertContains('Direct link', $link['description']);
|
||||
$this->assertContains('http://host.tld/?WDWyig', $link['description']);
|
||||
// Second link is a direct link
|
||||
$link = array_shift($data['links']);
|
||||
$link = $data['links'][array_keys($data['links'])[3]];
|
||||
$this->assertEquals(8, $link['id']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
|
||||
$this->assertEquals('http://host.tld/?RttfEw', $link['guid']);
|
||||
|
@ -237,7 +237,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
|
|||
);
|
||||
|
||||
// Test first link (note link)
|
||||
$link = array_shift($data['links']);
|
||||
$link = $data['links'][array_keys($data['links'])[2]];
|
||||
$this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']);
|
||||
$this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']);
|
||||
$this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']);
|
||||
|
|
|
@ -239,12 +239,12 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
|
|||
public function testDays()
|
||||
{
|
||||
$this->assertEquals(
|
||||
array('20100310', '20121206', '20130614', '20150310'),
|
||||
array('20100309', '20100310', '20121206', '20121207', '20130614', '20150310'),
|
||||
self::$publicLinkDB->days()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array('20100310', '20121206', '20130614', '20141125', '20150310'),
|
||||
array('20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'),
|
||||
self::$privateLinkDB->days()
|
||||
);
|
||||
}
|
||||
|
@ -475,13 +475,15 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
|
|||
public function testReorderLinksDesc()
|
||||
{
|
||||
self::$privateLinkDB->reorder('ASC');
|
||||
$linkIds = array(42, 4, 9, 1, 0, 7, 6, 8, 41);
|
||||
$stickyIds = [11, 10];
|
||||
$standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
|
||||
$linkIds = array_merge($stickyIds, $standardIds);
|
||||
$cpt = 0;
|
||||
foreach (self::$privateLinkDB as $key => $value) {
|
||||
$this->assertEquals($linkIds[$cpt++], $key);
|
||||
}
|
||||
self::$privateLinkDB->reorder('DESC');
|
||||
$linkIds = array_reverse($linkIds);
|
||||
$linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
|
||||
$cpt = 0;
|
||||
foreach (self::$privateLinkDB as $key => $value) {
|
||||
$this->assertEquals($linkIds[$cpt++], $key);
|
||||
|
|
|
@ -110,7 +110,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
|
|||
$links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, '');
|
||||
$this->assertEquals(
|
||||
'?WDWyig',
|
||||
$links[0]['url']
|
||||
$links[2]['url']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
|
|||
);
|
||||
$this->assertEquals(
|
||||
$indexUrl . '?WDWyig',
|
||||
$links[0]['url']
|
||||
$links[2]['url']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -688,6 +688,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
|
|||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* Test updateMethodWebThumbnailer with thumbnails enabled.
|
||||
*/
|
||||
public function testUpdateMethodWebThumbnailerEnabled()
|
||||
|
@ -732,4 +733,64 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
|
|||
$this->assertEquals(53, $this->conf->get('thumbnails.height'));
|
||||
$this->assertTrue(empty($_SESSION['warnings']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updateMethodSetSticky().
|
||||
*/
|
||||
public function testUpdateStickyValid()
|
||||
{
|
||||
$blank = [
|
||||
'id' => 1,
|
||||
'url' => 'z',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'tags' => '',
|
||||
'created' => new DateTime(),
|
||||
];
|
||||
$links = [
|
||||
1 => ['id' => 1] + $blank,
|
||||
2 => ['id' => 2] + $blank,
|
||||
];
|
||||
$refDB = new ReferenceLinkDB();
|
||||
$refDB->setLinks($links);
|
||||
$refDB->write(self::$testDatastore);
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
||||
$updater = new Updater(array(), $linkDB, $this->conf, true);
|
||||
$this->assertTrue($updater->updateMethodSetSticky());
|
||||
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
foreach ($linkDB as $link) {
|
||||
$this->assertFalse($link['sticky']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updateMethodSetSticky().
|
||||
*/
|
||||
public function testUpdateStickyNothingToDo()
|
||||
{
|
||||
$blank = [
|
||||
'id' => 1,
|
||||
'url' => 'z',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'tags' => '',
|
||||
'created' => new DateTime(),
|
||||
];
|
||||
$links = [
|
||||
1 => ['id' => 1, 'sticky' => true] + $blank,
|
||||
2 => ['id' => 2] + $blank,
|
||||
];
|
||||
$refDB = new ReferenceLinkDB();
|
||||
$refDB->setLinks($links);
|
||||
$refDB->write(self::$testDatastore);
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
||||
$updater = new Updater(array(), $linkDB, $this->conf, true);
|
||||
$this->assertTrue($updater->updateMethodSetSticky());
|
||||
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
$this->assertTrue($linkDB[1]['sticky']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEquals($this->refDB->countLinks(), count($data));
|
||||
|
||||
// Check order
|
||||
$order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
|
||||
$order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
|
||||
$cpt = 0;
|
||||
foreach ($data as $link) {
|
||||
$this->assertEquals(self::NB_FIELDS_LINK, count($link));
|
||||
|
@ -103,7 +103,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
}
|
||||
|
||||
// Check first element fields
|
||||
$first = $data[0];
|
||||
$first = $data[2];
|
||||
$this->assertEquals('http://domain.tld/?WDWyig', $first['url']);
|
||||
$this->assertEquals('WDWyig', $first['shorturl']);
|
||||
$this->assertEquals('Link title: @website', $first['title']);
|
||||
|
@ -120,7 +120,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEmpty($first['updated']);
|
||||
|
||||
// Multi tags
|
||||
$link = $data[1];
|
||||
$link = $data[3];
|
||||
$this->assertEquals(7, count($link['tags']));
|
||||
|
||||
// Update date
|
||||
|
@ -138,7 +138,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$env = Environment::mock([
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'QUERY_STRING' => 'offset=1&limit=1'
|
||||
'QUERY_STRING' => 'offset=3&limit=1'
|
||||
]);
|
||||
$request = Request::createFromEnvironment($env);
|
||||
$response = $this->controller->getLinks($request, new Response());
|
||||
|
@ -164,7 +164,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
$data = json_decode((string) $response->getBody(), true);
|
||||
$this->assertEquals($this->refDB->countLinks(), count($data));
|
||||
// Check order
|
||||
$order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
|
||||
$order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
|
||||
$cpt = 0;
|
||||
foreach ($data as $link) {
|
||||
$this->assertEquals(self::NB_FIELDS_LINK, count($link));
|
||||
|
@ -205,7 +205,8 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode((string)$response->getBody(), true);
|
||||
$this->assertEquals($this->refDB->countLinks(), count($data));
|
||||
$this->assertEquals(41, $data[0]['id']);
|
||||
$this->assertEquals(10, $data[0]['id']);
|
||||
$this->assertEquals(41, $data[2]['id']);
|
||||
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
|
||||
}
|
||||
|
||||
|
@ -243,7 +244,8 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode((string)$response->getBody(), true);
|
||||
$this->assertEquals($this->refDB->countPublicLinks(), count($data));
|
||||
$this->assertEquals(41, $data[0]['id']);
|
||||
$this->assertEquals(10, $data[0]['id']);
|
||||
$this->assertEquals(41, $data[2]['id']);
|
||||
$this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
|
||||
}
|
||||
|
||||
|
@ -413,8 +415,9 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
|
|||
$response = $this->controller->getLinks($request, new Response());
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode((string) $response->getBody(), true);
|
||||
$this->assertEquals(9, count($data));
|
||||
$this->assertEquals(41, $data[0]['id']);
|
||||
$this->assertEquals(\ReferenceLinkDB::$NB_LINKS_TOTAL, count($data));
|
||||
$this->assertEquals(10, $data[0]['id']);
|
||||
$this->assertEquals(41, $data[2]['id']);
|
||||
|
||||
// wildcard: optional ('*' does not need to expand)
|
||||
$env = Environment::mock([
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
class ReferenceLinkDB
|
||||
{
|
||||
public static $NB_LINKS_TOTAL = 9;
|
||||
public static $NB_LINKS_TOTAL = 11;
|
||||
|
||||
private $_links = array();
|
||||
private $_publicCount = 0;
|
||||
|
@ -15,6 +15,32 @@ class ReferenceLinkDB
|
|||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->addLink(
|
||||
11,
|
||||
'Pined older',
|
||||
'?PCRizQ',
|
||||
'This is an older pinned link',
|
||||
0,
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100309_101010'),
|
||||
'',
|
||||
null,
|
||||
'PCRizQ',
|
||||
true
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
10,
|
||||
'Pined',
|
||||
'?0gCTjQ',
|
||||
'This is a pinned link',
|
||||
0,
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121207_152312'),
|
||||
'',
|
||||
null,
|
||||
'0gCTjQ',
|
||||
true
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
41,
|
||||
'Link title: @website',
|
||||
|
@ -114,7 +140,17 @@ class ReferenceLinkDB
|
|||
/**
|
||||
* Adds a new link
|
||||
*/
|
||||
protected function addLink($id, $title, $url, $description, $private, $date, $tags, $updated = '', $shorturl = '')
|
||||
protected function addLink(
|
||||
$id,
|
||||
$title,
|
||||
$url,
|
||||
$description,
|
||||
$private,
|
||||
$date,
|
||||
$tags,
|
||||
$updated = '',
|
||||
$shorturl = '',
|
||||
$pinned = false)
|
||||
{
|
||||
$link = array(
|
||||
'id' => $id,
|
||||
|
@ -126,6 +162,7 @@ class ReferenceLinkDB
|
|||
'created' => $date,
|
||||
'updated' => $updated,
|
||||
'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id),
|
||||
'sticky' => $pinned
|
||||
);
|
||||
$this->_links[$id] = $link;
|
||||
|
||||
|
@ -165,6 +202,10 @@ class ReferenceLinkDB
|
|||
$order = $order === 'ASC' ? -1 : 1;
|
||||
// Reorder array by dates.
|
||||
usort($this->_links, function($a, $b) use ($order) {
|
||||
if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
|
||||
return $a['sticky'] ? -1 : 1;
|
||||
}
|
||||
|
||||
return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -201,6 +201,12 @@
|
|||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
</span>
|
||||
<span class="linklist-item-infos-controls-item ctrl-pin">
|
||||
<a href="?do=pin&id={$value.id}&token={$token}"
|
||||
title="{$strDelete}" class="pin-link {if="$value.sticky"}pinned-link{/if} pure-u-0 pure-u-lg-visible">
|
||||
<i class="fa fa-thumb-tack"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
<a href="?{$value.shorturl}" title="{$strPermalink}">
|
||||
|
|
Loading…
Reference in a new issue