aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2017-05-06 17:12:06 +0200
committerGitHub <noreply@github.com>2017-05-06 17:12:06 +0200
commitf9ff7f1b69f19b42569ffa67280807ba56f5d48a (patch)
tree22126f1de7aac6c03a0b2c609380057740b840ac
parenta271c5f34f99bab38a167d491b69e5942cd6da94 (diff)
parentd16ca2e22f3d7325fc9593fccd8523eebe226567 (diff)
downloadShaarli-f9ff7f1b69f19b42569ffa67280807ba56f5d48a.tar.gz
Shaarli-f9ff7f1b69f19b42569ffa67280807ba56f5d48a.tar.zst
Shaarli-f9ff7f1b69f19b42569ffa67280807ba56f5d48a.zip
Merge pull request #764 from ArthurHoaro/feature/history
History mechanism
-rw-r--r--application/FileUtils.php75
-rw-r--r--application/History.php200
-rw-r--r--application/LinkDB.php31
-rw-r--r--application/NetscapeBookmarkUtils.php5
-rw-r--r--application/config/ConfigManager.php1
-rw-r--r--application/exceptions/IOException.php22
-rw-r--r--index.php23
-rw-r--r--tests/FileUtilsTest.php108
-rw-r--r--tests/HistoryTest.php207
-rw-r--r--tests/LinkDBTest.php2
-rw-r--r--tests/NetscapeBookmarkUtils/BookmarkImportTest.php81
11 files changed, 696 insertions, 59 deletions
diff --git a/application/FileUtils.php b/application/FileUtils.php
index 6cac9825..b8ad8970 100644
--- a/application/FileUtils.php
+++ b/application/FileUtils.php
@@ -1,21 +1,76 @@
1<?php 1<?php
2
3require_once 'exceptions/IOException.php';
4
2/** 5/**
3 * Exception class thrown when a filesystem access failure happens 6 * Class FileUtils
7 *
8 * Utility class for file manipulation.
4 */ 9 */
5class IOException extends Exception 10class FileUtils
6{ 11{
7 private $path; 12 /**
13 * @var string
14 */
15 protected static $phpPrefix = '<?php /* ';
16
17 /**
18 * @var string
19 */
20 protected static $phpSuffix = ' */ ?>';
8 21
9 /** 22 /**
10 * Construct a new IOException 23 * Write data into a file (Shaarli database format).
24 * The data is stored in a PHP file, as a comment, in compressed base64 format.
25 *
26 * The file will be created if it doesn't exist.
27 *
28 * @param string $file File path.
29 * @param string $content Content to write.
30 *
31 * @return int|bool Number of bytes written or false if it fails.
11 * 32 *
12 * @param string $path path to the resource that cannot be accessed 33 * @throws IOException The destination file can't be written.
13 * @param string $message Custom exception message.
14 */ 34 */
15 public function __construct($path, $message = '') 35 public static function writeFlatDB($file, $content)
16 { 36 {
17 $this->path = $path; 37 if (is_file($file) && !is_writeable($file)) {
18 $this->message = empty($message) ? 'Error accessing' : $message; 38 // The datastore exists but is not writeable
19 $this->message .= PHP_EOL . $this->path; 39 throw new IOException($file);
40 } else if (!is_file($file) && !is_writeable(dirname($file))) {
41 // The datastore does not exist and its parent directory is not writeable
42 throw new IOException(dirname($file));
43 }
44
45 return file_put_contents(
46 $file,
47 self::$phpPrefix.base64_encode(gzdeflate(serialize($content))).self::$phpSuffix
48 );
49 }
50
51 /**
52 * Read data from a file containing Shaarli database format content.
53 * If the file isn't readable or doesn't exists, default data will be returned.
54 *
55 * @param string $file File path.
56 * @param mixed $default The default value to return if the file isn't readable.
57 *
58 * @return mixed The content unserialized, or default if the file isn't readable, or false if it fails.
59 */
60 public static function readFlatDB($file, $default = null)
61 {
62 // Note that gzinflate is faster than gzuncompress.
63 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439
64 if (is_readable($file)) {
65 return unserialize(
66 gzinflate(
67 base64_decode(
68 substr(file_get_contents($file), strlen(self::$phpPrefix), -strlen(self::$phpSuffix))
69 )
70 )
71 );
72 }
73
74 return $default;
20 } 75 }
21} 76}
diff --git a/application/History.php b/application/History.php
new file mode 100644
index 00000000..f93b0356
--- /dev/null
+++ b/application/History.php
@@ -0,0 +1,200 @@
1<?php
2
3/**
4 * Class History
5 *
6 * Handle the history file tracing events in Shaarli.
7 * The history is stored as JSON in a file set by 'resource.history' setting.
8 *
9 * Available data:
10 * - event: event key
11 * - datetime: event date, in ISO8601 format.
12 * - id: event item identifier (currently only link IDs).
13 *
14 * Available event keys:
15 * - CREATED: new link
16 * - UPDATED: link updated
17 * - DELETED: link deleted
18 * - SETTINGS: the settings have been updated through the UI.
19 *
20 * Note: new events are put at the beginning of the file and history array.
21 */
22class History
23{
24 /**
25 * @var string Action key: a new link has been created.
26 */
27 const CREATED = 'CREATED';
28
29 /**
30 * @var string Action key: a link has been updated.
31 */
32 const UPDATED = 'UPDATED';
33
34 /**
35 * @var string Action key: a link has been deleted.
36 */
37 const DELETED = 'DELETED';
38
39 /**
40 * @var string Action key: settings have been updated.
41 */
42 const SETTINGS = 'SETTINGS';
43
44 /**
45 * @var string History file path.
46 */
47 protected $historyFilePath;
48
49 /**
50 * @var array History data.
51 */
52 protected $history;
53
54 /**
55 * @var int History retention time in seconds (1 month).
56 */
57 protected $retentionTime = 2678400;
58
59 /**
60 * History constructor.
61 *
62 * @param string $historyFilePath History file path.
63 * @param int $retentionTime History content rentention time in seconds.
64 *
65 * @throws Exception if something goes wrong.
66 */
67 public function __construct($historyFilePath, $retentionTime = null)
68 {
69 $this->historyFilePath = $historyFilePath;
70 if ($retentionTime !== null) {
71 $this->retentionTime = $retentionTime;
72 }
73 }
74
75 /**
76 * Initialize: read history file.
77 *
78 * Allow lazy loading (don't read the file if it isn't necessary).
79 */
80 protected function initialize()
81 {
82 $this->check();
83 $this->read();
84 }
85
86 /**
87 * Add Event: new link.
88 *
89 * @param array $link Link data.
90 */
91 public function addLink($link)
92 {
93 $this->addEvent(self::CREATED, $link['id']);
94 }
95
96 /**
97 * Add Event: update existing link.
98 *
99 * @param array $link Link data.
100 */
101 public function updateLink($link)
102 {
103 $this->addEvent(self::UPDATED, $link['id']);
104 }
105
106 /**
107 * Add Event: delete existing link.
108 *
109 * @param array $link Link data.
110 */
111 public function deleteLink($link)
112 {
113 $this->addEvent(self::DELETED, $link['id']);
114 }
115
116 /**
117 * Add Event: settings updated.
118 */
119 public function updateSettings()
120 {
121 $this->addEvent(self::SETTINGS);
122 }
123
124 /**
125 * Save a new event and write it in the history file.
126 *
127 * @param string $status Event key, should be defined as constant.
128 * @param mixed $id Event item identifier (e.g. link ID).
129 */
130 protected function addEvent($status, $id = null)
131 {
132 if ($this->history === null) {
133 $this->initialize();
134 }
135
136 $item = [
137 'event' => $status,
138 'datetime' => (new DateTime())->format(DateTime::ATOM),
139 'id' => $id !== null ? $id : '',
140 ];
141 $this->history = array_merge([$item], $this->history);
142 $this->write();
143 }
144
145 /**
146 * Check that the history file is writable.
147 * Create the file if it doesn't exist.
148 *
149 * @throws Exception if it isn't writable.
150 */
151 protected function check()
152 {
153 if (! is_file($this->historyFilePath)) {
154 FileUtils::writeFlatDB($this->historyFilePath, []);
155 }
156
157 if (! is_writable($this->historyFilePath)) {
158 throw new Exception('History file isn\'t readable or writable');
159 }
160 }
161
162 /**
163 * Read JSON history file.
164 */
165 protected function read()
166 {
167 $this->history = FileUtils::readFlatDB($this->historyFilePath, []);
168 if ($this->history === false) {
169 throw new Exception('Could not parse history file');
170 }
171 }
172
173 /**
174 * Write JSON history file and delete old entries.
175 */
176 protected function write()
177 {
178 $comparaison = new DateTime('-'. $this->retentionTime . ' seconds');
179 foreach ($this->history as $key => $value) {
180 if (DateTime::createFromFormat(DateTime::ATOM, $value['datetime']) < $comparaison) {
181 unset($this->history[$key]);
182 }
183 }
184 FileUtils::writeFlatDB($this->historyFilePath, array_values($this->history));
185 }
186
187 /**
188 * Get the History.
189 *
190 * @return array
191 */
192 public function getHistory()
193 {
194 if ($this->history === null) {
195 $this->initialize();
196 }
197
198 return $this->history;
199 }
200}
diff --git a/application/LinkDB.php b/application/LinkDB.php
index 1e4d7ce8..0d3c85bd 100644
--- a/application/LinkDB.php
+++ b/application/LinkDB.php
@@ -50,12 +50,6 @@ class LinkDB implements Iterator, Countable, ArrayAccess
50 // Link date storage format 50 // Link date storage format
51 const LINK_DATE_FORMAT = 'Ymd_His'; 51 const LINK_DATE_FORMAT = 'Ymd_His';
52 52
53 // Datastore PHP prefix
54 protected static $phpPrefix = '<?php /* ';
55
56 // Datastore PHP suffix
57 protected static $phpSuffix = ' */ ?>';
58
59 // List of links (associative array) 53 // List of links (associative array)
60 // - key: link date (e.g. "20110823_124546"), 54 // - key: link date (e.g. "20110823_124546"),
61 // - value: associative array (keys: title, description...) 55 // - value: associative array (keys: title, description...)
@@ -295,16 +289,7 @@ You use the community supported version of the original Shaarli project, by Seba
295 return; 289 return;
296 } 290 }
297 291
298 // Read data 292 $this->links = FileUtils::readFlatDB($this->datastore, []);
299 // Note that gzinflate is faster than gzuncompress.
300 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439
301 $this->links = array();
302
303 if (file_exists($this->datastore)) {
304 $this->links = unserialize(gzinflate(base64_decode(
305 substr(file_get_contents($this->datastore),
306 strlen(self::$phpPrefix), -strlen(self::$phpSuffix)))));
307 }
308 293
309 $toremove = array(); 294 $toremove = array();
310 foreach ($this->links as $key => &$link) { 295 foreach ($this->links as $key => &$link) {
@@ -361,19 +346,7 @@ You use the community supported version of the original Shaarli project, by Seba
361 */ 346 */
362 private function write() 347 private function write()
363 { 348 {
364 if (is_file($this->datastore) && !is_writeable($this->datastore)) { 349 FileUtils::writeFlatDB($this->datastore, $this->links);
365 // The datastore exists but is not writeable
366 throw new IOException($this->datastore);
367 } else if (!is_file($this->datastore) && !is_writeable(dirname($this->datastore))) {
368 // The datastore does not exist and its parent directory is not writeable
369 throw new IOException(dirname($this->datastore));
370 }
371
372 file_put_contents(
373 $this->datastore,
374 self::$phpPrefix.base64_encode(gzdeflate(serialize($this->links))).self::$phpSuffix
375 );
376
377 } 350 }
378 351
379 /** 352 /**
diff --git a/application/NetscapeBookmarkUtils.php b/application/NetscapeBookmarkUtils.php
index ab346f81..bbfde138 100644
--- a/application/NetscapeBookmarkUtils.php
+++ b/application/NetscapeBookmarkUtils.php
@@ -95,10 +95,11 @@ class NetscapeBookmarkUtils
95 * @param array $files Server $_FILES parameters 95 * @param array $files Server $_FILES parameters
96 * @param LinkDB $linkDb Loaded LinkDB instance 96 * @param LinkDB $linkDb Loaded LinkDB instance
97 * @param ConfigManager $conf instance 97 * @param ConfigManager $conf instance
98 * @param History $history History instance
98 * 99 *
99 * @return string Summary of the bookmark import status 100 * @return string Summary of the bookmark import status
100 */ 101 */
101 public static function import($post, $files, $linkDb, $conf) 102 public static function import($post, $files, $linkDb, $conf, $history)
102 { 103 {
103 $filename = $files['filetoupload']['name']; 104 $filename = $files['filetoupload']['name'];
104 $filesize = $files['filetoupload']['size']; 105 $filesize = $files['filetoupload']['size'];
@@ -182,6 +183,7 @@ class NetscapeBookmarkUtils
182 $linkDb[$existingLink['id']] = $newLink; 183 $linkDb[$existingLink['id']] = $newLink;
183 $importCount++; 184 $importCount++;
184 $overwriteCount++; 185 $overwriteCount++;
186 $history->updateLink($newLink);
185 continue; 187 continue;
186 } 188 }
187 189
@@ -193,6 +195,7 @@ class NetscapeBookmarkUtils
193 $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); 195 $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']);
194 $linkDb[$newLink['id']] = $newLink; 196 $linkDb[$newLink['id']] = $newLink;
195 $importCount++; 197 $importCount++;
198 $history->addLink($newLink);
196 } 199 }
197 200
198 $linkDb->save($conf->get('resource.page_cache')); 201 $linkDb->save($conf->get('resource.page_cache'));
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index 7bfbfc72..86a917fb 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -301,6 +301,7 @@ class ConfigManager
301 $this->setEmpty('resource.updates', 'data/updates.txt'); 301 $this->setEmpty('resource.updates', 'data/updates.txt');
302 $this->setEmpty('resource.log', 'data/log.txt'); 302 $this->setEmpty('resource.log', 'data/log.txt');
303 $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); 303 $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt');
304 $this->setEmpty('resource.history', 'data/history.php');
304 $this->setEmpty('resource.raintpl_tpl', 'tpl/'); 305 $this->setEmpty('resource.raintpl_tpl', 'tpl/');
305 $this->setEmpty('resource.theme', 'default'); 306 $this->setEmpty('resource.theme', 'default');
306 $this->setEmpty('resource.raintpl_tmp', 'tmp/'); 307 $this->setEmpty('resource.raintpl_tmp', 'tmp/');
diff --git a/application/exceptions/IOException.php b/application/exceptions/IOException.php
new file mode 100644
index 00000000..b563b23d
--- /dev/null
+++ b/application/exceptions/IOException.php
@@ -0,0 +1,22 @@
1<?php
2
3/**
4 * Exception class thrown when a filesystem access failure happens
5 */
6class IOException extends Exception
7{
8 private $path;
9
10 /**
11 * Construct a new IOException
12 *
13 * @param string $path path to the resource that cannot be accessed
14 * @param string $message Custom exception message.
15 */
16 public function __construct($path, $message = '')
17 {
18 $this->path = $path;
19 $this->message = empty($message) ? 'Error accessing' : $message;
20 $this->message .= ' "' . $this->path .'"';
21 }
22}
diff --git a/index.php b/index.php
index e392e501..76aa1ae0 100644
--- a/index.php
+++ b/index.php
@@ -62,6 +62,7 @@ require_once 'application/CachedPage.php';
62require_once 'application/config/ConfigPlugin.php'; 62require_once 'application/config/ConfigPlugin.php';
63require_once 'application/FeedBuilder.php'; 63require_once 'application/FeedBuilder.php';
64require_once 'application/FileUtils.php'; 64require_once 'application/FileUtils.php';
65require_once 'application/History.php';
65require_once 'application/HttpUtils.php'; 66require_once 'application/HttpUtils.php';
66require_once 'application/Languages.php'; 67require_once 'application/Languages.php';
67require_once 'application/LinkDB.php'; 68require_once 'application/LinkDB.php';
@@ -727,6 +728,12 @@ function renderPage($conf, $pluginManager, $LINKSDB)
727 die($e->getMessage()); 728 die($e->getMessage());
728 } 729 }
729 730
731 try {
732 $history = new History($conf->get('resource.history'));
733 } catch(Exception $e) {
734 die($e->getMessage());
735 }
736
730 $PAGE = new PageBuilder($conf); 737 $PAGE = new PageBuilder($conf);
731 $PAGE->assign('linkcount', count($LINKSDB)); 738 $PAGE->assign('linkcount', count($LINKSDB));
732 $PAGE->assign('privateLinkcount', count_private($LINKSDB)); 739 $PAGE->assign('privateLinkcount', count_private($LINKSDB));
@@ -1125,6 +1132,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1125 $conf->set('api.secret', escape($_POST['apiSecret'])); 1132 $conf->set('api.secret', escape($_POST['apiSecret']));
1126 try { 1133 try {
1127 $conf->write(isLoggedIn()); 1134 $conf->write(isLoggedIn());
1135 $history->updateSettings();
1128 invalidateCaches($conf->get('resource.page_cache')); 1136 invalidateCaches($conf->get('resource.page_cache'));
1129 } 1137 }
1130 catch(Exception $e) { 1138 catch(Exception $e) {
@@ -1159,6 +1167,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1159 $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false)); 1167 $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false));
1160 $PAGE->assign('api_enabled', $conf->get('api.enabled', true)); 1168 $PAGE->assign('api_enabled', $conf->get('api.enabled', true));
1161 $PAGE->assign('api_secret', $conf->get('api.secret')); 1169 $PAGE->assign('api_secret', $conf->get('api.secret'));
1170 $history->updateSettings();
1162 $PAGE->renderPage('configure'); 1171 $PAGE->renderPage('configure');
1163 exit; 1172 exit;
1164 } 1173 }
@@ -1188,6 +1197,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1188 unset($tags[array_search($needle,$tags)]); // Remove tag. 1197 unset($tags[array_search($needle,$tags)]); // Remove tag.
1189 $value['tags']=trim(implode(' ',$tags)); 1198 $value['tags']=trim(implode(' ',$tags));
1190 $LINKSDB[$key]=$value; 1199 $LINKSDB[$key]=$value;
1200 $history->updateLink($LINKSDB[$key]);
1191 } 1201 }
1192 $LINKSDB->save($conf->get('resource.page_cache')); 1202 $LINKSDB->save($conf->get('resource.page_cache'));
1193 echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?do=changetag\';</script>'; 1203 echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?do=changetag\';</script>';
@@ -1205,6 +1215,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1205 $tags[array_search($needle, $tags)] = trim($_POST['totag']); 1215 $tags[array_search($needle, $tags)] = trim($_POST['totag']);
1206 $value['tags'] = implode(' ', array_unique($tags)); 1216 $value['tags'] = implode(' ', array_unique($tags));
1207 $LINKSDB[$key] = $value; 1217 $LINKSDB[$key] = $value;
1218 $history->updateLink($LINKSDB[$key]);
1208 } 1219 }
1209 $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk. 1220 $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk.
1210 echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode(escape($_POST['totag'])).'\';</script>'; 1221 echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode(escape($_POST['totag'])).'\';</script>';
@@ -1239,11 +1250,13 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1239 $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); 1250 $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
1240 $updated = new DateTime(); 1251 $updated = new DateTime();
1241 $shortUrl = $LINKSDB[$id]['shorturl']; 1252 $shortUrl = $LINKSDB[$id]['shorturl'];
1253 $new = false;
1242 } else { 1254 } else {
1243 // New link 1255 // New link
1244 $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); 1256 $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
1245 $updated = null; 1257 $updated = null;
1246 $shortUrl = link_small_hash($created, $id); 1258 $shortUrl = link_small_hash($created, $id);
1259 $new = true;
1247 } 1260 }
1248 1261
1249 // Remove multiple spaces. 1262 // Remove multiple spaces.
@@ -1282,6 +1295,11 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1282 1295
1283 $LINKSDB[$id] = $link; 1296 $LINKSDB[$id] = $link;
1284 $LINKSDB->save($conf->get('resource.page_cache')); 1297 $LINKSDB->save($conf->get('resource.page_cache'));
1298 if ($new) {
1299 $history->addLink($link);
1300 } else {
1301 $history->updateLink($link);
1302 }
1285 1303
1286 // If we are called from the bookmarklet, we must close the popup: 1304 // If we are called from the bookmarklet, we must close the popup:
1287 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { 1305 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
@@ -1332,6 +1350,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1332 $pluginManager->executeHooks('delete_link', $link); 1350 $pluginManager->executeHooks('delete_link', $link);
1333 unset($LINKSDB[$id]); 1351 unset($LINKSDB[$id]);
1334 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk 1352 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk
1353 $history->deleteLink($link);
1335 1354
1336 // If we are called from the bookmarklet, we must close the popup: 1355 // If we are called from the bookmarklet, we must close the popup:
1337 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } 1356 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
@@ -1529,7 +1548,8 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1529 $_POST, 1548 $_POST,
1530 $_FILES, 1549 $_FILES,
1531 $LINKSDB, 1550 $LINKSDB,
1532 $conf 1551 $conf,
1552 $history
1533 ); 1553 );
1534 echo '<script>alert("'.$status.'");document.location=\'?do=' 1554 echo '<script>alert("'.$status.'");document.location=\'?do='
1535 .Router::$PAGE_IMPORT .'\';</script>'; 1555 .Router::$PAGE_IMPORT .'\';</script>';
@@ -1558,6 +1578,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1558 1578
1559 // Plugin administration form action 1579 // Plugin administration form action
1560 if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) { 1580 if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) {
1581 $history->updateSettings();
1561 try { 1582 try {
1562 if (isset($_POST['parameters_form'])) { 1583 if (isset($_POST['parameters_form'])) {
1563 unset($_POST['parameters_form']); 1584 unset($_POST['parameters_form']);
diff --git a/tests/FileUtilsTest.php b/tests/FileUtilsTest.php
new file mode 100644
index 00000000..d764e495
--- /dev/null
+++ b/tests/FileUtilsTest.php
@@ -0,0 +1,108 @@
1<?php
2
3require_once 'application/FileUtils.php';
4
5/**
6 * Class FileUtilsTest
7 *
8 * Test file utility class.
9 */
10class FileUtilsTest extends PHPUnit_Framework_TestCase
11{
12 /**
13 * @var string Test file path.
14 */
15 protected static $file = 'sandbox/flat.db';
16
17 /**
18 * Delete test file after every test.
19 */
20 public function tearDown()
21 {
22 @unlink(self::$file);
23 }
24
25 /**
26 * Test writeDB, then readDB with different data.
27 */
28 public function testSimpleWriteRead()
29 {
30 $data = ['blue', 'red'];
31 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
32 $this->assertTrue(startsWith(file_get_contents(self::$file), '<?php /*'));
33 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
34
35 $data = 0;
36 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
37 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
38
39 $data = null;
40 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
41 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
42
43 $data = false;
44 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
45 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
46 }
47
48 /**
49 * File not writable: raise an exception.
50 *
51 * @expectedException IOException
52 * @expectedExceptionMessage Error accessing "sandbox/flat.db"
53 */
54 public function testWriteWithoutPermission()
55 {
56 touch(self::$file);
57 chmod(self::$file, 0440);
58 FileUtils::writeFlatDB(self::$file, null);
59 }
60
61 /**
62 * Folder non existent: raise an exception.
63 *
64 * @expectedException IOException
65 * @expectedExceptionMessage Error accessing "nopefolder"
66 */
67 public function testWriteFolderDoesNotExist()
68 {
69 FileUtils::writeFlatDB('nopefolder/file', null);
70 }
71
72 /**
73 * Folder non writable: raise an exception.
74 *
75 * @expectedException IOException
76 * @expectedExceptionMessage Error accessing "sandbox"
77 */
78 public function testWriteFolderPermission()
79 {
80 chmod(dirname(self::$file), 0555);
81 try {
82 FileUtils::writeFlatDB(self::$file, null);
83 } catch (Exception $e) {
84 chmod(dirname(self::$file), 0755);
85 throw $e;
86 }
87 }
88
89 /**
90 * Read non existent file, use default parameter.
91 */
92 public function testReadNotExistentFile()
93 {
94 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
95 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
96 }
97
98 /**
99 * Read non readable file, use default parameter.
100 */
101 public function testReadNotReadable()
102 {
103 touch(self::$file);
104 chmod(self::$file, 0220);
105 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
106 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
107 }
108}
diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php
new file mode 100644
index 00000000..91525845
--- /dev/null
+++ b/tests/HistoryTest.php
@@ -0,0 +1,207 @@
1<?php
2
3require_once 'application/History.php';
4
5
6class HistoryTest extends PHPUnit_Framework_TestCase
7{
8 /**
9 * @var string History file path
10 */
11 protected static $historyFilePath = 'sandbox/history.php';
12
13 /**
14 * Delete history file.
15 */
16 public function tearDown()
17 {
18 @unlink(self::$historyFilePath);
19 }
20
21 /**
22 * Test that the history file is created if it doesn't exist.
23 */
24 public function testConstructLazyLoading()
25 {
26 new History(self::$historyFilePath);
27 $this->assertFileNotExists(self::$historyFilePath);
28 }
29
30 /**
31 * Test that the history file is created if it doesn't exist.
32 */
33 public function testAddEventCreateFile()
34 {
35 $history = new History(self::$historyFilePath);
36 $history->updateSettings();
37 $this->assertFileExists(self::$historyFilePath);
38 }
39
40 /**
41 * Not writable history file: raise an exception.
42 *
43 * @expectedException Exception
44 * @expectedExceptionMessage History file isn't readable or writable
45 */
46 public function testConstructNotWritable()
47 {
48 touch(self::$historyFilePath);
49 chmod(self::$historyFilePath, 0440);
50 $history = new History(self::$historyFilePath);
51 $history->updateSettings();
52 }
53
54 /**
55 * Not parsable history file: raise an exception.
56 *
57 * @expectedException Exception
58 * @expectedExceptionMessageRegExp /Could not parse history file/
59 */
60 public function testConstructNotParsable()
61 {
62 file_put_contents(self::$historyFilePath, 'not parsable');
63 $history = new History(self::$historyFilePath);
64 // gzinflate generates a warning
65 @$history->updateSettings();
66 }
67
68 /**
69 * Test add link event
70 */
71 public function testAddLink()
72 {
73 $history = new History(self::$historyFilePath);
74 $history->addLink(['id' => 0]);
75 $actual = $history->getHistory()[0];
76 $this->assertEquals(History::CREATED, $actual['event']);
77 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
78 $this->assertEquals(0, $actual['id']);
79
80 $history = new History(self::$historyFilePath);
81 $history->addLink(['id' => 1]);
82 $actual = $history->getHistory()[0];
83 $this->assertEquals(History::CREATED, $actual['event']);
84 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
85 $this->assertEquals(1, $actual['id']);
86
87 $history = new History(self::$historyFilePath);
88 $history->addLink(['id' => 'str']);
89 $actual = $history->getHistory()[0];
90 $this->assertEquals(History::CREATED, $actual['event']);
91 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
92 $this->assertEquals('str', $actual['id']);
93 }
94
95 /**
96 * Test updated link event
97 */
98 public function testUpdateLink()
99 {
100 $history = new History(self::$historyFilePath);
101 $history->updateLink(['id' => 1]);
102 $actual = $history->getHistory()[0];
103 $this->assertEquals(History::UPDATED, $actual['event']);
104 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
105 $this->assertEquals(1, $actual['id']);
106 }
107
108 /**
109 * Test delete link event
110 */
111 public function testDeleteLink()
112 {
113 $history = new History(self::$historyFilePath);
114 $history->deleteLink(['id' => 1]);
115 $actual = $history->getHistory()[0];
116 $this->assertEquals(History::DELETED, $actual['event']);
117 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
118 $this->assertEquals(1, $actual['id']);
119 }
120
121 /**
122 * Test updated settings event
123 */
124 public function testUpdateSettings()
125 {
126 $history = new History(self::$historyFilePath);
127 $history->updateSettings();
128 $actual = $history->getHistory()[0];
129 $this->assertEquals(History::SETTINGS, $actual['event']);
130 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
131 $this->assertEmpty($actual['id']);
132 }
133
134 /**
135 * Make sure that new items are stored at the beginning
136 */
137 public function testHistoryOrder()
138 {
139 $history = new History(self::$historyFilePath);
140 $history->updateLink(['id' => 1]);
141 $actual = $history->getHistory()[0];
142 $this->assertEquals(History::UPDATED, $actual['event']);
143 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
144 $this->assertEquals(1, $actual['id']);
145
146 $history->addLink(['id' => 1]);
147 $actual = $history->getHistory()[0];
148 $this->assertEquals(History::CREATED, $actual['event']);
149 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
150 $this->assertEquals(1, $actual['id']);
151 }
152
153 /**
154 * Re-read history from file after writing an event
155 */
156 public function testHistoryRead()
157 {
158 $history = new History(self::$historyFilePath);
159 $history->updateLink(['id' => 1]);
160 $history = new History(self::$historyFilePath);
161 $actual = $history->getHistory()[0];
162 $this->assertEquals(History::UPDATED, $actual['event']);
163 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
164 $this->assertEquals(1, $actual['id']);
165 }
166
167 /**
168 * Re-read history from file after writing an event and make sure that the order is correct
169 */
170 public function testHistoryOrderRead()
171 {
172 $history = new History(self::$historyFilePath);
173 $history->updateLink(['id' => 1]);
174 $history->addLink(['id' => 1]);
175
176 $history = new History(self::$historyFilePath);
177 $actual = $history->getHistory()[0];
178 $this->assertEquals(History::CREATED, $actual['event']);
179 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
180 $this->assertEquals(1, $actual['id']);
181
182 $actual = $history->getHistory()[1];
183 $this->assertEquals(History::UPDATED, $actual['event']);
184 $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime']));
185 $this->assertEquals(1, $actual['id']);
186 }
187
188 /**
189 * Test retention time: delete old entries.
190 */
191 public function testHistoryRententionTime()
192 {
193 $history = new History(self::$historyFilePath, 5);
194 $history->updateLink(['id' => 1]);
195 $this->assertEquals(1, count($history->getHistory()));
196 $arr = $history->getHistory();
197 $arr[0]['datetime'] = (new DateTime('-1 hour'))->format(DateTime::ATOM);
198 FileUtils::writeFlatDB(self::$historyFilePath, $arr);
199
200 $history = new History(self::$historyFilePath, 60);
201 $this->assertEquals(1, count($history->getHistory()));
202 $this->assertEquals(1, $history->getHistory()[0]['id']);
203 $history->updateLink(['id' => 2]);
204 $this->assertEquals(1, count($history->getHistory()));
205 $this->assertEquals(2, $history->getHistory()[0]['id']);
206 }
207}
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php
index 1f62a34a..7bf98f92 100644
--- a/tests/LinkDBTest.php
+++ b/tests/LinkDBTest.php
@@ -101,7 +101,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
101 * Attempt to instantiate a LinkDB whereas the datastore is not writable 101 * Attempt to instantiate a LinkDB whereas the datastore is not writable
102 * 102 *
103 * @expectedException IOException 103 * @expectedException IOException
104 * @expectedExceptionMessageRegExp /Error accessing\nnull/ 104 * @expectedExceptionMessageRegExp /Error accessing "null"/
105 */ 105 */
106 public function testConstructDatastoreNotWriteable() 106 public function testConstructDatastoreNotWriteable()
107 { 107 {
diff --git a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php
index 5925a8e1..f838f259 100644
--- a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php
+++ b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php
@@ -34,6 +34,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
34 protected static $testDatastore = 'sandbox/datastore.php'; 34 protected static $testDatastore = 'sandbox/datastore.php';
35 35
36 /** 36 /**
37 * @var string History file path
38 */
39 protected static $historyFilePath = 'sandbox/history.php';
40
41 /**
37 * @var LinkDB private LinkDB instance 42 * @var LinkDB private LinkDB instance
38 */ 43 */
39 protected $linkDb = null; 44 protected $linkDb = null;
@@ -49,6 +54,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
49 protected $conf; 54 protected $conf;
50 55
51 /** 56 /**
57 * @var History instance.
58 */
59 protected $history;
60
61 /**
52 * @var string Save the current timezone. 62 * @var string Save the current timezone.
53 */ 63 */
54 protected static $defaultTimeZone; 64 protected static $defaultTimeZone;
@@ -73,6 +83,15 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
73 $this->linkDb = new LinkDB(self::$testDatastore, true, false); 83 $this->linkDb = new LinkDB(self::$testDatastore, true, false);
74 $this->conf = new ConfigManager('tests/utils/config/configJson'); 84 $this->conf = new ConfigManager('tests/utils/config/configJson');
75 $this->conf->set('resource.page_cache', $this->pagecache); 85 $this->conf->set('resource.page_cache', $this->pagecache);
86 $this->history = new History(self::$historyFilePath);
87 }
88
89 /**
90 * Delete history file.
91 */
92 public function tearDown()
93 {
94 @unlink(self::$historyFilePath);
76 } 95 }
77 96
78 public static function tearDownAfterClass() 97 public static function tearDownAfterClass()
@@ -89,7 +108,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
89 $this->assertEquals( 108 $this->assertEquals(
90 'File empty.htm (0 bytes) has an unknown file format.' 109 'File empty.htm (0 bytes) has an unknown file format.'
91 .' Nothing was imported.', 110 .' Nothing was imported.',
92 NetscapeBookmarkUtils::import(NULL, $files, NULL, $this->conf) 111 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history)
93 ); 112 );
94 $this->assertEquals(0, count($this->linkDb)); 113 $this->assertEquals(0, count($this->linkDb));
95 } 114 }
@@ -102,7 +121,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
102 $files = file2array('no_doctype.htm'); 121 $files = file2array('no_doctype.htm');
103 $this->assertEquals( 122 $this->assertEquals(
104 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.', 123 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
105 NetscapeBookmarkUtils::import(NULL, $files, NULL, $this->conf) 124 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history)
106 ); 125 );
107 $this->assertEquals(0, count($this->linkDb)); 126 $this->assertEquals(0, count($this->linkDb));
108 } 127 }
@@ -116,7 +135,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
116 $this->assertEquals( 135 $this->assertEquals(
117 'File internet_explorer_encoding.htm (356 bytes) was successfully processed:' 136 'File internet_explorer_encoding.htm (356 bytes) was successfully processed:'
118 .' 1 links imported, 0 links overwritten, 0 links skipped.', 137 .' 1 links imported, 0 links overwritten, 0 links skipped.',
119 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) 138 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
120 ); 139 );
121 $this->assertEquals(1, count($this->linkDb)); 140 $this->assertEquals(1, count($this->linkDb));
122 $this->assertEquals(0, count_private($this->linkDb)); 141 $this->assertEquals(0, count_private($this->linkDb));
@@ -145,7 +164,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
145 $this->assertEquals( 164 $this->assertEquals(
146 'File netscape_nested.htm (1337 bytes) was successfully processed:' 165 'File netscape_nested.htm (1337 bytes) was successfully processed:'
147 .' 8 links imported, 0 links overwritten, 0 links skipped.', 166 .' 8 links imported, 0 links overwritten, 0 links skipped.',
148 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) 167 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
149 ); 168 );
150 $this->assertEquals(8, count($this->linkDb)); 169 $this->assertEquals(8, count($this->linkDb));
151 $this->assertEquals(2, count_private($this->linkDb)); 170 $this->assertEquals(2, count_private($this->linkDb));
@@ -267,7 +286,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
267 $this->assertEquals( 286 $this->assertEquals(
268 'File netscape_basic.htm (482 bytes) was successfully processed:' 287 'File netscape_basic.htm (482 bytes) was successfully processed:'
269 .' 2 links imported, 0 links overwritten, 0 links skipped.', 288 .' 2 links imported, 0 links overwritten, 0 links skipped.',
270 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) 289 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
271 ); 290 );
272 291
273 $this->assertEquals(2, count($this->linkDb)); 292 $this->assertEquals(2, count($this->linkDb));
@@ -312,7 +331,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
312 $this->assertEquals( 331 $this->assertEquals(
313 'File netscape_basic.htm (482 bytes) was successfully processed:' 332 'File netscape_basic.htm (482 bytes) was successfully processed:'
314 .' 2 links imported, 0 links overwritten, 0 links skipped.', 333 .' 2 links imported, 0 links overwritten, 0 links skipped.',
315 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 334 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
316 ); 335 );
317 $this->assertEquals(2, count($this->linkDb)); 336 $this->assertEquals(2, count($this->linkDb));
318 $this->assertEquals(1, count_private($this->linkDb)); 337 $this->assertEquals(1, count_private($this->linkDb));
@@ -356,7 +375,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
356 $this->assertEquals( 375 $this->assertEquals(
357 'File netscape_basic.htm (482 bytes) was successfully processed:' 376 'File netscape_basic.htm (482 bytes) was successfully processed:'
358 .' 2 links imported, 0 links overwritten, 0 links skipped.', 377 .' 2 links imported, 0 links overwritten, 0 links skipped.',
359 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 378 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
360 ); 379 );
361 $this->assertEquals(2, count($this->linkDb)); 380 $this->assertEquals(2, count($this->linkDb));
362 $this->assertEquals(0, count_private($this->linkDb)); 381 $this->assertEquals(0, count_private($this->linkDb));
@@ -380,7 +399,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
380 $this->assertEquals( 399 $this->assertEquals(
381 'File netscape_basic.htm (482 bytes) was successfully processed:' 400 'File netscape_basic.htm (482 bytes) was successfully processed:'
382 .' 2 links imported, 0 links overwritten, 0 links skipped.', 401 .' 2 links imported, 0 links overwritten, 0 links skipped.',
383 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 402 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
384 ); 403 );
385 $this->assertEquals(2, count($this->linkDb)); 404 $this->assertEquals(2, count($this->linkDb));
386 $this->assertEquals(2, count_private($this->linkDb)); 405 $this->assertEquals(2, count_private($this->linkDb));
@@ -406,7 +425,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
406 $this->assertEquals( 425 $this->assertEquals(
407 'File netscape_basic.htm (482 bytes) was successfully processed:' 426 'File netscape_basic.htm (482 bytes) was successfully processed:'
408 .' 2 links imported, 0 links overwritten, 0 links skipped.', 427 .' 2 links imported, 0 links overwritten, 0 links skipped.',
409 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 428 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
410 ); 429 );
411 $this->assertEquals(2, count($this->linkDb)); 430 $this->assertEquals(2, count($this->linkDb));
412 $this->assertEquals(2, count_private($this->linkDb)); 431 $this->assertEquals(2, count_private($this->linkDb));
@@ -426,7 +445,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
426 $this->assertEquals( 445 $this->assertEquals(
427 'File netscape_basic.htm (482 bytes) was successfully processed:' 446 'File netscape_basic.htm (482 bytes) was successfully processed:'
428 .' 2 links imported, 2 links overwritten, 0 links skipped.', 447 .' 2 links imported, 2 links overwritten, 0 links skipped.',
429 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 448 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
430 ); 449 );
431 $this->assertEquals(2, count($this->linkDb)); 450 $this->assertEquals(2, count($this->linkDb));
432 $this->assertEquals(0, count_private($this->linkDb)); 451 $this->assertEquals(0, count_private($this->linkDb));
@@ -452,7 +471,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
452 $this->assertEquals( 471 $this->assertEquals(
453 'File netscape_basic.htm (482 bytes) was successfully processed:' 472 'File netscape_basic.htm (482 bytes) was successfully processed:'
454 .' 2 links imported, 0 links overwritten, 0 links skipped.', 473 .' 2 links imported, 0 links overwritten, 0 links skipped.',
455 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 474 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
456 ); 475 );
457 $this->assertEquals(2, count($this->linkDb)); 476 $this->assertEquals(2, count($this->linkDb));
458 $this->assertEquals(0, count_private($this->linkDb)); 477 $this->assertEquals(0, count_private($this->linkDb));
@@ -473,7 +492,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
473 $this->assertEquals( 492 $this->assertEquals(
474 'File netscape_basic.htm (482 bytes) was successfully processed:' 493 'File netscape_basic.htm (482 bytes) was successfully processed:'
475 .' 2 links imported, 2 links overwritten, 0 links skipped.', 494 .' 2 links imported, 2 links overwritten, 0 links skipped.',
476 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 495 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
477 ); 496 );
478 $this->assertEquals(2, count($this->linkDb)); 497 $this->assertEquals(2, count($this->linkDb));
479 $this->assertEquals(2, count_private($this->linkDb)); 498 $this->assertEquals(2, count_private($this->linkDb));
@@ -497,7 +516,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
497 $this->assertEquals( 516 $this->assertEquals(
498 'File netscape_basic.htm (482 bytes) was successfully processed:' 517 'File netscape_basic.htm (482 bytes) was successfully processed:'
499 .' 2 links imported, 0 links overwritten, 0 links skipped.', 518 .' 2 links imported, 0 links overwritten, 0 links skipped.',
500 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 519 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
501 ); 520 );
502 $this->assertEquals(2, count($this->linkDb)); 521 $this->assertEquals(2, count($this->linkDb));
503 $this->assertEquals(0, count_private($this->linkDb)); 522 $this->assertEquals(0, count_private($this->linkDb));
@@ -507,7 +526,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
507 $this->assertEquals( 526 $this->assertEquals(
508 'File netscape_basic.htm (482 bytes) was successfully processed:' 527 'File netscape_basic.htm (482 bytes) was successfully processed:'
509 .' 0 links imported, 0 links overwritten, 2 links skipped.', 528 .' 0 links imported, 0 links overwritten, 2 links skipped.',
510 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 529 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
511 ); 530 );
512 $this->assertEquals(2, count($this->linkDb)); 531 $this->assertEquals(2, count($this->linkDb));
513 $this->assertEquals(0, count_private($this->linkDb)); 532 $this->assertEquals(0, count_private($this->linkDb));
@@ -526,7 +545,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
526 $this->assertEquals( 545 $this->assertEquals(
527 'File netscape_basic.htm (482 bytes) was successfully processed:' 546 'File netscape_basic.htm (482 bytes) was successfully processed:'
528 .' 2 links imported, 0 links overwritten, 0 links skipped.', 547 .' 2 links imported, 0 links overwritten, 0 links skipped.',
529 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 548 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
530 ); 549 );
531 $this->assertEquals(2, count($this->linkDb)); 550 $this->assertEquals(2, count($this->linkDb));
532 $this->assertEquals(0, count_private($this->linkDb)); 551 $this->assertEquals(0, count_private($this->linkDb));
@@ -553,7 +572,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
553 $this->assertEquals( 572 $this->assertEquals(
554 'File netscape_basic.htm (482 bytes) was successfully processed:' 573 'File netscape_basic.htm (482 bytes) was successfully processed:'
555 .' 2 links imported, 0 links overwritten, 0 links skipped.', 574 .' 2 links imported, 0 links overwritten, 0 links skipped.',
556 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) 575 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
557 ); 576 );
558 $this->assertEquals(2, count($this->linkDb)); 577 $this->assertEquals(2, count($this->linkDb));
559 $this->assertEquals(0, count_private($this->linkDb)); 578 $this->assertEquals(0, count_private($this->linkDb));
@@ -578,7 +597,7 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
578 $this->assertEquals( 597 $this->assertEquals(
579 'File same_date.htm (453 bytes) was successfully processed:' 598 'File same_date.htm (453 bytes) was successfully processed:'
580 .' 3 links imported, 0 links overwritten, 0 links skipped.', 599 .' 3 links imported, 0 links overwritten, 0 links skipped.',
581 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) 600 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf, $this->history)
582 ); 601 );
583 $this->assertEquals(3, count($this->linkDb)); 602 $this->assertEquals(3, count($this->linkDb));
584 $this->assertEquals(0, count_private($this->linkDb)); 603 $this->assertEquals(0, count_private($this->linkDb));
@@ -595,4 +614,32 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase
595 $this->linkDb[2]['id'] 614 $this->linkDb[2]['id']
596 ); 615 );
597 } 616 }
617
618 public function testImportCreateUpdateHistory()
619 {
620 $post = [
621 'privacy' => 'public',
622 'overwrite' => 'true',
623 ];
624 $files = file2array('netscape_basic.htm');
625 $nbLinks = 2;
626 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history);
627 $history = $this->history->getHistory();
628 $this->assertEquals($nbLinks, count($history));
629 foreach ($history as $value) {
630 $this->assertEquals(History::CREATED, $value['event']);
631 $this->assertTrue(new DateTime('-5 seconds') < DateTime::createFromFormat(DateTime::ATOM, $value['datetime']));
632 $this->assertTrue(is_int($value['id']));
633 }
634
635 // re-import as private, enable overwriting
636 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history);
637 $history = $this->history->getHistory();
638 $this->assertEquals($nbLinks * 2, count($history));
639 for ($i = 0 ; $i < $nbLinks ; $i++) {
640 $this->assertEquals(History::UPDATED, $history[$i]['event']);
641 $this->assertTrue(new DateTime('-5 seconds') < DateTime::createFromFormat(DateTime::ATOM, $history[$i]['datetime']));
642 $this->assertTrue(is_int($history[$i]['id']));
643 }
644 }
598} 645}