Merge pull request #697 from ArthurHoaro/feature/ids-bis
Link ID refactoring
This commit is contained in:
commit
9cf93bcfc5
20 changed files with 618 additions and 234 deletions
|
@ -143,7 +143,7 @@ public function buildData()
|
|||
*/
|
||||
protected function buildItem($link, $pageaddr)
|
||||
{
|
||||
$link['guid'] = $pageaddr .'?'. smallHash($link['linkdate']);
|
||||
$link['guid'] = $pageaddr .'?'. $link['shorturl'];
|
||||
// Check for both signs of a note: starting with ? and 7 chars long.
|
||||
if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
|
||||
$link['url'] = $pageaddr . $link['url'];
|
||||
|
@ -156,12 +156,12 @@ protected function buildItem($link, $pageaddr)
|
|||
$link['description'] = format_description($link['description'], '', $pageaddr);
|
||||
$link['description'] .= PHP_EOL .'<br>— '. $permalink;
|
||||
|
||||
$pubDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$pubDate = $link['created'];
|
||||
$link['pub_iso_date'] = $this->getIsoDate($pubDate);
|
||||
|
||||
// atom:entry elements MUST contain exactly one atom:updated element.
|
||||
if (!empty($link['updated'])) {
|
||||
$upDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['updated']);
|
||||
$upDate = $link['updated'];
|
||||
$link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM);
|
||||
} else {
|
||||
$link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);;
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
*
|
||||
* Example:
|
||||
* $myLinks = new LinkDB();
|
||||
* echo $myLinks['20110826_161819']['title'];
|
||||
* echo $myLinks[350]['title'];
|
||||
* foreach ($myLinks as $link)
|
||||
* echo $link['title'].' at url '.$link['url'].'; description:'.$link['description'];
|
||||
*
|
||||
* Available keys:
|
||||
* - id: primary key, incremental integer identifier (persistent)
|
||||
* - description: description of the entry
|
||||
* - linkdate: creation date of this entry, format: YYYYMMDD_HHMMSS
|
||||
* (e.g.'20110914_192317')
|
||||
* - updated: last modification date of this entry, format: YYYYMMDD_HHMMSS
|
||||
* - created: creation date of this entry, DateTime object.
|
||||
* - updated: last modification date of this entry, DateTime object.
|
||||
* - private: Is this link private? 0=no, other value=yes
|
||||
* - tags: tags attached to this entry (separated by spaces)
|
||||
* - title Title of the link
|
||||
|
@ -22,11 +22,25 @@
|
|||
* Can be absolute or relative.
|
||||
* Relative URLs are permalinks (e.g.'?m-ukcw')
|
||||
* - real_url Absolute processed URL.
|
||||
* - shorturl Permalink smallhash
|
||||
*
|
||||
* Implements 3 interfaces:
|
||||
* - ArrayAccess: behaves like an associative array;
|
||||
* - Countable: there is a count() method;
|
||||
* - Iterator: usable in foreach () loops.
|
||||
*
|
||||
* ID mechanism:
|
||||
* ArrayAccess is implemented in a way that will allow to access a link
|
||||
* with the unique identifier ID directly with $link[ID].
|
||||
* Note that it's not the real key of the link array attribute.
|
||||
* This mechanism is in place to have persistent link IDs,
|
||||
* even though the internal array is reordered by date.
|
||||
* Example:
|
||||
* - DB: link #1 (2010-01-01) link #2 (2016-01-01)
|
||||
* - Order: #2 #1
|
||||
* - Import links containing: link #3 (2013-01-01)
|
||||
* - New DB: link #1 (2010-01-01) link #2 (2016-01-01) link #3 (2013-01-01)
|
||||
* - Real order: #2 #3 #1
|
||||
*/
|
||||
class LinkDB implements Iterator, Countable, ArrayAccess
|
||||
{
|
||||
|
@ -47,11 +61,17 @@ class LinkDB implements Iterator, Countable, ArrayAccess
|
|||
// - value: associative array (keys: title, description...)
|
||||
private $links;
|
||||
|
||||
// List of all recorded URLs (key=url, value=linkdate)
|
||||
// for fast reserve search (url-->linkdate)
|
||||
// List of all recorded URLs (key=url, value=link offset)
|
||||
// for fast reserve search (url-->link offset)
|
||||
private $urls;
|
||||
|
||||
// List of linkdate keys (for the Iterator interface implementation)
|
||||
/**
|
||||
* @var array List of all links IDS mapped with their array offset.
|
||||
* Map: id->offset.
|
||||
*/
|
||||
protected $ids;
|
||||
|
||||
// List of offset keys (for the Iterator interface implementation)
|
||||
private $keys;
|
||||
|
||||
// Position in the $this->keys array (for the Iterator interface)
|
||||
|
@ -121,14 +141,26 @@ public function offsetSet($offset, $value)
|
|||
if (!$this->loggedIn) {
|
||||
die('You are not authorized to add a link.');
|
||||
}
|
||||
if (empty($value['linkdate']) || empty($value['url'])) {
|
||||
die('Internal Error: A link should always have a linkdate and URL.');
|
||||
if (!isset($value['id']) || empty($value['url'])) {
|
||||
die('Internal Error: A link should always have an id and URL.');
|
||||
}
|
||||
if (empty($offset)) {
|
||||
die('You must specify a key.');
|
||||
if ((! empty($offset) && ! is_int($offset)) || ! is_int($value['id'])) {
|
||||
die('You must specify an integer as a key.');
|
||||
}
|
||||
if (! empty($offset) && $offset !== $value['id']) {
|
||||
die('Array offset and link ID must be equal.');
|
||||
}
|
||||
|
||||
// If the link exists, we reuse the real offset, otherwise new entry
|
||||
$existing = $this->getLinkOffset($offset);
|
||||
if ($existing !== null) {
|
||||
$offset = $existing;
|
||||
} else {
|
||||
$offset = count($this->links);
|
||||
}
|
||||
$this->links[$offset] = $value;
|
||||
$this->urls[$value['url']]=$offset;
|
||||
$this->urls[$value['url']] = $offset;
|
||||
$this->ids[$value['id']] = $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,7 +168,7 @@ public function offsetSet($offset, $value)
|
|||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->links);
|
||||
return array_key_exists($this->getLinkOffset($offset), $this->links);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,9 +180,11 @@ public function offsetUnset($offset)
|
|||
// TODO: raise an exception
|
||||
die('You are not authorized to delete a link.');
|
||||
}
|
||||
$url = $this->links[$offset]['url'];
|
||||
$realOffset = $this->getLinkOffset($offset);
|
||||
$url = $this->links[$realOffset]['url'];
|
||||
unset($this->urls[$url]);
|
||||
unset($this->links[$offset]);
|
||||
unset($this->ids[$realOffset]);
|
||||
unset($this->links[$realOffset]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,7 +192,8 @@ public function offsetUnset($offset)
|
|||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->links[$offset]) ? $this->links[$offset] : null;
|
||||
$realOffset = $this->getLinkOffset($offset);
|
||||
return isset($this->links[$realOffset]) ? $this->links[$realOffset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,7 +201,7 @@ public function offsetGet($offset)
|
|||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->links[$this->keys[$this->position]];
|
||||
return $this[$this->keys[$this->position]];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,8 +227,7 @@ public function next()
|
|||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->keys = array_keys($this->links);
|
||||
rsort($this->keys);
|
||||
$this->keys = array_keys($this->ids);
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
|
@ -219,6 +253,7 @@ private function check()
|
|||
// Create a dummy database for example
|
||||
$this->links = array();
|
||||
$link = array(
|
||||
'id' => 1,
|
||||
'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone',
|
||||
'url'=>'https://github.com/shaarli/Shaarli/wiki',
|
||||
'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login.
|
||||
|
@ -227,20 +262,23 @@ private function check()
|
|||
|
||||
You use the community supported version of the original Shaarli project, by Sebastien Sauvage.',
|
||||
'private'=>0,
|
||||
'linkdate'=> date('Ymd_His'),
|
||||
'created'=> new DateTime(),
|
||||
'tags'=>'opensource software'
|
||||
);
|
||||
$this->links[$link['linkdate']] = $link;
|
||||
$link['shorturl'] = link_small_hash($link['created'], $link['id']);
|
||||
$this->links[1] = $link;
|
||||
|
||||
$link = array(
|
||||
'id' => 0,
|
||||
'title'=>'My secret stuff... - Pastebin.com',
|
||||
'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=',
|
||||
'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.',
|
||||
'private'=>1,
|
||||
'linkdate'=> date('Ymd_His', strtotime('-1 minute')),
|
||||
'tags'=>'secretstuff'
|
||||
'created'=> new DateTime('1 minute ago'),
|
||||
'tags'=>'secretstuff',
|
||||
);
|
||||
$this->links[$link['linkdate']] = $link;
|
||||
$link['shorturl'] = link_small_hash($link['created'], $link['id']);
|
||||
$this->links[0] = $link;
|
||||
|
||||
// Write database to disk
|
||||
$this->write();
|
||||
|
@ -251,7 +289,6 @@ private function check()
|
|||
*/
|
||||
private function read()
|
||||
{
|
||||
|
||||
// Public links are hidden and user not logged in => nothing to show
|
||||
if ($this->hidePublicLinks && !$this->loggedIn) {
|
||||
$this->links = array();
|
||||
|
@ -269,23 +306,13 @@ private function read()
|
|||
strlen(self::$phpPrefix), -strlen(self::$phpSuffix)))));
|
||||
}
|
||||
|
||||
// If user is not logged in, filter private links.
|
||||
if (!$this->loggedIn) {
|
||||
$toremove = array();
|
||||
foreach ($this->links as $link) {
|
||||
if ($link['private'] != 0) {
|
||||
$toremove[] = $link['linkdate'];
|
||||
foreach ($this->links as $key => &$link) {
|
||||
if (! $this->loggedIn && $link['private'] != 0) {
|
||||
// Transition for not upgraded databases.
|
||||
$toremove[] = $key;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foreach ($toremove as $linkdate) {
|
||||
unset($this->links[$linkdate]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->urls = array();
|
||||
foreach ($this->links as &$link) {
|
||||
// Keep the list of the mapping URLs-->linkdate up-to-date.
|
||||
$this->urls[$link['url']] = $link['linkdate'];
|
||||
|
||||
// Sanitize data fields.
|
||||
sanitizeLink($link);
|
||||
|
@ -307,7 +334,24 @@ private function read()
|
|||
else {
|
||||
$link['real_url'] = $link['url'];
|
||||
}
|
||||
|
||||
// To be able to load links before running the update, and prepare the update
|
||||
if (! isset($link['created'])) {
|
||||
$link['id'] = $link['linkdate'];
|
||||
$link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
if (! empty($link['updated'])) {
|
||||
$link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']);
|
||||
}
|
||||
$link['shorturl'] = smallHash($link['linkdate']);
|
||||
}
|
||||
}
|
||||
|
||||
// If user is not logged in, filter private links.
|
||||
foreach ($toremove as $offset) {
|
||||
unset($this->links[$offset]);
|
||||
}
|
||||
|
||||
$this->reorder();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -430,7 +474,7 @@ public function filterSearch($filterRequest = array(), $casesensitive = false, $
|
|||
$request = '';
|
||||
}
|
||||
|
||||
$linkFilter = new LinkFilter($this->links);
|
||||
$linkFilter = new LinkFilter($this);
|
||||
return $linkFilter->filter($type, $request, $casesensitive, $privateonly);
|
||||
}
|
||||
|
||||
|
@ -467,12 +511,64 @@ public function allTags()
|
|||
public function days()
|
||||
{
|
||||
$linkDays = array();
|
||||
foreach (array_keys($this->links) as $day) {
|
||||
$linkDays[substr($day, 0, 8)] = 0;
|
||||
foreach ($this->links as $link) {
|
||||
$linkDays[$link['created']->format('Ymd')] = 0;
|
||||
}
|
||||
$linkDays = array_keys($linkDays);
|
||||
sort($linkDays);
|
||||
|
||||
return $linkDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder links by creation date (newest first).
|
||||
*
|
||||
* Also update the urls and ids mapping arrays.
|
||||
*
|
||||
* @param string $order ASC|DESC
|
||||
*/
|
||||
public function reorder($order = 'DESC')
|
||||
{
|
||||
$order = $order === 'ASC' ? -1 : 1;
|
||||
// Reorder array by dates.
|
||||
usort($this->links, function($a, $b) use ($order) {
|
||||
return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
|
||||
});
|
||||
|
||||
$this->urls = array();
|
||||
$this->ids = array();
|
||||
foreach ($this->links as $key => $link) {
|
||||
$this->urls[$link['url']] = $key;
|
||||
$this->ids[$link['id']] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next key for link creation.
|
||||
* E.g. If the last ID is 597, the next will be 598.
|
||||
*
|
||||
* @return int next ID.
|
||||
*/
|
||||
public function getNextId()
|
||||
{
|
||||
if (!empty($this->ids)) {
|
||||
return max(array_keys($this->ids)) + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a link offset in links array from its unique ID.
|
||||
*
|
||||
* @param int $id Persistent ID of a link.
|
||||
*
|
||||
* @return int Real offset in local array, or null if doesn't exist.
|
||||
*/
|
||||
protected function getLinkOffset($id)
|
||||
{
|
||||
if (isset($this->ids[$id])) {
|
||||
return $this->ids[$id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,12 +33,12 @@ class LinkFilter
|
|||
public static $HASHTAG_CHARS = '\p{Pc}\p{N}\p{L}\p{Mn}';
|
||||
|
||||
/**
|
||||
* @var array all available links.
|
||||
* @var LinkDB all available links.
|
||||
*/
|
||||
private $links;
|
||||
|
||||
/**
|
||||
* @param array $links initialization.
|
||||
* @param LinkDB $links initialization.
|
||||
*/
|
||||
public function __construct($links)
|
||||
{
|
||||
|
@ -94,18 +94,16 @@ public function filter($type, $request, $casesensitive = false, $privateonly = f
|
|||
private function noFilter($privateonly = false)
|
||||
{
|
||||
if (! $privateonly) {
|
||||
krsort($this->links);
|
||||
return $this->links;
|
||||
}
|
||||
|
||||
$out = array();
|
||||
foreach ($this->links as $value) {
|
||||
foreach ($this->links as $key => $value) {
|
||||
if ($value['private']) {
|
||||
$out[$value['linkdate']] = $value;
|
||||
$out[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
krsort($out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
@ -121,10 +119,10 @@ private function noFilter($privateonly = false)
|
|||
private function filterSmallHash($smallHash)
|
||||
{
|
||||
$filtered = array();
|
||||
foreach ($this->links as $l) {
|
||||
if ($smallHash == smallHash($l['linkdate'])) {
|
||||
foreach ($this->links as $key => $l) {
|
||||
if ($smallHash == $l['shorturl']) {
|
||||
// Yes, this is ugly and slow
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
$filtered[$key] = $l;
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +186,7 @@ private function filterFulltext($searchterms, $privateonly = false)
|
|||
$keys = array('title', 'description', 'url', 'tags');
|
||||
|
||||
// Iterate over every stored link.
|
||||
foreach ($this->links as $link) {
|
||||
foreach ($this->links as $id => $link) {
|
||||
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if (! $link['private'] && $privateonly === true) {
|
||||
|
@ -222,11 +220,10 @@ private function filterFulltext($searchterms, $privateonly = false)
|
|||
}
|
||||
|
||||
if ($found) {
|
||||
$filtered[$link['linkdate']] = $link;
|
||||
$filtered[$id] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
krsort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
|
@ -256,7 +253,7 @@ public function filterTags($tags, $casesensitive = false, $privateonly = false)
|
|||
return $filtered;
|
||||
}
|
||||
|
||||
foreach ($this->links as $link) {
|
||||
foreach ($this->links as $key => $link) {
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if (! $link['private'] && $privateonly === true) {
|
||||
continue;
|
||||
|
@ -278,10 +275,9 @@ public function filterTags($tags, $casesensitive = false, $privateonly = false)
|
|||
}
|
||||
|
||||
if ($found) {
|
||||
$filtered[$link['linkdate']] = $link;
|
||||
$filtered[$key] = $link;
|
||||
}
|
||||
}
|
||||
krsort($filtered);
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
|
@ -304,13 +300,14 @@ public function filterDay($day)
|
|||
}
|
||||
|
||||
$filtered = array();
|
||||
foreach ($this->links as $l) {
|
||||
if (startsWith($l['linkdate'], $day)) {
|
||||
$filtered[$l['linkdate']] = $l;
|
||||
foreach ($this->links as $key => $l) {
|
||||
if ($l['created']->format('Ymd') == $day) {
|
||||
$filtered[$key] = $l;
|
||||
}
|
||||
}
|
||||
ksort($filtered);
|
||||
return $filtered;
|
||||
|
||||
// sort by date ASC
|
||||
return array_reverse($filtered, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -169,3 +169,16 @@ function space2nbsp($text)
|
|||
function format_description($description, $redirector = '', $indexUrl = '') {
|
||||
return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector), $indexUrl)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a small hash for a link.
|
||||
*
|
||||
* @param DateTime $date Link creation date.
|
||||
* @param int $id Link ID.
|
||||
*
|
||||
* @return string the small hash generated from link data.
|
||||
*/
|
||||
function link_small_hash($date, $id)
|
||||
{
|
||||
return smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $in
|
|||
if ($link['private'] == 0 && $selection == 'private') {
|
||||
continue;
|
||||
}
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$date = $link['created'];
|
||||
$link['timestamp'] = $date->getTimestamp();
|
||||
$link['taglist'] = str_replace(' ', ',', $link['tags']);
|
||||
|
||||
|
@ -147,7 +147,6 @@ public static function import($post, $files, $linkDb, $pagecache)
|
|||
'url' => $bkm['uri'],
|
||||
'description' => $bkm['note'],
|
||||
'private' => $private,
|
||||
'linkdate'=> '',
|
||||
'tags' => $bkm['tags']
|
||||
);
|
||||
|
||||
|
@ -161,25 +160,22 @@ public static function import($post, $files, $linkDb, $pagecache)
|
|||
}
|
||||
|
||||
// Overwrite an existing link, keep its date
|
||||
$newLink['linkdate'] = $existingLink['linkdate'];
|
||||
$linkDb[$existingLink['linkdate']] = $newLink;
|
||||
$newLink['id'] = $existingLink['id'];
|
||||
$newLink['created'] = $existingLink['created'];
|
||||
$newLink['updated'] = new DateTime();
|
||||
$linkDb[$existingLink['id']] = $newLink;
|
||||
$importCount++;
|
||||
$overwriteCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add a new link
|
||||
// Add a new link - @ used for UNIX timestamps
|
||||
$newLinkDate = new DateTime('@'.strval($bkm['time']));
|
||||
while (!empty($linkDb[$newLinkDate->format(LinkDB::LINK_DATE_FORMAT)])) {
|
||||
// Ensure the date/time is not already used
|
||||
// - this hack is necessary as the date/time acts as a primary key
|
||||
// - apply 1 second increments until an unused index is found
|
||||
// See https://github.com/shaarli/Shaarli/issues/351
|
||||
$newLinkDate->add(new DateInterval('PT1S'));
|
||||
}
|
||||
$linkDbDate = $newLinkDate->format(LinkDB::LINK_DATE_FORMAT);
|
||||
$newLink['linkdate'] = $linkDbDate;
|
||||
$linkDb[$linkDbDate] = $newLink;
|
||||
$newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
|
||||
$newLink['created'] = $newLinkDate;
|
||||
$newLink['id'] = $linkDb->getNextId();
|
||||
$newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']);
|
||||
$linkDb[$newLink['id']] = $newLink;
|
||||
$importCount++;
|
||||
}
|
||||
|
||||
|
|
|
@ -138,10 +138,10 @@ public function updateMethodMergeDeprecatedConfigFile()
|
|||
public function updateMethodRenameDashTags()
|
||||
{
|
||||
$linklist = $this->linkDB->filterSearch();
|
||||
foreach ($linklist as $link) {
|
||||
foreach ($linklist as $key => $link) {
|
||||
$link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']);
|
||||
$link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true)));
|
||||
$this->linkDB[$link['linkdate']] = $link;
|
||||
$this->linkDB[$key] = $link;
|
||||
}
|
||||
$this->linkDB->save($this->conf->get('resource.page_cache'));
|
||||
return true;
|
||||
|
@ -215,6 +215,47 @@ public function updateMethodEscapeUnescapedConfig()
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database to use the new ID system, which replaces linkdate primary keys.
|
||||
* Also, creation and update dates are now DateTime objects (done by LinkDB).
|
||||
*
|
||||
* Since this update is very sensitve (changing the whole database), the datastore will be
|
||||
* automatically backed up into the file datastore.<datetime>.php.
|
||||
*
|
||||
* LinkDB also adds the field 'shorturl' with the precedent format (linkdate smallhash),
|
||||
* which will be saved by this method.
|
||||
*
|
||||
* @return bool true if the update is successful, false otherwise.
|
||||
*/
|
||||
public function updateMethodDatastoreIds()
|
||||
{
|
||||
// up to date database
|
||||
if (isset($this->linkDB[0])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$save = $this->conf->get('resource.data_dir') .'/datastore.'. date('YmdHis') .'.php';
|
||||
copy($this->conf->get('resource.datastore'), $save);
|
||||
|
||||
$links = array();
|
||||
foreach ($this->linkDB as $offset => $value) {
|
||||
$links[] = $value;
|
||||
unset($this->linkDB[$offset]);
|
||||
}
|
||||
$links = array_reverse($links);
|
||||
$cpt = 0;
|
||||
foreach ($links as $l) {
|
||||
unset($l['linkdate']);
|
||||
$l['id'] = $cpt;
|
||||
$this->linkDB[$cpt++] = $l;
|
||||
}
|
||||
|
||||
$this->linkDB->save($this->conf->get('resource.page_cache'));
|
||||
$this->linkDB->reorder();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,11 @@ function logm($logFile, $clientIp, $message)
|
|||
* - are NOT cryptographically secure (they CAN be forged)
|
||||
*
|
||||
* In Shaarli, they are used as a tinyurl-like link to individual entries,
|
||||
* e.g. smallHash('20111006_131924') --> yZH23w
|
||||
* built once with the combination of the date and item ID.
|
||||
* e.g. smallHash('20111006_131924' . 142) --> eaWxtQ
|
||||
*
|
||||
* @warning before v0.8.1, smallhashes were built only with the date,
|
||||
* and their value has been preserved.
|
||||
*
|
||||
* @param string $text Create a hash from this text.
|
||||
*
|
||||
|
|
104
index.php
104
index.php
|
@ -564,24 +564,19 @@ function showDailyRSS($conf) {
|
|||
);
|
||||
|
||||
/* Some Shaarlies may have very few links, so we need to look
|
||||
back in time (rsort()) until we have enough days ($nb_of_days).
|
||||
back in time until we have enough days ($nb_of_days).
|
||||
*/
|
||||
$linkdates = array();
|
||||
foreach ($LINKSDB as $linkdate => $value) {
|
||||
$linkdates[] = $linkdate;
|
||||
}
|
||||
rsort($linkdates);
|
||||
$nb_of_days = 7; // We take 7 days.
|
||||
$today = date('Ymd');
|
||||
$days = array();
|
||||
|
||||
foreach ($linkdates as $linkdate) {
|
||||
$day = substr($linkdate, 0, 8); // Extract day (without time)
|
||||
if (strcmp($day,$today) < 0) {
|
||||
foreach ($LINKSDB as $link) {
|
||||
$day = $link['created']->format('Ymd'); // Extract day (without time)
|
||||
if (strcmp($day, $today) < 0) {
|
||||
if (empty($days[$day])) {
|
||||
$days[$day] = array();
|
||||
}
|
||||
$days[$day][] = $linkdate;
|
||||
$days[$day][] = $link;
|
||||
}
|
||||
|
||||
if (count($days) > $nb_of_days) {
|
||||
|
@ -601,24 +596,18 @@ function showDailyRSS($conf) {
|
|||
echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL;
|
||||
|
||||
// For each day.
|
||||
foreach ($days as $day => $linkdates) {
|
||||
foreach ($days as $day => $links) {
|
||||
$dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000');
|
||||
$absurl = escape(index_url($_SERVER).'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page.
|
||||
|
||||
// Build the HTML body of this RSS entry.
|
||||
$links = array();
|
||||
|
||||
// We pre-format some fields for proper output.
|
||||
foreach ($linkdates as $linkdate) {
|
||||
$l = $LINKSDB[$linkdate];
|
||||
$l['formatedDescription'] = format_description($l['description'], $conf->get('redirector.url'));
|
||||
$l['thumbnail'] = thumbnail($conf, $l['url']);
|
||||
$l_date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $l['linkdate']);
|
||||
$l['timestamp'] = $l_date->getTimestamp();
|
||||
if (startsWith($l['url'], '?')) {
|
||||
$l['url'] = index_url($_SERVER) . $l['url']; // make permalink URL absolute
|
||||
foreach ($links as &$link) {
|
||||
$link['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url'));
|
||||
$link['thumbnail'] = thumbnail($conf, $link['url']);
|
||||
$link['timestamp'] = $link['created']->getTimestamp();
|
||||
if (startsWith($link['url'], '?')) {
|
||||
$link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute
|
||||
}
|
||||
$links[$linkdate] = $l;
|
||||
}
|
||||
|
||||
// Then build the HTML for this day:
|
||||
|
@ -680,8 +669,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager)
|
|||
$linksToDisplay[$key]['taglist']=$taglist;
|
||||
$linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url'));
|
||||
$linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']);
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$linksToDisplay[$key]['timestamp'] = $date->getTimestamp();
|
||||
$linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp();
|
||||
}
|
||||
|
||||
/* We need to spread the articles on 3 columns.
|
||||
|
@ -831,7 +819,7 @@ function renderPage($conf, $pluginManager)
|
|||
// Get only links which have a thumbnail.
|
||||
foreach($links as $link)
|
||||
{
|
||||
$permalink='?'.escape(smallHash($link['linkdate']));
|
||||
$permalink='?'.$link['shorturl'];
|
||||
$thumb=lazyThumbnail($conf, $link['url'],$permalink);
|
||||
if ($thumb!='') // Only output links which have a thumbnail.
|
||||
{
|
||||
|
@ -1245,13 +1233,28 @@ function renderPage($conf, $pluginManager)
|
|||
// -------- User clicked the "Save" button when editing a link: Save link to database.
|
||||
if (isset($_POST['save_edit']))
|
||||
{
|
||||
$linkdate = $_POST['lf_linkdate'];
|
||||
$updated = isset($LINKSDB[$linkdate]) ? strval(date('Ymd_His')) : false;
|
||||
|
||||
// Go away!
|
||||
if (! tokenOk($_POST['token'])) {
|
||||
die('Wrong token.');
|
||||
}
|
||||
|
||||
// lf_id should only be present if the link exists.
|
||||
$id = !empty($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId();
|
||||
// Linkdate is kept here to:
|
||||
// - use the same permalink for notes as they're displayed when creating them
|
||||
// - let users hack creation date of their posts
|
||||
// See: https://github.com/shaarli/Shaarli/wiki/Datastore-hacks#changing-the-timestamp-for-a-link
|
||||
$linkdate = escape($_POST['lf_linkdate']);
|
||||
if (isset($LINKSDB[$id])) {
|
||||
// Edit
|
||||
$created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
|
||||
$updated = new DateTime();
|
||||
} else {
|
||||
// New link
|
||||
$created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
|
||||
$updated = null;
|
||||
}
|
||||
|
||||
// Remove multiple spaces.
|
||||
$tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags']));
|
||||
// Remove first '-' char in tags.
|
||||
|
@ -1268,14 +1271,17 @@ function renderPage($conf, $pluginManager)
|
|||
}
|
||||
|
||||
$link = array(
|
||||
'id' => $id,
|
||||
'title' => trim($_POST['lf_title']),
|
||||
'url' => $url,
|
||||
'description' => $_POST['lf_description'],
|
||||
'private' => (isset($_POST['lf_private']) ? 1 : 0),
|
||||
'linkdate' => $linkdate,
|
||||
'created' => $created,
|
||||
'updated' => $updated,
|
||||
'tags' => str_replace(',', ' ', $tags)
|
||||
'tags' => str_replace(',', ' ', $tags),
|
||||
'shorturl' => link_small_hash($created, $id),
|
||||
);
|
||||
|
||||
// If title is empty, use the URL as title.
|
||||
if ($link['title'] == '') {
|
||||
$link['title'] = $link['url'];
|
||||
|
@ -1283,7 +1289,7 @@ function renderPage($conf, $pluginManager)
|
|||
|
||||
$pluginManager->executeHooks('save_link', $link);
|
||||
|
||||
$LINKSDB[$linkdate] = $link;
|
||||
$LINKSDB[$id] = $link;
|
||||
$LINKSDB->save($conf->get('resource.page_cache'));
|
||||
pubsubhub($conf);
|
||||
|
||||
|
@ -1296,7 +1302,7 @@ function renderPage($conf, $pluginManager)
|
|||
$returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?';
|
||||
$location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
|
||||
// Scroll to the link which has been edited.
|
||||
$location .= '#' . smallHash($_POST['lf_linkdate']);
|
||||
$location .= '#' . $link['shorturl'];
|
||||
// After saving the link, redirect to the page the user was on.
|
||||
header('Location: '. $location);
|
||||
exit;
|
||||
|
@ -1307,8 +1313,10 @@ function renderPage($conf, $pluginManager)
|
|||
{
|
||||
// If we are called from the bookmarklet, we must close the popup:
|
||||
if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
|
||||
$link = $LINKSDB[(int) escape($_POST['lf_id'])];
|
||||
$returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
|
||||
$returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited.
|
||||
// Scroll to the link which has been edited.
|
||||
$returnurl .= '#'. $link['shorturl'];
|
||||
$returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
|
||||
header('Location: '.$returnurl); // After canceling, redirect to the page the user was on.
|
||||
exit;
|
||||
|
@ -1318,14 +1326,17 @@ function renderPage($conf, $pluginManager)
|
|||
if (isset($_POST['delete_link']))
|
||||
{
|
||||
if (!tokenOk($_POST['token'])) die('Wrong token.');
|
||||
|
||||
// We do not need to ask for confirmation:
|
||||
// - confirmation is handled by JavaScript
|
||||
// - we are protected from XSRF by the token.
|
||||
$linkdate=$_POST['lf_linkdate'];
|
||||
|
||||
$pluginManager->executeHooks('delete_link', $LINKSDB[$linkdate]);
|
||||
// FIXME! We keep `lf_linkdate` for consistency before a proper API. To be removed.
|
||||
$id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : intval(escape($_POST['lf_linkdate']));
|
||||
|
||||
unset($LINKSDB[$linkdate]);
|
||||
$pluginManager->executeHooks('delete_link', $LINKSDB[$id]);
|
||||
|
||||
unset($LINKSDB[$id]);
|
||||
$LINKSDB->save('resource.page_cache'); // save to disk
|
||||
|
||||
// If we are called from the bookmarklet, we must close the popup:
|
||||
|
@ -1364,8 +1375,10 @@ function renderPage($conf, $pluginManager)
|
|||
// -------- User clicked the "EDIT" button on a link: Display link edit form.
|
||||
if (isset($_GET['edit_link']))
|
||||
{
|
||||
$link = $LINKSDB[$_GET['edit_link']]; // Read database
|
||||
$id = (int) escape($_GET['edit_link']);
|
||||
$link = $LINKSDB[$id]; // Read database
|
||||
if (!$link) { header('Location: ?'); exit; } // Link not found in database.
|
||||
$link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT);
|
||||
$data = array(
|
||||
'link' => $link,
|
||||
'link_is_new' => false,
|
||||
|
@ -1389,10 +1402,10 @@ function renderPage($conf, $pluginManager)
|
|||
$link_is_new = false;
|
||||
// Check if URL is not already in database (in this case, we will edit the existing link)
|
||||
$link = $LINKSDB->getLinkFromUrl($url);
|
||||
if (!$link)
|
||||
if (! $link)
|
||||
{
|
||||
$link_is_new = true;
|
||||
$linkdate = strval(date('Ymd_His'));
|
||||
$linkdate = strval(date(LinkDB::LINK_DATE_FORMAT));
|
||||
// Get title if it was provided in URL (by the bookmarklet).
|
||||
$title = empty($_GET['title']) ? '' : escape($_GET['title']);
|
||||
// Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
|
||||
|
@ -1416,7 +1429,7 @@ function renderPage($conf, $pluginManager)
|
|||
}
|
||||
|
||||
if ($url == '') {
|
||||
$url = '?' . smallHash($linkdate);
|
||||
$url = '?' . smallHash($linkdate . $LINKSDB->getNextId());
|
||||
$title = 'Note: ';
|
||||
}
|
||||
$url = escape($url);
|
||||
|
@ -1430,6 +1443,8 @@ function renderPage($conf, $pluginManager)
|
|||
'tags' => $tags,
|
||||
'private' => $private
|
||||
);
|
||||
} else {
|
||||
$link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT);
|
||||
}
|
||||
|
||||
$data = array(
|
||||
|
@ -1635,18 +1650,15 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
|
|||
$link['description'] = format_description($link['description'], $conf->get('redirector.url'));
|
||||
$classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight';
|
||||
$link['class'] = $link['private'] == 0 ? $classLi : 'private';
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$link['timestamp'] = $date->getTimestamp();
|
||||
$link['timestamp'] = $link['created']->getTimestamp();
|
||||
if (! empty($link['updated'])) {
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['updated']);
|
||||
$link['updated_timestamp'] = $date->getTimestamp();
|
||||
$link['updated_timestamp'] = $link['updated']->getTimestamp();
|
||||
} else {
|
||||
$link['updated_timestamp'] = '';
|
||||
}
|
||||
$taglist = explode(' ', $link['tags']);
|
||||
uasort($taglist, 'strcasecmp');
|
||||
$link['taglist'] = $taglist;
|
||||
$link['shorturl'] = smallHash($link['linkdate']);
|
||||
// Check for both signs of a note: starting with ? and 7 chars long.
|
||||
if ($link['url'][0] === '?' &&
|
||||
strlen($link['url']) === 7) {
|
||||
|
|
|
@ -41,9 +41,9 @@ function hook_isso_render_linklist($data, $conf)
|
|||
// Only display comments for permalinks.
|
||||
if (count($data['links']) == 1 && empty($data['search_tags']) && empty($data['search_term'])) {
|
||||
$link = reset($data['links']);
|
||||
$isso_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/isso/isso.html');
|
||||
$issoHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/isso/isso.html');
|
||||
|
||||
$isso = sprintf($isso_html, $issoUrl, $issoUrl, $link['linkdate'], $link['linkdate']);
|
||||
$isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']);
|
||||
$data['plugin_end_zone'][] = $isso;
|
||||
|
||||
// Hackish way to include this CSS file only when necessary.
|
||||
|
|
|
@ -84,8 +84,9 @@ public function testRSSBuildData()
|
|||
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
|
||||
|
||||
// Test first link (note link)
|
||||
$link = array_shift($data['links']);
|
||||
$this->assertEquals('20150310_114651', $link['linkdate']);
|
||||
$link = reset($data['links']);
|
||||
$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']);
|
||||
$this->assertEquals('http://host.tld/?WDWyig', $link['url']);
|
||||
$this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']);
|
||||
|
@ -99,14 +100,14 @@ public function testRSSBuildData()
|
|||
$this->assertEquals('sTuff', $link['taglist'][0]);
|
||||
|
||||
// Test URL with external link.
|
||||
$this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $data['links']['20150310_114633']['url']);
|
||||
$this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $data['links'][8]['url']);
|
||||
|
||||
// Test multitags.
|
||||
$this->assertEquals(5, count($data['links']['20141125_084734']['taglist']));
|
||||
$this->assertEquals('css', $data['links']['20141125_084734']['taglist'][0]);
|
||||
$this->assertEquals(5, count($data['links'][6]['taglist']));
|
||||
$this->assertEquals('css', $data['links'][6]['taglist'][0]);
|
||||
|
||||
// Test update date
|
||||
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links']['20150310_114633']['up_iso_date']);
|
||||
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,9 +120,9 @@ public function testAtomBuildData()
|
|||
$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 = array_shift($data['links']);
|
||||
$link = reset($data['links']);
|
||||
$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']['20150310_114633']['up_iso_date']);
|
||||
$this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,7 +139,8 @@ public function testBuildDataFiltered()
|
|||
$data = $feedBuilder->buildData();
|
||||
$this->assertEquals(1, count($data['links']));
|
||||
$link = array_shift($data['links']);
|
||||
$this->assertEquals('20150310_114651', $link['linkdate']);
|
||||
$this->assertEquals(41, $link['id']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +156,8 @@ public function testBuildDataCount()
|
|||
$data = $feedBuilder->buildData();
|
||||
$this->assertEquals(1, count($data['links']));
|
||||
$link = array_shift($data['links']);
|
||||
$this->assertEquals('20150310_114651', $link['linkdate']);
|
||||
$this->assertEquals(41, $link['id']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,15 +173,17 @@ public function testBuildDataPermalinks()
|
|||
$this->assertTrue($data['usepermalinks']);
|
||||
// First link is a permalink
|
||||
$link = array_shift($data['links']);
|
||||
$this->assertEquals('20150310_114651', $link['linkdate']);
|
||||
$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']);
|
||||
$this->assertEquals('http://host.tld/?WDWyig', $link['url']);
|
||||
$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']);
|
||||
$this->assertEquals('20150310_114633', $link['linkdate']);
|
||||
$this->assertEquals('http://host.tld/?kLHmZg', $link['guid']);
|
||||
$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']);
|
||||
$this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']);
|
||||
$this->assertContains('Direct link', $link['description']);
|
||||
$this->assertContains('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']);
|
||||
|
|
|
@ -186,14 +186,15 @@ public function testSave()
|
|||
$dbSize = sizeof($testDB);
|
||||
|
||||
$link = array(
|
||||
'id' => 42,
|
||||
'title'=>'an additional link',
|
||||
'url'=>'http://dum.my',
|
||||
'description'=>'One more',
|
||||
'private'=>0,
|
||||
'linkdate'=>'20150518_190000',
|
||||
'created'=> DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'),
|
||||
'tags'=>'unit test'
|
||||
);
|
||||
$testDB[$link['linkdate']] = $link;
|
||||
$testDB[$link['id']] = $link;
|
||||
$testDB->save('tests');
|
||||
|
||||
$testDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
@ -238,12 +239,12 @@ public function testCountHiddenPublic()
|
|||
public function testDays()
|
||||
{
|
||||
$this->assertEquals(
|
||||
array('20121206', '20130614', '20150310'),
|
||||
array('20100310', '20121206', '20130614', '20150310'),
|
||||
self::$publicLinkDB->days()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array('20121206', '20130614', '20141125', '20150310'),
|
||||
array('20100310', '20121206', '20130614', '20141125', '20150310'),
|
||||
self::$privateLinkDB->days()
|
||||
);
|
||||
}
|
||||
|
@ -290,10 +291,11 @@ public function testAllTags()
|
|||
'stallman' => 1,
|
||||
'free' => 1,
|
||||
'-exclude' => 1,
|
||||
// The DB contains a link with `sTuff` and another one with `stuff` tag.
|
||||
// They need to be grouped with the first case found (`sTuff`).
|
||||
'sTuff' => 2,
|
||||
'hashtag' => 2,
|
||||
// The DB contains a link with `sTuff` and another one with `stuff` tag.
|
||||
// They need to be grouped with the first case found - order by date DESC: `sTuff`.
|
||||
'sTuff' => 2,
|
||||
'ut' => 1,
|
||||
),
|
||||
self::$publicLinkDB->allTags()
|
||||
);
|
||||
|
@ -321,6 +323,7 @@ public function testAllTags()
|
|||
'tag2' => 1,
|
||||
'tag3' => 1,
|
||||
'tag4' => 1,
|
||||
'ut' => 1,
|
||||
),
|
||||
self::$privateLinkDB->allTags()
|
||||
);
|
||||
|
@ -411,6 +414,11 @@ public function testFilterHashValid()
|
|||
1,
|
||||
count(self::$publicLinkDB->filterHash($request))
|
||||
);
|
||||
$request = smallHash('20150310_114633' . 8);
|
||||
$this->assertEquals(
|
||||
1,
|
||||
count(self::$publicLinkDB->filterHash($request))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -433,4 +441,23 @@ public function testFilterHashInValid()
|
|||
{
|
||||
self::$publicLinkDB->filterHash('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reorder with asc/desc parameter.
|
||||
*/
|
||||
public function testReorderLinksDesc()
|
||||
{
|
||||
self::$privateLinkDB->reorder('ASC');
|
||||
$linkIds = array(42, 4, 1, 0, 7, 6, 8, 41);
|
||||
$cpt = 0;
|
||||
foreach (self::$privateLinkDB as $key => $value) {
|
||||
$this->assertEquals($linkIds[$cpt++], $key);
|
||||
}
|
||||
self::$privateLinkDB->reorder('DESC');
|
||||
$linkIds = array_reverse($linkIds);
|
||||
$cpt = 0;
|
||||
foreach (self::$privateLinkDB as $key => $value) {
|
||||
$this->assertEquals($linkIds[$cpt++], $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ public function testFilterSmallHash()
|
|||
|
||||
$this->assertEquals(
|
||||
'MediaGoblin',
|
||||
$links['20130614_184135']['title']
|
||||
$links[7]['title']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ public function testExcludeSearch()
|
|||
);
|
||||
|
||||
$this->assertEquals(
|
||||
6,
|
||||
7,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '-revolution'))
|
||||
);
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ public function testTagFilterWithExclusion()
|
|||
);
|
||||
|
||||
$this->assertEquals(
|
||||
6,
|
||||
7,
|
||||
count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free'))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public function testFilterAndFormatAll()
|
|||
$links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all', false, '');
|
||||
$this->assertEquals(self::$refDb->countLinks(), sizeof($links));
|
||||
foreach ($links as $link) {
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$date = $link['created'];
|
||||
$this->assertEquals(
|
||||
$date->getTimestamp(),
|
||||
$link['timestamp']
|
||||
|
@ -70,7 +70,7 @@ public function testFilterAndFormatPrivate()
|
|||
$links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private', false, '');
|
||||
$this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links));
|
||||
foreach ($links as $link) {
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$date = $link['created'];
|
||||
$this->assertEquals(
|
||||
$date->getTimestamp(),
|
||||
$link['timestamp']
|
||||
|
@ -90,7 +90,7 @@ public function testFilterAndFormatPublic()
|
|||
$links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, '');
|
||||
$this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links));
|
||||
foreach ($links as $link) {
|
||||
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
|
||||
$date = $link['created'];
|
||||
$this->assertEquals(
|
||||
$date->getTimestamp(),
|
||||
$link['timestamp']
|
||||
|
|
|
@ -42,6 +42,18 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
|
|||
*/
|
||||
protected $pagecache = 'tests';
|
||||
|
||||
/**
|
||||
* @var string Save the current timezone.
|
||||
*/
|
||||
protected static $defaultTimeZone;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::$defaultTimeZone = date_default_timezone_get();
|
||||
// Timezone without DST for test consistency
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets test data before each test
|
||||
*/
|
||||
|
@ -55,6 +67,11 @@ protected function setUp()
|
|||
$this->linkDb = new LinkDB(self::$testDatastore, true, false);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
date_default_timezone_set(self::$defaultTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to import bookmarks from an empty file
|
||||
*/
|
||||
|
@ -98,18 +115,19 @@ public function testImportInternetExplorerEncoding()
|
|||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160618_173944',
|
||||
'id' => 0,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160618_203944'),
|
||||
'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky',
|
||||
'url' => 'http://hginit.com/',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => ''
|
||||
'tags' => '',
|
||||
'shorturl' => 'La37cg',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://hginit.com/')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import bookmarks nested in a folder hierarchy
|
||||
*/
|
||||
|
@ -126,89 +144,105 @@ public function testImportNested()
|
|||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160225_205541',
|
||||
'id' => 0,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235541'),
|
||||
'title' => 'Nested 1',
|
||||
'url' => 'http://nest.ed/1',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'tag1 tag2'
|
||||
'tags' => 'tag1 tag2',
|
||||
'shorturl' => 'KyDNKA',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/1')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160225_205542',
|
||||
'id' => 1,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235542'),
|
||||
'title' => 'Nested 1-1',
|
||||
'url' => 'http://nest.ed/1-1',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'folder1 tag1 tag2'
|
||||
'tags' => 'folder1 tag1 tag2',
|
||||
'shorturl' => 'T2LnXg',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/1-1')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160225_205547',
|
||||
'id' => 2,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235547'),
|
||||
'title' => 'Nested 1-2',
|
||||
'url' => 'http://nest.ed/1-2',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'folder1 tag3 tag4'
|
||||
'tags' => 'folder1 tag3 tag4',
|
||||
'shorturl' => '46SZxA',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/1-2')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160202_172222',
|
||||
'id' => 3,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
|
||||
'title' => 'Nested 2-1',
|
||||
'url' => 'http://nest.ed/2-1',
|
||||
'description' => 'First link of the second section',
|
||||
'private' => 1,
|
||||
'tags' => 'folder2'
|
||||
'tags' => 'folder2',
|
||||
'shorturl' => '4UHOSw',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/2-1')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160119_200227',
|
||||
'id' => 4,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
|
||||
'title' => 'Nested 2-2',
|
||||
'url' => 'http://nest.ed/2-2',
|
||||
'description' => 'Second link of the second section',
|
||||
'private' => 1,
|
||||
'tags' => 'folder2'
|
||||
'tags' => 'folder2',
|
||||
'shorturl' => 'yfzwbw',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/2-2')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160202_172223',
|
||||
'id' => 5,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
|
||||
'title' => 'Nested 3-1',
|
||||
'url' => 'http://nest.ed/3-1',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'folder3 folder3-1 tag3'
|
||||
'tags' => 'folder3 folder3-1 tag3',
|
||||
'shorturl' => 'UwxIUQ',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/3-1')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160119_200228',
|
||||
'id' => 6,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
|
||||
'title' => 'Nested 3-2',
|
||||
'url' => 'http://nest.ed/3-2',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'folder3 folder3-1'
|
||||
'tags' => 'folder3 folder3-1',
|
||||
'shorturl' => 'p8dyZg',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/3-2')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160229_081541',
|
||||
'id' => 7,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160229_111541'),
|
||||
'title' => 'Nested 2',
|
||||
'url' => 'http://nest.ed/2',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'tag4'
|
||||
'tags' => 'tag4',
|
||||
'shorturl' => 'Gt3Uug',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://nest.ed/2')
|
||||
);
|
||||
|
@ -227,28 +261,34 @@ public function testImportDefaultPrivacyNoPost()
|
|||
.' 2 links imported, 0 links overwritten, 0 links skipped.',
|
||||
NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache)
|
||||
);
|
||||
|
||||
$this->assertEquals(2, count($this->linkDb));
|
||||
$this->assertEquals(1, count_private($this->linkDb));
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20001010_105536',
|
||||
'id' => 0,
|
||||
// Old link - UTC+4 (note that TZ in the import file is ignored).
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
|
||||
'title' => 'Secret stuff',
|
||||
'url' => 'https://private.tld',
|
||||
'description' => "Super-secret stuff you're not supposed to know about",
|
||||
'private' => 1,
|
||||
'tags' => 'private secret'
|
||||
'tags' => 'private secret',
|
||||
'shorturl' => 'EokDtA',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('https://private.tld')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160225_205548',
|
||||
'id' => 1,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
|
||||
'title' => 'Public stuff',
|
||||
'url' => 'http://public.tld',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'public hello world'
|
||||
'tags' => 'public hello world',
|
||||
'shorturl' => 'Er9ddA',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://public.tld')
|
||||
);
|
||||
|
@ -271,23 +311,28 @@ public function testImportKeepPrivacy()
|
|||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20001010_105536',
|
||||
'id' => 0,
|
||||
// Note that TZ in the import file is ignored.
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
|
||||
'title' => 'Secret stuff',
|
||||
'url' => 'https://private.tld',
|
||||
'description' => "Super-secret stuff you're not supposed to know about",
|
||||
'private' => 1,
|
||||
'tags' => 'private secret'
|
||||
'tags' => 'private secret',
|
||||
'shorturl' => 'EokDtA',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('https://private.tld')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'linkdate' => '20160225_205548',
|
||||
'id' => 1,
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
|
||||
'title' => 'Public stuff',
|
||||
'url' => 'http://public.tld',
|
||||
'description' => '',
|
||||
'private' => 0,
|
||||
'tags' => 'public hello world'
|
||||
'tags' => 'public hello world',
|
||||
'shorturl' => 'Er9ddA',
|
||||
),
|
||||
$this->linkDb->getLinkFromUrl('http://public.tld')
|
||||
);
|
||||
|
@ -309,11 +354,11 @@ public function testImportAsPublic()
|
|||
$this->assertEquals(0, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->linkDb['20001010_105536']['private']
|
||||
$this->linkDb[0]['private']
|
||||
);
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->linkDb['20160225_205548']['private']
|
||||
$this->linkDb[1]['private']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -333,11 +378,11 @@ public function testImportAsPrivate()
|
|||
$this->assertEquals(2, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->linkDb['20001010_105536']['private']
|
||||
$this->linkDb['0']['private']
|
||||
);
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->linkDb['20160225_205548']['private']
|
||||
$this->linkDb['1']['private']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -359,13 +404,12 @@ public function testOverwriteAsPublic()
|
|||
$this->assertEquals(2, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->linkDb['20001010_105536']['private']
|
||||
$this->linkDb[0]['private']
|
||||
);
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->linkDb['20160225_205548']['private']
|
||||
$this->linkDb[1]['private']
|
||||
);
|
||||
|
||||
// re-import as public, enable overwriting
|
||||
$post = array(
|
||||
'privacy' => 'public',
|
||||
|
@ -380,11 +424,11 @@ public function testOverwriteAsPublic()
|
|||
$this->assertEquals(0, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->linkDb['20001010_105536']['private']
|
||||
$this->linkDb[0]['private']
|
||||
);
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->linkDb['20160225_205548']['private']
|
||||
$this->linkDb[1]['private']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -406,11 +450,11 @@ public function testOverwriteAsPrivate()
|
|||
$this->assertEquals(0, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->linkDb['20001010_105536']['private']
|
||||
$this->linkDb['0']['private']
|
||||
);
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->linkDb['20160225_205548']['private']
|
||||
$this->linkDb['1']['private']
|
||||
);
|
||||
|
||||
// re-import as private, enable overwriting
|
||||
|
@ -427,11 +471,11 @@ public function testOverwriteAsPrivate()
|
|||
$this->assertEquals(2, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->linkDb['20001010_105536']['private']
|
||||
$this->linkDb['0']['private']
|
||||
);
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->linkDb['20160225_205548']['private']
|
||||
$this->linkDb['1']['private']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -480,11 +524,11 @@ public function testSetDefaultTags()
|
|||
$this->assertEquals(0, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
'tag1 tag2 tag3 private secret',
|
||||
$this->linkDb['20001010_105536']['tags']
|
||||
$this->linkDb['0']['tags']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'tag1 tag2 tag3 public hello world',
|
||||
$this->linkDb['20160225_205548']['tags']
|
||||
$this->linkDb['1']['tags']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -507,16 +551,16 @@ public function testSanitizeDefaultTags()
|
|||
$this->assertEquals(0, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
'tag1& tag2 "tag3" private secret',
|
||||
$this->linkDb['20001010_105536']['tags']
|
||||
$this->linkDb['0']['tags']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'tag1& tag2 "tag3" public hello world',
|
||||
$this->linkDb['20160225_205548']['tags']
|
||||
$this->linkDb['1']['tags']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure each imported bookmark has a unique linkdate
|
||||
* Ensure each imported bookmark has a unique id
|
||||
*
|
||||
* See https://github.com/shaarli/Shaarli/issues/351
|
||||
*/
|
||||
|
@ -531,16 +575,16 @@ public function testImportSameDate()
|
|||
$this->assertEquals(3, count($this->linkDb));
|
||||
$this->assertEquals(0, count_private($this->linkDb));
|
||||
$this->assertEquals(
|
||||
'20160225_205548',
|
||||
$this->linkDb['20160225_205548']['linkdate']
|
||||
0,
|
||||
$this->linkDb[0]['id']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'20160225_205549',
|
||||
$this->linkDb['20160225_205549']['linkdate']
|
||||
1,
|
||||
$this->linkDb[1]['id']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'20160225_205550',
|
||||
$this->linkDb['20160225_205550']['linkdate']
|
||||
2,
|
||||
$this->linkDb[2]['id']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,6 +214,7 @@ public function testRenameDashTags()
|
|||
$refDB = new ReferenceLinkDB();
|
||||
$refDB->write(self::$testDatastore);
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
||||
$this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
|
||||
$updater = new Updater(array(), $linkDB, $this->conf, true);
|
||||
$updater->updateMethodRenameDashTags();
|
||||
|
@ -287,4 +288,101 @@ public function testEscapeConfig()
|
|||
$this->assertEquals(escape($redirectorUrl), $this->conf->get('redirector.url'));
|
||||
unlink($sandbox .'.json.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updateMethodDatastoreIds().
|
||||
*/
|
||||
public function testDatastoreIds()
|
||||
{
|
||||
$links = array(
|
||||
'20121206_182539' => array(
|
||||
'linkdate' => '20121206_182539',
|
||||
'title' => 'Geek and Poke',
|
||||
'url' => 'http://geek-and-poke.com/',
|
||||
'description' => 'desc',
|
||||
'tags' => 'dev cartoon tag1 tag2 tag3 tag4 ',
|
||||
'updated' => '20121206_190301',
|
||||
'private' => false,
|
||||
),
|
||||
'20121206_172539' => array(
|
||||
'linkdate' => '20121206_172539',
|
||||
'title' => 'UserFriendly - Samba',
|
||||
'url' => 'http://ars.userfriendly.org/cartoons/?id=20010306',
|
||||
'description' => '',
|
||||
'tags' => 'samba cartoon web',
|
||||
'private' => false,
|
||||
),
|
||||
'20121206_142300' => array(
|
||||
'linkdate' => '20121206_142300',
|
||||
'title' => 'UserFriendly - Web Designer',
|
||||
'url' => 'http://ars.userfriendly.org/cartoons/?id=20121206',
|
||||
'description' => 'Naming conventions... #private',
|
||||
'tags' => 'samba cartoon web',
|
||||
'private' => true,
|
||||
),
|
||||
);
|
||||
$refDB = new ReferenceLinkDB();
|
||||
$refDB->setLinks($links);
|
||||
$refDB->write(self::$testDatastore);
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
||||
$checksum = hash_file('sha1', self::$testDatastore);
|
||||
|
||||
$this->conf->set('resource.data_dir', 'sandbox');
|
||||
$this->conf->set('resource.datastore', self::$testDatastore);
|
||||
|
||||
$updater = new Updater(array(), $linkDB, $this->conf, true);
|
||||
$this->assertTrue($updater->updateMethodDatastoreIds());
|
||||
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
||||
$backup = glob($this->conf->get('resource.data_dir') . '/datastore.'. date('YmdH') .'*.php');
|
||||
$backup = $backup[0];
|
||||
|
||||
$this->assertFileExists($backup);
|
||||
$this->assertEquals($checksum, hash_file('sha1', $backup));
|
||||
unlink($backup);
|
||||
|
||||
$this->assertEquals(3, count($linkDB));
|
||||
$this->assertTrue(isset($linkDB[0]));
|
||||
$this->assertFalse(isset($linkDB[0]['linkdate']));
|
||||
$this->assertEquals(0, $linkDB[0]['id']);
|
||||
$this->assertEquals('UserFriendly - Web Designer', $linkDB[0]['title']);
|
||||
$this->assertEquals('http://ars.userfriendly.org/cartoons/?id=20121206', $linkDB[0]['url']);
|
||||
$this->assertEquals('Naming conventions... #private', $linkDB[0]['description']);
|
||||
$this->assertEquals('samba cartoon web', $linkDB[0]['tags']);
|
||||
$this->assertTrue($linkDB[0]['private']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), $linkDB[0]['created']);
|
||||
|
||||
$this->assertTrue(isset($linkDB[1]));
|
||||
$this->assertFalse(isset($linkDB[1]['linkdate']));
|
||||
$this->assertEquals(1, $linkDB[1]['id']);
|
||||
$this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), $linkDB[1]['created']);
|
||||
|
||||
$this->assertTrue(isset($linkDB[2]));
|
||||
$this->assertFalse(isset($linkDB[2]['linkdate']));
|
||||
$this->assertEquals(2, $linkDB[2]['id']);
|
||||
$this->assertEquals('Geek and Poke', $linkDB[2]['title']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), $linkDB[2]['created']);
|
||||
$this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'), $linkDB[2]['updated']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updateMethodDatastoreIds() with the update already applied: nothing to do.
|
||||
*/
|
||||
public function testDatastoreIdsNothingToDo()
|
||||
{
|
||||
$refDB = new ReferenceLinkDB();
|
||||
$refDB->write(self::$testDatastore);
|
||||
$linkDB = new LinkDB(self::$testDatastore, true, false);
|
||||
|
||||
$this->conf->set('resource.data_dir', 'sandbox');
|
||||
$this->conf->set('resource.datastore', self::$testDatastore);
|
||||
|
||||
$checksum = hash_file('sha1', self::$testDatastore);
|
||||
$updater = new Updater(array(), $linkDB, $this->conf, true);
|
||||
$this->assertTrue($updater->updateMethodDatastoreIds());
|
||||
$this->assertEquals($checksum, hash_file('sha1', self::$testDatastore));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,12 +47,14 @@ function testIssoDisplayed()
|
|||
$conf->set('plugins.ISSO_SERVER', 'value');
|
||||
|
||||
$str = 'http://randomstr.com/test';
|
||||
$date = '20161118_100001';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'id' => 12,
|
||||
'url' => $str,
|
||||
'linkdate' => 'abc',
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -65,7 +67,14 @@ function testIssoDisplayed()
|
|||
|
||||
// plugin data
|
||||
$this->assertEquals(1, count($data['plugin_end_zone']));
|
||||
$this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'abc'));
|
||||
$this->assertNotFalse(strpos(
|
||||
$data['plugin_end_zone'][0],
|
||||
'data-isso-id="'. $data['links'][0]['id'] .'"'
|
||||
));
|
||||
$this->assertNotFalse(strpos(
|
||||
$data['plugin_end_zone'][0],
|
||||
'data-title="'. $data['links'][0]['id'] .'"'
|
||||
));
|
||||
$this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'embed.min.js'));
|
||||
}
|
||||
|
||||
|
@ -78,16 +87,20 @@ function testIssoMultipleLinks()
|
|||
$conf->set('plugins.ISSO_SERVER', 'value');
|
||||
|
||||
$str = 'http://randomstr.com/test';
|
||||
$date1 = '20161118_100001';
|
||||
$date2 = '20161118_100002';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'id' => 12,
|
||||
'url' => $str,
|
||||
'linkdate' => 'abc',
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1),
|
||||
),
|
||||
array(
|
||||
'id' => 13,
|
||||
'url' => $str . '2',
|
||||
'linkdate' => 'abc2',
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
@ -106,12 +119,14 @@ function testIssoNotDisplayedWhenSearch()
|
|||
$conf->set('plugins.ISSO_SERVER', 'value');
|
||||
|
||||
$str = 'http://randomstr.com/test';
|
||||
$date = '20161118_100001';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'id' => 12,
|
||||
'url' => $str,
|
||||
'linkdate' => 'abc',
|
||||
'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date),
|
||||
)
|
||||
),
|
||||
'search_term' => $str
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
class ReferenceLinkDB
|
||||
{
|
||||
public static $NB_LINKS_TOTAL = 7;
|
||||
public static $NB_LINKS_TOTAL = 8;
|
||||
|
||||
private $_links = array();
|
||||
private $_publicCount = 0;
|
||||
|
@ -16,66 +16,87 @@ class ReferenceLinkDB
|
|||
public function __construct()
|
||||
{
|
||||
$this->addLink(
|
||||
41,
|
||||
'Link title: @website',
|
||||
'?WDWyig',
|
||||
'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
|
||||
0,
|
||||
'20150310_114651',
|
||||
'sTuff'
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'),
|
||||
'sTuff',
|
||||
null,
|
||||
'WDWyig'
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
42,
|
||||
'Note: I have a big ID but an old date',
|
||||
'?WDWyig',
|
||||
'Used to test links reordering.',
|
||||
0,
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100310_101010'),
|
||||
'ut'
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
8,
|
||||
'Free as in Freedom 2.0 @website',
|
||||
'https://static.fsf.org/nosvn/faif-2.0.pdf',
|
||||
'Richard Stallman and the Free Software Revolution. Read this. #hashtag',
|
||||
0,
|
||||
'20150310_114633',
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'),
|
||||
'free gnu software stallman -exclude stuff hashtag',
|
||||
'20160803_093033'
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033')
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
7,
|
||||
'MediaGoblin',
|
||||
'http://mediagoblin.org/',
|
||||
'A free software media publishing platform #hashtagOther',
|
||||
0,
|
||||
'20130614_184135',
|
||||
'gnu media web .hidden hashtag'
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
|
||||
'gnu media web .hidden hashtag',
|
||||
null,
|
||||
'IuWvgA'
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
6,
|
||||
'w3c-markup-validator',
|
||||
'https://dvcs.w3.org/hg/markup-validator/summary',
|
||||
'Mercurial repository for the W3C Validator #private',
|
||||
1,
|
||||
'20141125_084734',
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20141125_084734'),
|
||||
'css html w3c web Mercurial'
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
4,
|
||||
'UserFriendly - Web Designer',
|
||||
'http://ars.userfriendly.org/cartoons/?id=20121206',
|
||||
'Naming conventions... #private',
|
||||
0,
|
||||
'20121206_142300',
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'),
|
||||
'dev cartoon web'
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
1,
|
||||
'UserFriendly - Samba',
|
||||
'http://ars.userfriendly.org/cartoons/?id=20010306',
|
||||
'Tropical printing',
|
||||
0,
|
||||
'20121206_172539',
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'),
|
||||
'samba cartoon web'
|
||||
);
|
||||
|
||||
$this->addLink(
|
||||
0,
|
||||
'Geek and Poke',
|
||||
'http://geek-and-poke.com/',
|
||||
'',
|
||||
1,
|
||||
'20121206_182539',
|
||||
DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'),
|
||||
'dev cartoon tag1 tag2 tag3 tag4 '
|
||||
);
|
||||
}
|
||||
|
@ -83,18 +104,20 @@ public function __construct()
|
|||
/**
|
||||
* Adds a new link
|
||||
*/
|
||||
protected function addLink($title, $url, $description, $private, $date, $tags, $updated = '')
|
||||
protected function addLink($id, $title, $url, $description, $private, $date, $tags, $updated = '', $shorturl = '')
|
||||
{
|
||||
$link = array(
|
||||
'id' => $id,
|
||||
'title' => $title,
|
||||
'url' => $url,
|
||||
'description' => $description,
|
||||
'private' => $private,
|
||||
'linkdate' => $date,
|
||||
'tags' => $tags,
|
||||
'created' => $date,
|
||||
'updated' => $updated,
|
||||
'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id),
|
||||
);
|
||||
$this->_links[$date] = $link;
|
||||
$this->_links[$id] = $link;
|
||||
|
||||
if ($private) {
|
||||
$this->_privateCount++;
|
||||
|
@ -142,4 +165,14 @@ public function getLinks()
|
|||
{
|
||||
return $this->_links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter to override link creation.
|
||||
*
|
||||
* @param array $links List of links.
|
||||
*/
|
||||
public function setLinks($links)
|
||||
{
|
||||
$this->_links = $links;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,13 +49,13 @@
|
|||
{$link=$value}
|
||||
<div class="dailyEntry">
|
||||
<div class="dailyEntryPermalink">
|
||||
<a href="?{$link.linkdate|smallHash}">
|
||||
<a href="?{$value.shorturl}">
|
||||
<img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink">
|
||||
</a>
|
||||
</div>
|
||||
{if="!$hide_timestamps || isLoggedIn()"}
|
||||
<div class="dailyEntryLinkdate">
|
||||
<a href="?{$link.linkdate|smallHash}">{function="strftime('%c', $link.timestamp)"}</a>
|
||||
<a href="?{$value.shorturl}">{function="strftime('%c', $link.timestamp)"}</a>
|
||||
</div>
|
||||
{/if}
|
||||
{if="$link.tags"}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
<div id="editlinkform">
|
||||
<form method="post" name="linkform">
|
||||
<input type="hidden" name="lf_linkdate" value="{$link.linkdate}">
|
||||
{if="isset($link.id)"}
|
||||
<input type="hidden" name="lf_id" value="{$link.id}">
|
||||
{/if}
|
||||
<label for="lf_url"><i>URL</i></label><br><input type="text" name="lf_url" id="lf_url" value="{$link.url}" class="lf_input"><br>
|
||||
<label for="lf_title"><i>Title</i></label><br><input type="text" name="lf_title" id="lf_title" value="{$link.title}" class="lf_input"><br>
|
||||
<label for="lf_description"><i>Description</i></label><br><textarea name="lf_description" id="lf_description" rows="4" cols="25">{$link.description}</textarea><br>
|
||||
|
|
|
@ -81,11 +81,11 @@
|
|||
{if="isLoggedIn()"}
|
||||
<div class="linkeditbuttons">
|
||||
<form method="GET" class="buttoneditform">
|
||||
<input type="hidden" name="edit_link" value="{$value.linkdate}">
|
||||
<input type="hidden" name="edit_link" value="{$value.id}">
|
||||
<input type="image" alt="Edit" src="images/edit_icon.png#" title="Edit" class="button_edit">
|
||||
</form><br>
|
||||
<form method="POST" class="buttoneditform">
|
||||
<input type="hidden" name="lf_linkdate" value="{$value.linkdate}">
|
||||
<input type="hidden" name="lf_linkdate" value="{$value.id}">
|
||||
<input type="hidden" name="token" value="{$token}">
|
||||
<input type="hidden" name="delete_link">
|
||||
<input type="image" alt="Delete" src="images/delete_icon.png#" title="Delete"
|
||||
|
@ -101,7 +101,7 @@
|
|||
{if="!$hide_timestamps || isLoggedIn()"}
|
||||
{$updated=$value.updated_timestamp ? 'Edited: '. strftime('%c', $value.updated_timestamp) : 'Permalink'}
|
||||
<span class="linkdate" title="Permalink">
|
||||
<a href="?{$value.linkdate|smallHash}">
|
||||
<a href="?{$value.shorturl}">
|
||||
<span title="{$updated}">
|
||||
{function="strftime('%c', $value.timestamp)"}
|
||||
{if="$value.updated_timestamp"}*{/if}
|
||||
|
|
Loading…
Reference in a new issue