--- /dev/null
+<?php
+
+/**
+ * Utilities to import and export bookmarks using the Netscape format
+ */
+class NetscapeBookmarkUtils
+{
+
+ /**
+ * Filters links and adds Netscape-formatted fields
+ *
+ * Added fields:
+ * - timestamp link addition date, using the Unix epoch format
+ * - taglist comma-separated tag list
+ *
+ * @param LinkDB $linkDb The link datastore
+ * @param string $selection Which links to export: (all|private|public)
+ *
+ * @throws Exception Invalid export selection
+ *
+ * @return array The links to be exported, with additional fields
+ */
+ public static function filterAndFormat($linkDb, $selection)
+ {
+ // see tpl/export.html for possible values
+ if (! in_array($selection, array('all','public','private'))) {
+ throw new Exception('Invalid export selection: "'.$selection.'"');
+ }
+
+ $bookmarkLinks = array();
+
+ foreach ($linkDb as $link) {
+ if ($link['private'] != 0 && $selection == 'public') {
+ continue;
+ }
+ if ($link['private'] == 0 && $selection == 'private') {
+ continue;
+ }
+ $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
+ $link['timestamp'] = $date->getTimestamp();
+ $link['taglist'] = str_replace(' ', ',', $link['tags']);
+ $bookmarkLinks[] = $link;
+ }
+
+ return $bookmarkLinks;
+ }
+}
require_once 'application/LinkDB.php';
require_once 'application/LinkFilter.php';
require_once 'application/LinkUtils.php';
+require_once 'application/NetscapeBookmarkUtils.php';
require_once 'application/TimeZone.php';
require_once 'application/Url.php';
require_once 'application/Utils.php';
}
// -------- Export as Netscape Bookmarks HTML file.
- if ($targetPage == Router::$PAGE_EXPORT)
- {
- if (empty($_GET['what']))
- {
+ if ($targetPage == Router::$PAGE_EXPORT) {
+ if (empty($_GET['selection'])) {
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->renderPage('export');
exit;
}
- $exportWhat=$_GET['what'];
- if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export???');
- header('Content-Type: text/html; charset=utf-8');
- header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html');
- $currentdate=date('Y/m/d H:i:s');
- echo <<<HTML
-<!DOCTYPE NETSCAPE-Bookmark-file-1>
-<!-- This is an automatically generated file.
- It will be read and overwritten.
- DO NOT EDIT! -->
-<!-- Shaarli {$exportWhat} bookmarks export on {$currentdate} -->
-<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
-<TITLE>Bookmarks</TITLE>
-<H1>Bookmarks</H1>
-HTML;
- foreach($LINKSDB as $link)
- {
- if ($exportWhat=='all' ||
- ($exportWhat=='private' && $link['private']!=0) ||
- ($exportWhat=='public' && $link['private']==0))
- {
- $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
- echo '<DT><A HREF="'.$link['url'].'" ADD_DATE="'.$date->getTimestamp().'" PRIVATE="'.$link['private'].'"';
- if ($link['tags']!='') echo ' TAGS="'.str_replace(' ',',',$link['tags']).'"';
- echo '>'.$link['title']."</A>\n";
- if ($link['description']!='') echo '<DD>'.$link['description']."\n";
- }
+ // export as bookmarks_(all|private|public)_YYYYmmdd_HHMMSS.html
+ $selection = $_GET['selection'];
+ try {
+ $PAGE->assign(
+ 'links',
+ NetscapeBookmarkUtils::filterAndFormat($LINKSDB, $selection)
+ );
+ } catch (Exception $exc) {
+ header('Content-Type: text/plain; charset=utf-8');
+ echo $exc->getMessage();
+ exit;
}
- exit;
+ $now = new DateTime();
+ header('Content-Type: text/html; charset=utf-8');
+ header(
+ 'Content-disposition: attachment; filename=bookmarks_'
+ .$selection.'_'.$now->format(LinkDB::LINK_DATE_FORMAT).'.html'
+ );
+ $PAGE->assign('date', $now->format(DateTime::RFC822));
+ $PAGE->assign('eol', PHP_EOL);
+ $PAGE->assign('selection', $selection);
+ $PAGE->renderPage('export.bookmarks');
+ exit;
}
// -------- User is uploading a file for import
--- /dev/null
+<?php
+
+require_once 'application/NetscapeBookmarkUtils.php';
+
+/**
+ * Netscape bookmark import and export
+ */
+class NetscapeBookmarkUtilsTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @var string datastore to test write operations
+ */
+ protected static $testDatastore = 'sandbox/datastore.php';
+
+ /**
+ * @var ReferenceLinkDB instance.
+ */
+ protected static $refDb = null;
+
+ /**
+ * @var LinkDB private LinkDB instance.
+ */
+ protected static $linkDb = null;
+
+ /**
+ * Instantiate reference data
+ */
+ public static function setUpBeforeClass()
+ {
+ self::$refDb = new ReferenceLinkDB();
+ self::$refDb->write(self::$testDatastore);
+ self::$linkDb = new LinkDB(self::$testDatastore, true, false);
+ }
+
+ /**
+ * Attempt to export an invalid link selection
+ * @expectedException Exception
+ * @expectedExceptionMessageRegExp /Invalid export selection/
+ */
+ public function testFilterAndFormatInvalid()
+ {
+ NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'derp');
+ }
+
+ /**
+ * Prepare all links for export
+ */
+ public function testFilterAndFormatAll()
+ {
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all');
+ $this->assertEquals(self::$refDb->countLinks(), sizeof($links));
+ foreach ($links as $link) {
+ $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
+ $this->assertEquals(
+ $date->getTimestamp(),
+ $link['timestamp']
+ );
+ $this->assertEquals(
+ str_replace(' ', ',', $link['tags']),
+ $link['taglist']
+ );
+ }
+ }
+
+ /**
+ * Prepare private links for export
+ */
+ public function testFilterAndFormatPrivate()
+ {
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private');
+ $this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links));
+ foreach ($links as $link) {
+ $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
+ $this->assertEquals(
+ $date->getTimestamp(),
+ $link['timestamp']
+ );
+ $this->assertEquals(
+ str_replace(' ', ',', $link['tags']),
+ $link['taglist']
+ );
+ }
+ }
+
+ /**
+ * Prepare public links for export
+ */
+ public function testFilterAndFormatPublic()
+ {
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public');
+ $this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links));
+ foreach ($links as $link) {
+ $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
+ $this->assertEquals(
+ $date->getTimestamp(),
+ $link['timestamp']
+ );
+ $this->assertEquals(
+ str_replace(' ', ',', $link['tags']),
+ $link['taglist']
+ );
+ }
+ }
+}
--- /dev/null
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<!-- This is an automatically generated file.
+ It will be read and overwritten.
+ Do Not Edit! -->{ignore}The RainTPL loop is formatted to avoid generating extra newlines{/ignore}
+<TITLE>{$pagetitle}</TITLE>
+<H1>Shaarli export of {$selection} bookmarks on {$date}</H1>
+<DL><p>{loop="links"}
+<DT><A HREF="{$value.url}" ADD_DATE="{$value.timestamp}" PRIVATE="{$value.private}" TAGS="{$value.taglist}">{$value.title}</A>{if="$value.description"}{$eol}<DD>{$value.description}{/if}{/loop}
+</DL><p>
<html>
<head>{include="includes"}</head>
<body>
-<div id="pageheader">
- {include="page.header"}
- <div id="toolsdiv">
- <a href="?do=export&what=all"><b>Export all</b> <span>: Export all links</span></a><br><br>
- <a href="?do=export&what=public"><b>Export public</b> <span>: Export public links only</span></a><br><br>
- <a href="?do=export&what=private"><b>Export private</b> <span>: Export private links only</span></a>
- <div class="clear"></div>
- </div>
-</div>
-{include="page.footer"}
+ <div id="pageheader">
+ {include="page.header"}
+ <div id="toolsdiv">
+ <a href="?do=export&selection=all">
+ <b>Export all</b><span>: Export all links</span>
+ </a><br>
+ <a href="?do=export&selection=public">
+ <b>Export public</b><span>: Only export public links</span>
+ </a><br>
+ <a href="?do=export&selection=private">
+ <b>Export private</b><span>: Only export private links</span>
+ </a>
+ <div class="clear"></div>
+ </div>
+ </div>
+ {include="page.footer"}
</body>
</html>