From 4306b184c4471825f916d895b047ed03fdf58985 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 16 Jan 2017 12:31:08 +0100 Subject: [PATCH] History mechanism Use case: rest API service * saved by default in data/history * same format as datastore.php * traced events: * save/edit/delete link * change settings or plugins settings * rename tag --- application/History.php | 183 ++++++++++++++++ application/NetscapeBookmarkUtils.php | 5 +- application/config/ConfigManager.php | 1 + index.php | 23 ++- tests/HistoryTest.php | 195 ++++++++++++++++++ .../BookmarkImportTest.php | 81 ++++++-- 6 files changed, 469 insertions(+), 19 deletions(-) create mode 100644 application/History.php create mode 100644 tests/HistoryTest.php diff --git a/application/History.php b/application/History.php new file mode 100644 index 00000000..c06067df --- /dev/null +++ b/application/History.php @@ -0,0 +1,183 @@ +historyFilePath = $historyFilePath; + if ($retentionTime !== null) { + $this->retentionTime = $retentionTime; + } + $this->check(); + $this->read(); + } + + /** + * Add Event: new link. + * + * @param array $link Link data. + */ + public function addLink($link) + { + $this->addEvent(self::CREATED, $link['id']); + } + + /** + * Add Event: update existing link. + * + * @param array $link Link data. + */ + public function updateLink($link) + { + $this->addEvent(self::UPDATED, $link['id']); + } + + /** + * Add Event: delete existing link. + * + * @param array $link Link data. + */ + public function deleteLink($link) + { + $this->addEvent(self::DELETED, $link['id']); + } + + /** + * Add Event: settings updated. + */ + public function updateSettings() + { + $this->addEvent(self::SETTINGS); + } + + /** + * Save a new event and write it in the history file. + * + * @param string $status Event key, should be defined as constant. + * @param mixed $id Event item identifier (e.g. link ID). + */ + protected function addEvent($status, $id = null) + { + $item = [ + 'event' => $status, + 'datetime' => (new DateTime())->format(DateTime::ATOM), + 'id' => $id !== null ? $id : '', + ]; + $this->history = array_merge([$item], $this->history); + $this->write(); + } + + /** + * Check that the history file is writable. + * Create the file if it doesn't exist. + * + * @throws Exception if it isn't writable. + */ + protected function check() + { + if (! is_file($this->historyFilePath)) { + FileUtils::writeFlatDB($this->historyFilePath, []); + } + + if (! is_writable($this->historyFilePath)) { + throw new Exception('History file isn\'t readable or writable'); + } + } + + /** + * Read JSON history file. + */ + protected function read() + { + $this->history = FileUtils::readFlatDB($this->historyFilePath, []); + if ($this->history === false) { + throw new Exception('Could not parse history file'); + } + } + + /** + * Write JSON history file and delete old entries. + */ + protected function write() + { + $comparaison = new DateTime('-'. $this->retentionTime . ' seconds'); + foreach ($this->history as $key => $value) { + if (DateTime::createFromFormat(DateTime::ATOM, $value['datetime']) < $comparaison) { + unset($this->history[$key]); + } + } + FileUtils::writeFlatDB($this->historyFilePath, array_values($this->history)); + } + + /** + * Get the History. + * + * @return array + */ + public function getHistory() + { + return $this->history; + } +} 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 @@ private static function importStatus( * @param array $files Server $_FILES parameters * @param LinkDB $linkDb Loaded LinkDB instance * @param ConfigManager $conf instance + * @param History $history History instance * * @return string Summary of the bookmark import status */ - public static function import($post, $files, $linkDb, $conf) + public static function import($post, $files, $linkDb, $conf, $history) { $filename = $files['filetoupload']['name']; $filesize = $files['filetoupload']['size']; @@ -182,6 +183,7 @@ public static function import($post, $files, $linkDb, $conf) $linkDb[$existingLink['id']] = $newLink; $importCount++; $overwriteCount++; + $history->updateLink($newLink); continue; } @@ -193,6 +195,7 @@ public static function import($post, $files, $linkDb, $conf) $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); $linkDb[$newLink['id']] = $newLink; $importCount++; + $history->addLink($newLink); } $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 @@ protected function setDefaultValues() $this->setEmpty('resource.updates', 'data/updates.txt'); $this->setEmpty('resource.log', 'data/log.txt'); $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); + $this->setEmpty('resource.history', 'data/history.php'); $this->setEmpty('resource.raintpl_tpl', 'tpl/'); $this->setEmpty('resource.theme', 'default'); $this->setEmpty('resource.raintpl_tmp', 'tmp/'); diff --git a/index.php b/index.php index cc7f3ca3..7f357c69 100644 --- a/index.php +++ b/index.php @@ -65,6 +65,7 @@ require_once 'application/config/ConfigPlugin.php'; require_once 'application/FeedBuilder.php'; require_once 'application/FileUtils.php'; +require_once 'application/History.php'; require_once 'application/HttpUtils.php'; require_once 'application/Languages.php'; require_once 'application/LinkDB.php'; @@ -754,6 +755,12 @@ function renderPage($conf, $pluginManager, $LINKSDB) die($e->getMessage()); } + try { + $history = new History($conf->get('resource.history')); + } catch(Exception $e) { + die($e->getMessage()); + } + $PAGE = new PageBuilder($conf); $PAGE->assign('linkcount', count($LINKSDB)); $PAGE->assign('privateLinkcount', count_private($LINKSDB)); @@ -1146,6 +1153,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $conf->set('api.secret', escape($_POST['apiSecret'])); try { $conf->write(isLoggedIn()); + $history->updateSettings(); invalidateCaches($conf->get('resource.page_cache')); } catch(Exception $e) { @@ -1177,6 +1185,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false)); $PAGE->assign('api_enabled', $conf->get('api.enabled', true)); $PAGE->assign('api_secret', $conf->get('api.secret')); + $history->updateSettings(); $PAGE->renderPage('configure'); exit; } @@ -1206,6 +1215,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) unset($tags[array_search($needle,$tags)]); // Remove tag. $value['tags']=trim(implode(' ',$tags)); $LINKSDB[$key]=$value; + $history->updateLink($LINKSDB[$key]); } $LINKSDB->save($conf->get('resource.page_cache')); echo ''; @@ -1223,6 +1233,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $tags[array_search($needle, $tags)] = trim($_POST['totag']); $value['tags'] = implode(' ', array_unique($tags)); $LINKSDB[$key] = $value; + $history->updateLink($LINKSDB[$key]); } $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk. echo ''; @@ -1257,11 +1268,13 @@ function renderPage($conf, $pluginManager, $LINKSDB) $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); $updated = new DateTime(); $shortUrl = $LINKSDB[$id]['shorturl']; + $new = false; } else { // New link $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); $updated = null; $shortUrl = link_small_hash($created, $id); + $new = true; } // Remove multiple spaces. @@ -1300,6 +1313,11 @@ function renderPage($conf, $pluginManager, $LINKSDB) $LINKSDB[$id] = $link; $LINKSDB->save($conf->get('resource.page_cache')); + if ($new) { + $history->addLink($link); + } else { + $history->updateLink($link); + } // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { @@ -1346,6 +1364,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $pluginManager->executeHooks('delete_link', $link); unset($LINKSDB[$id]); $LINKSDB->save($conf->get('resource.page_cache')); // save to disk + $history->deleteLink($link); // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo ''; exit; } @@ -1528,7 +1547,8 @@ function renderPage($conf, $pluginManager, $LINKSDB) $_POST, $_FILES, $LINKSDB, - $conf + $conf, + $history ); echo ''; @@ -1557,6 +1577,7 @@ function($a, $b) { return $a['order'] - $b['order']; } // Plugin administration form action if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) { + $history->updateSettings(); try { if (isset($_POST['parameters_form'])) { unset($_POST['parameters_form']); diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php new file mode 100644 index 00000000..79322249 --- /dev/null +++ b/tests/HistoryTest.php @@ -0,0 +1,195 @@ +assertFileExists(self::$historyFilePath); + } + + /** + * Not writable history file: raise an exception. + * + * @expectedException Exception + * @expectedExceptionMessage History file isn't readable or writable + */ + public function testConstructNotWritable() + { + touch(self::$historyFilePath); + chmod(self::$historyFilePath, 0440); + new History(self::$historyFilePath); + } + + /** + * Not parsable history file: raise an exception. + * + * @expectedException Exception + * @expectedExceptionMessageRegExp /Could not parse history file/ + */ + public function testConstructNotParsable() + { + file_put_contents(self::$historyFilePath, 'not parsable'); + // gzinflate generates a warning + @new History(self::$historyFilePath); + } + + /** + * Test add link event + */ + public function testAddLink() + { + $history = new History(self::$historyFilePath); + $history->addLink(['id' => 0]); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::CREATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(0, $actual['id']); + + $history = new History(self::$historyFilePath); + $history->addLink(['id' => 1]); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::CREATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + + $history = new History(self::$historyFilePath); + $history->addLink(['id' => 'str']); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::CREATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals('str', $actual['id']); + } + + /** + * Test updated link event + */ + public function testUpdateLink() + { + $history = new History(self::$historyFilePath); + $history->updateLink(['id' => 1]); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::UPDATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + } + + /** + * Test delete link event + */ + public function testDeleteLink() + { + $history = new History(self::$historyFilePath); + $history->deleteLink(['id' => 1]); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::DELETED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + } + + /** + * Test updated settings event + */ + public function testUpdateSettings() + { + $history = new History(self::$historyFilePath); + $history->updateSettings(); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::SETTINGS, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEmpty($actual['id']); + } + + /** + * Make sure that new items are stored at the beginning + */ + public function testHistoryOrder() + { + $history = new History(self::$historyFilePath); + $history->updateLink(['id' => 1]); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::UPDATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + + $history->addLink(['id' => 1]); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::CREATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + } + + /** + * Re-read history from file after writing an event + */ + public function testHistoryRead() + { + $history = new History(self::$historyFilePath); + $history->updateLink(['id' => 1]); + $history = new History(self::$historyFilePath); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::UPDATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + } + + /** + * Re-read history from file after writing an event and make sure that the order is correct + */ + public function testHistoryOrderRead() + { + $history = new History(self::$historyFilePath); + $history->updateLink(['id' => 1]); + $history->addLink(['id' => 1]); + + $history = new History(self::$historyFilePath); + $actual = $history->getHistory()[0]; + $this->assertEquals(History::CREATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + + $actual = $history->getHistory()[1]; + $this->assertEquals(History::UPDATED, $actual['event']); + $this->assertTrue(new DateTime('-2 seconds') < DateTime::createFromFormat(DateTime::ATOM, $actual['datetime'])); + $this->assertEquals(1, $actual['id']); + } + + /** + * Test retention time: delete old entries. + */ + public function testHistoryRententionTime() + { + $history = new History(self::$historyFilePath, 5); + $history->updateLink(['id' => 1]); + $this->assertEquals(1, count($history->getHistory())); + $arr = $history->getHistory(); + $arr[0]['datetime'] = (new DateTime('-1 hour'))->format(DateTime::ATOM); + FileUtils::writeFlatDB(self::$historyFilePath, $arr); + + $history = new History(self::$historyFilePath, 60); + $this->assertEquals(1, count($history->getHistory())); + $this->assertEquals(1, $history->getHistory()[0]['id']); + $history->updateLink(['id' => 2]); + $this->assertEquals(1, count($history->getHistory())); + $this->assertEquals(2, $history->getHistory()[0]['id']); + } +} 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 @@ -33,6 +33,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase */ protected static $testDatastore = 'sandbox/datastore.php'; + /** + * @var string History file path + */ + protected static $historyFilePath = 'sandbox/history.php'; + /** * @var LinkDB private LinkDB instance */ @@ -48,6 +53,11 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase */ protected $conf; + /** + * @var History instance. + */ + protected $history; + /** * @var string Save the current timezone. */ @@ -73,6 +83,15 @@ protected function setUp() $this->linkDb = new LinkDB(self::$testDatastore, true, false); $this->conf = new ConfigManager('tests/utils/config/configJson'); $this->conf->set('resource.page_cache', $this->pagecache); + $this->history = new History(self::$historyFilePath); + } + + /** + * Delete history file. + */ + public function tearDown() + { + @unlink(self::$historyFilePath); } public static function tearDownAfterClass() @@ -89,7 +108,7 @@ public function testImportEmptyData() $this->assertEquals( 'File empty.htm (0 bytes) has an unknown file format.' .' Nothing was imported.', - NetscapeBookmarkUtils::import(NULL, $files, NULL, $this->conf) + NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history) ); $this->assertEquals(0, count($this->linkDb)); } @@ -102,7 +121,7 @@ public function testImportNoDoctype() $files = file2array('no_doctype.htm'); $this->assertEquals( 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.', - NetscapeBookmarkUtils::import(NULL, $files, NULL, $this->conf) + NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history) ); $this->assertEquals(0, count($this->linkDb)); } @@ -116,7 +135,7 @@ public function testImportInternetExplorerEncoding() $this->assertEquals( 'File internet_explorer_encoding.htm (356 bytes) was successfully processed:' .' 1 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(1, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -145,7 +164,7 @@ public function testImportNested() $this->assertEquals( 'File netscape_nested.htm (1337 bytes) was successfully processed:' .' 8 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(8, count($this->linkDb)); $this->assertEquals(2, count_private($this->linkDb)); @@ -267,7 +286,7 @@ public function testImportDefaultPrivacyNoPost() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); @@ -312,7 +331,7 @@ public function testImportKeepPrivacy() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(1, count_private($this->linkDb)); @@ -356,7 +375,7 @@ public function testImportAsPublic() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -380,7 +399,7 @@ public function testImportAsPrivate() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(2, count_private($this->linkDb)); @@ -406,7 +425,7 @@ public function testOverwriteAsPublic() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(2, count_private($this->linkDb)); @@ -426,7 +445,7 @@ public function testOverwriteAsPublic() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 2 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -452,7 +471,7 @@ public function testOverwriteAsPrivate() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -473,7 +492,7 @@ public function testOverwriteAsPrivate() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 2 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(2, count_private($this->linkDb)); @@ -497,7 +516,7 @@ public function testSkipOverwrite() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -507,7 +526,7 @@ public function testSkipOverwrite() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 0 links imported, 0 links overwritten, 2 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -526,7 +545,7 @@ public function testSetDefaultTags() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -553,7 +572,7 @@ public function testSanitizeDefaultTags() $this->assertEquals( 'File netscape_basic.htm (482 bytes) was successfully processed:' .' 2 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(2, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -578,7 +597,7 @@ public function testImportSameDate() $this->assertEquals( 'File same_date.htm (453 bytes) was successfully processed:' .' 3 links imported, 0 links overwritten, 0 links skipped.', - NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf) + NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf, $this->history) ); $this->assertEquals(3, count($this->linkDb)); $this->assertEquals(0, count_private($this->linkDb)); @@ -595,4 +614,32 @@ public function testImportSameDate() $this->linkDb[2]['id'] ); } + + public function testImportCreateUpdateHistory() + { + $post = [ + 'privacy' => 'public', + 'overwrite' => 'true', + ]; + $files = file2array('netscape_basic.htm'); + $nbLinks = 2; + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history); + $history = $this->history->getHistory(); + $this->assertEquals($nbLinks, count($history)); + foreach ($history as $value) { + $this->assertEquals(History::CREATED, $value['event']); + $this->assertTrue(new DateTime('-5 seconds') < DateTime::createFromFormat(DateTime::ATOM, $value['datetime'])); + $this->assertTrue(is_int($value['id'])); + } + + // re-import as private, enable overwriting + NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history); + $history = $this->history->getHistory(); + $this->assertEquals($nbLinks * 2, count($history)); + for ($i = 0 ; $i < $nbLinks ; $i++) { + $this->assertEquals(History::UPDATED, $history[$i]['event']); + $this->assertTrue(new DateTime('-5 seconds') < DateTime::createFromFormat(DateTime::ATOM, $history[$i]['datetime'])); + $this->assertTrue(is_int($history[$i]['id'])); + } + } }