From 7699c685e6872e8bff4a4c137000432e43176870 Mon Sep 17 00:00:00 2001 From: yohan <783b8c87@scimetis.net> Date: Sun, 13 Sep 2015 03:33:39 +0200 Subject: [PATCH] Better import for XML bookmark file. Support for adding multiple bookmarks at once with my modified Firefox's Scuttle plugin. --- data/templates/default/editbookmark.tpl.php | 51 +++-- www/bookmarks.php | 139 ++++++++------ www/edit.php | 28 ++- www/importNetscape.php | 200 +++++++++++++++----- 4 files changed, 293 insertions(+), 125 deletions(-) diff --git a/data/templates/default/editbookmark.tpl.php b/data/templates/default/editbookmark.tpl.php index 8b71230..c400de3 100644 --- a/data/templates/default/editbookmark.tpl.php +++ b/data/templates/default/editbookmark.tpl.php @@ -1,6 +1,5 @@ includeTemplate($GLOBALS['top_include']); - $accessPublic = ''; $accessShared = ''; $accessPrivate = ''; @@ -33,6 +32,14 @@ if (is_array($row['tags'])) { $row['tags'] = implode(', ', $row['tags']); } +if (! is_array($row['bAddress'])) { + $row['bAddress'] = array($row['bAddress']); +} + +if (! is_array($row['bTitle'])) { + $row['bTitle'] = array($row['bTitle']); +} + $ajaxUrl = ROOT . 'ajax/' . ( ($GLOBALS['adminsAreAdvisedTagsFromOtherAdmins'] && $currentUser->isAdmin()) @@ -40,18 +47,8 @@ $ajaxUrl = ROOT . 'ajax/' : 'getcontacttags' ) . '.php'; ?> -
+ - - - - - - - - - - -
@@ -126,14 +123,40 @@ $ajaxUrl = ROOT . 'ajax/' - +
+ $address) { +?> + + + + + + + + + ← + + + + + ← + + + + + + + +
diff --git a/www/bookmarks.php b/www/bookmarks.php index fe12310..84ad021 100644 --- a/www/bookmarks.php +++ b/www/bookmarks.php @@ -29,15 +29,21 @@ $cacheservice =SemanticScuttle_Service_Factory::get('Cache'); isset($_GET['action']) ? define('GET_ACTION', $_GET['action']): define('GET_ACTION', ''); isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', ''); -isset($_GET['title']) ? define('GET_TITLE', $_GET['title']): define('GET_TITLE', ''); -isset($_GET['address']) ? define('GET_ADDRESS', $_GET['address']): define('GET_ADDRESS', ''); +// define does not support arrays before PHP version 7 +isset($_GET['title']) ? $TITLE = $_GET['title']: $TITLE = array(); +//isset($_GET['title']) ? define('GET_TITLE', $_GET['title']): define('GET_TITLE', ''); +isset($_GET['address']) ? $ADDRESS = $_GET['address']: $ADDRESS = array(); +//isset($_GET['address']) ? define('GET_ADDRESS', $_GET['address']): define('GET_ADDRESS', ''); isset($_GET['description']) ? define('GET_DESCRIPTION', $_GET['description']): define('GET_DESCRIPTION', ''); isset($_GET['privateNote']) ? define('GET_PRIVATENOTE', $_GET['privateNote']): define('GET_PRIVATENOTE', ''); isset($_GET['tags']) ? define('GET_TAGS', $_GET['tags']): define('GET_TAGS', ''); isset($_GET['copyOf']) ? define('GET_COPYOF', $_GET['copyOf']): define('GET_COPYOF', ''); -isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', ''); -isset($_POST['address']) ? define('POST_ADDRESS', $_POST['address']): define('POST_ADDRESS', ''); +// define does not support arrays before PHP version 7 +isset($_POST['title']) ? $TITLE = $_POST['title']: $TITLE = array(); +//isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', ''); +isset($_POST['address']) ? $ADDRESS = $_POST['address']: $ADDRESS = array(); +//isset($_POST['address']) ? define('POST_ADDRESS', $_POST['address']): define('POST_ADDRESS', ''); isset($_POST['description']) ? define('POST_DESCRIPTION', $_POST['description']): define('POST_DESCRIPTION', ''); isset($_POST['privateNote']) ? define('POST_PRIVATENOTE', $_POST['privateNote']): define('POST_PRIVATENOTE', ''); isset($_POST['status']) ? define('POST_STATUS', $_POST['status']): define('POST_STATUS', ''); @@ -53,7 +59,13 @@ if (!isset($_POST['tags'])) { $_POST['tags'] = array(); } //echo '

' . var_export($_POST, true) . '

';die(); +if (! is_array($ADDRESS)) { + $ADDRESS = array($ADDRESS); +} +if (! is_array($TITLE)) { + $TITLE = array($TITLE); +} if ((GET_ACTION == "add") && !$userservice->isLoggedOn()) { $loginqry = str_replace("'", '%27', stripslashes($_SERVER['QUERY_STRING'])); @@ -130,53 +142,72 @@ $tplVars['loadjs'] = true; $saved = false; $templatename = 'bookmarks.tpl'; if ($userservice->isLoggedOn() && POST_SUBMITTED != '') { - if (!POST_TITLE || !POST_ADDRESS) { + if (!$TITLE || !$ADDRESS) { $tplVars['error'] = T_('Your bookmark must have a title and an address'); $templatename = 'editbookmark.tpl'; - } else { - $address = trim(POST_ADDRESS); - if (!SemanticScuttle_Model_Bookmark::isValidUrl($address)) { - $tplVars['error'] = T_('This bookmark URL may not be added'); - $templatename = 'editbookmark.tpl'; - } else if ($bookmarkservice->bookmarkExists($address, $currentUserID)) { - // If the bookmark exists already, edit the original - $bookmark = $bookmarkservice->getBookmarkByAddress($address); - header('Location: '. createURL('edit', $bookmark['bId'])); - exit(); - // If it's new, save it - } else { - $title = trim(POST_TITLE); - $description = trim(POST_DESCRIPTION); - $privateNote = trim(POST_PRIVATENOTE); - $status = intval(POST_STATUS); - $categories = explode(',', $_POST['tags']); - $saved = true; - if ($bookmarkservice->addBookmark($address, $title, $description, $privateNote, $status, $categories)) { - if (POST_POPUP != '') { - $tplVars['msg'] = ''; - } else { - $tplVars['msg'] = T_('Bookmark saved') . ' '.T_('(Come back to previous page.)').''; - // Redirection option - if ($GLOBALS['useredir']) { - $address = $GLOBALS['url_redir'] . $address; - } - } - } else { - $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.'); - $templatename = 'editbookmark.tpl'; - $saved = false; - } - } + } + else { + $address = array_map('trim', $ADDRESS); + $valid = 1; + foreach($address as $value) { + if (!SemanticScuttle_Model_Bookmark::isValidUrl($value)) { + $tplVars['error'] = T_('This bookmark URL may not be added' + $value); + $templatename = 'editbookmark.tpl'; + $valid = 0; + break; + } + } + if ($valid) { + $title = array_map('trim', $TITLE); + $description = trim(POST_DESCRIPTION); + $privateNote = trim(POST_PRIVATENOTE); + $status = intval(POST_STATUS); + $categories = explode(',', $_POST['tags']); + $saved = true; + foreach($address as $index => $value) { + if ($bookmarkservice->bookmarkExists($value, $currentUserID)) { + // If the bookmark exists already, edit the original + $bookmark = $bookmarkservice->getBookmarkByAddress($value); + $bId = intval($bookmark['bId']); + $row = $bookmarkservice->getBookmark($bId, true); + $categories = array_unique(array_merge($row['tags'], $categories)); + if (!$bookmarkservice->updateBookmark($bId, $value, $title[$index], $description, $privateNote, $status, $categories)) { + $tplvars['error'] = T_('Error while saving this bookmark : ' + $value); + $templatename = 'editbookmark.tpl'; + $saved = false; + break; + } + } + // If it's new, save it + elseif (!$bookmarkservice->addBookmark($value, $title[$index], $description, $privateNote, $status, $categories)) { + $tplVars['error'] = T_('There was an error saving this bookmark : ' + $value + ' Please try again or contact the administrator.'); + $templatename = 'editbookmark.tpl'; + $saved = false; + break; + } + } + if ($saved) { + if (POST_POPUP != '') { + $tplVars['msg'] = ''; + } + else { + $tplVars['msg'] = T_('Bookmark saved') . ' '.T_('(Come back to previous page.)').''; + } + + } + } } } if (GET_ACTION == "add") { // If the bookmark exists already, edit the original - if ($bookmarkservice->bookmarkExists(stripslashes(GET_ADDRESS), $currentUserID)) { - $bookmark =& $bookmarkservice->getBookmarks(0, NULL, $currentUserID, NULL, NULL, NULL, NULL, NULL, NULL, $bookmarkservice->getHash(stripslashes(GET_ADDRESS))); - $popup = (GET_POPUP!='') ? '?popup=1' : ''; - header('Location: '. createURL('edit', $bookmark['bookmarks'][0]['bId'] . $popup)); - exit(); + if (count($ADDRESS) === 1) { + if ($bookmarkservice->bookmarkExists(stripslashes($ADDRESS[0]), $currentUserID)) { + $bookmark =& $bookmarkservice->getBookmarks(0, NULL, $currentUserID, NULL, NULL, NULL, NULL, NULL, NULL, $bookmarkservice->getHash(stripslashes($ADDRESS[0]))); + $popup = (GET_POPUP!='') ? '?popup=1' : ''; + header('Location: '. createURL('edit', $bookmark['bookmarks'][0]['bId'] . $popup)); + exit(); + } } $templatename = 'editbookmark.tpl'; } @@ -186,10 +217,10 @@ if ($templatename == 'editbookmark.tpl') { $tplVars['formaction'] = createURL('bookmarks', $currentUsername); if (POST_SUBMITTED != '') { $tplVars['row'] = array( - 'bTitle' => stripslashes(POST_TITLE), - 'bAddress' => stripslashes(POST_ADDRESS), + 'bTitle' => array_map('stripslashes', $TITLE), + 'bAddress' => array_map('stripslashes', $ADDRESS), 'bDescription' => stripslashes(POST_DESCRIPTION), - 'bPrivateNote' => stripslashes(POST_PRIVATENOTE), + 'bPrivateNote' => stripslashes(POST_PRIVATENOTE), 'tags' => ($_POST['tags'] ? $_POST['tags'] : array()), 'bStatus' => $GLOBALS['defaults']['privacy'], ); @@ -202,12 +233,14 @@ if ($templatename == 'editbookmark.tpl') { } }else { //copy from pop-up bookmarklet $tplVars['row'] = array( - 'bTitle' => stripslashes(GET_TITLE), - 'bAddress' => stripslashes(GET_ADDRESS), - 'bDescription' => stripslashes(GET_DESCRIPTION), - 'bPrivateNote' => stripslashes(GET_PRIVATENOTE), - 'tags' => (GET_TAGS ? explode(',', stripslashes(GET_TAGS)) : array()), - 'bStatus' => $GLOBALS['defaults']['privacy'] + 'bTitle' => array_map('stripslashes', $TITLE), + //'bTitle' => stripslashes(GET_TITLE), + 'bAddress' => array_map('stripslashes', $ADDRESS), + //'bAddress' => stripslashes(GET_ADDRESS), + 'bDescription' => stripslashes(GET_DESCRIPTION), + 'bPrivateNote' => stripslashes(GET_PRIVATENOTE), + 'tags' => (GET_TAGS ? explode(',', stripslashes(GET_TAGS)) : array()), + 'bStatus' => $GLOBALS['defaults']['privacy'] ); } diff --git a/www/edit.php b/www/edit.php index cbfa30b..0f9e7a3 100644 --- a/www/edit.php +++ b/www/edit.php @@ -29,8 +29,12 @@ $bookmarkservice = SemanticScuttle_Service_Factory :: get('Bookmark'); isset($_POST['submitted']) ? define('POST_SUBMITTED', $_POST['submitted']): define('POST_SUBMITTED', ''); isset($_POST['delete']) ? define('POST_DELETE', $_POST['delete']): define('POST_DELETE', ''); -isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', ''); -isset($_POST['address']) ? define('POST_ADDRESS', $_POST['address']): define('POST_ADDRESS', ''); +// define does not support arrays before PHP version 7 +isset($_POST['title']) ? $TITLE = $_POST['title']: $TITLE = array(); +//isset($_POST['title']) ? define('POST_TITLE', $_POST['title']): define('POST_TITLE', ''); +isset($_POST['address']) ? $ADDRESS = $_POST['address']: $ADDRESS = array(); +//isset($_POST['address']) ? define('POST_ADDRESS', $_POST['address']): define('POST_ADDRESS', ''); + isset($_POST['description']) ? define('POST_DESCRIPTION', $_POST['description']): define('POST_DESCRIPTION', ''); isset($_POST['privateNote']) ? define('POST_PRIVATENOTE', $_POST['privateNote']): define('POST_PRIVATENOTE', ''); isset($_POST['status']) ? define('POST_STATUS', $_POST['status']): define('POST_STATUS', $GLOBALS['defaults']['privacy']); @@ -40,6 +44,14 @@ isset($_GET['popup']) ? define('GET_POPUP', $_GET['popup']): define('GET_POPUP', isset($_POST['popup']) ? define('POST_POPUP', $_POST['popup']): define('POST_POPUP', ''); isset($_POST['referrer']) ? define('POST_REFERRER', $_POST['referrer']): define('POST_REFERRER', ''); +if (! is_array($ADDRESS)) { + $ADDRESS = array($ADDRESS); +} + +if (! is_array($TITLE)) { + $TITLE = array($TITLE); +} + // Header variables $tplVars['pagetitle'] = T_('Edit Bookmark'); $tplVars['subtitle'] = T_('Edit Bookmark'); @@ -52,28 +64,24 @@ if (!($row = $bookmarkservice->getBookmark(intval($bookmark), true))) { $templateservice->loadTemplate('error.404.tpl', $tplVars); exit(); } else { - if (!$bookmarkservice->editAllowed($row)) { $tplVars['error'] = T_('You are not allowed to edit this bookmark'); $templateservice->loadTemplate('error.500.tpl', $tplVars); exit(); } else if (POST_SUBMITTED != '') { - - - - if (!POST_TITLE || !POST_ADDRESS) { + if (!$TITLE || !$ADDRESS) { $tplVars['error'] = T_('Your bookmark must have a title and an address'); } else { // Update bookmark $bId = intval($bookmark); - $address = trim(POST_ADDRESS); - $title = trim(POST_TITLE); + $address = array_map('trim', $ADDRESS); + $title = array_map('trim', $TITLE); $description = trim(POST_DESCRIPTION); $privateNote = trim(POST_PRIVATENOTE); $status = intval(POST_STATUS); $tags = trim(POST_TAGS); - if (!$bookmarkservice->updateBookmark($bId, $address, $title, $description, $privateNote, $status, $tags)) { + if (!$bookmarkservice->updateBookmark($bId, $address[0], $title[0], $description, $privateNote, $status, $tags)) { $tplvars['error'] = T_('Error while saving your bookmark'); } else { if (POST_POPUP != '') { diff --git a/www/importNetscape.php b/www/importNetscape.php index b476c40..89d75b7 100644 --- a/www/importNetscape.php +++ b/www/importNetscape.php @@ -44,68 +44,45 @@ if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['si // File handle $html = file_get_contents($_FILES['userfile']['tmp_name']); + // Create array + $matches = parse_netscape_bookmarks($html); + //var_dump($matches); - // Create link array - //preg_match_all('/([^<]*)/si', $html, $matches); - preg_match_all('/([^<]*?)<\/a>.*?(
([^<]*)|
)/si', $html, $matches); - - //var_dump($matches);die(); - - - $links = $matches[1]; - $titles = $matches[2]; - $descriptions = $matches[4]; - - $size = count($links); + $size = count($matches); for ($i = 0; $i < $size; $i++) { - - // echo "
"; - // echo $links[$i]."
"; - - preg_match_all('/(\w*\s*=\s*"[^"]*")/', $links[$i], $attributes); - //$attributes = $attributes[0]; // we keep just one row - - $bDatetime = ""; //bDateTime optional - $bCategories = ""; //bCategories optional - - foreach ($attributes[0] as $attribute) { - $att = preg_split('/\s*=\s*/s', $attribute, 2); - $attrTitle = $att[0]; - - $attrVal = eregi_replace('"', '"', preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1])); - - switch ($attrTitle) { - case "HREF": - $bAddress = $attrVal; - break; - case "ADD_DATE": - $bDatetime = gmdate('Y-m-d H:i:s', $attrVal); - break; - case "TAGS": - $bCategories = $attrVal; - break; - case "NOTE": - $bPrivateNote = $attrVal; - } - } - $bTitle = trim($titles[$i]); - $bDescription = trim($descriptions[$i]); + $bDatetime = gmdate('Y-m-d H:i:s', $matches[$i]['time']); //bDateTime optional + $bCategories = $matches[$i]['tags']; //bCategories optional + $bAddress = $matches[$i]['uri']; + $bDescription = $matches[$i]['note']; + $bTitle = $matches[$i]['title']; + $bPrivateNote = ''; if ($bookmarkservice->bookmarkExists($bAddress, $userservice->getCurrentUserId())) { - $tplVars['error'] = T_('You have already submitted some of these bookmarks.'); + //$tplVars['error'] = T_('You have already submitted some of these bookmarks.'); + // If the bookmark exists already, edit the original + $bookmark = $bookmarkservice->getBookmarkByAddress($bAddress); + $bId = intval($bookmark['bId']); + $row = $bookmarkservice->getBookmark($bId, true); + $categories = array_unique(array_merge($row['tags'], $bCategories)); + //var_dump('update', $bId, $bAddress, $row['bTitle'], $row['bDescription'], $row['bPrivateNote'], $row['bStatus'], $categories); + if ($bookmarkservice->updateBookmark($bId, $bAddress, $row['bTitle'], $row['bDescription'], $row['bPrivateNote'], $row['bStatus'], $categories)) { + $countImportedBookmarks++; + } + else { + $tplvars['error'] = T_('Error while saving this bookmark : ' + $bAddress); + } } else { // If bookmark is local (like javascript: or place: in Firefox3), do nothing if(substr($bAddress, 0, 7) == "http://" || substr($bAddress, 0, 8) == "https://") { - // If bookmark claims to be from the future, set it to be now instead if (strtotime($bDatetime) > time()) { $bDatetime = gmdate('Y-m-d H:i:s'); } - + //var_dump('add ', $bAddress, $bTitle, $bDescription, $bPrivateNote, $status, $bCategories, $bDatetime); if ($bookmarkservice->addBookmark($bAddress, $bTitle, $bDescription, $bPrivateNote, $status, $bCategories, null, $bDatetime, false, true)) { $countImportedBookmarks++; } else { - $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.'); + $tplVars['error'] = T_('There was an error saving your bookmark : ' . $bAddress . ' Please try again or contact the administrator.'); } } } @@ -123,4 +100,131 @@ if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['si $tplVars['formaction'] = createURL('importNetscape'); $templateservice->loadTemplate($templatename, $tplVars); } + + +/** + * Basically netscape bookmark files often come so badly formed, there's + * no reliable way I could find to parse them with DOM or SimpleXML, + * even after running HTML Tidy on them. So, this function does a bunch of + * transformations on the general format of a netscape bookmark file, to get + * Each bookmark and its description onto one line, and goes through line by + * line, matching tags and attributes. It's messy, but it works better than + * anything I could find in hours of googling, and anything that I could + * write after hours with DOM and SimpleXML. I didn't want to pull in a big + * DOM parsing library just to do this one thing, so this is it. + * @todo - running Tidy before doing this might be beneficial. + * ?? $bkmk_str = tidy_parse_string($bkmk_str)->cleanRepair(); + * + * Update 2013-07-08: + * Just tested this on an export of some bookmarks from Pinboard.in + * and it seems that it is still working, so good for me. + */ + +/* +print '
';
+var_dump(parse_netscape_bookmarks(file_get_contents('bookmarks_export.htm')));
+*/
+
+function parse_netscape_bookmarks($bkmk_str, $default_tag = null) {
+    $i = 0;
+    $next = false;
+    $items = [];
+
+    $current_tag = $default_tag = $default_tag ?: 'autoimported-'.date("Ymd");
+
+    $bkmk_str = str_replace(["\r","\n","\t"], ['','',' '], $bkmk_str);
+
+    $bkmk_str = preg_replace_callback('@
(.*?)('.str_replace(["\r", "\n"], ['', '
'], trim($m[1])).'(\s*?)\n<", $bkmk_str); + $bkmk_str = preg_replace('/(/', "\n", $bkmk_str); + $lines = explode("\n", $bkmk_str); + + $str_bool = function($str, $default = false) { + if (!$str) { + return false; + } elseif (!is_string($str) && $str) { + return true; + } + + $true = 'y|yes|on|checked|ok|1|true|array|\+|okay|yes+|t|one'; + $false = 'n|no|off|empty|null|false|0|-|exit|die|neg|f|zero|void'; + + if (preg_match("/^($true)$/i", $str)) { + return true; + } elseif (preg_match("/^($false)$/i", $str)) { + return false; + } + + return $default; + }; + $tags = array($default_tag); + foreach ($lines as $line_no => $line) { + /* If we match a tag, set current tag to that, if
, stop tag. */ + if (preg_match('/^(.*?)<\/h\d>/i', $line, $m1)) { + $current_tag = trim(preg_replace("/\s+/", "_", strtr($m1[2], ', /+', '____'))); + $tags[] = $current_tag; + continue; + } elseif (preg_match('/^<\/DL>/i', $line)) { + $current_tag = $default_tag; + array_pop($tags); + } + + if (preg_match('/(.*?)<\/a>/i', $line, $m4)) { + $items[$i]['title'] = $m4[2]; + // $items[$i]['slug'] = slugify($m4[2]); + } else { + $items[$i]['title'] = 'untitled'; + // $items[$i]['slug'] = ''; + } + + if (preg_match('/note="(.*?)"<\/a>/i', $line, $m5)) { + $items[$i]['note'] = $m5[1]; + } elseif (preg_match('/
(.*?)<\//i', $line, $m6)) { + $items[$i]['note'] = str_replace('
', "\n", $m6[1]); + } else { + $items[$i]['note'] = ''; + } + + if (preg_match('/(tags?|labels?|folders?)="(.*?)"/i', $line, $m7)) { + array_unique(array_merge($items[$i]['tags'], explode(' ', trim(preg_replace("/\s+/", " ", strtr($m7[2], ',', ' ')))))); + } + if (preg_match('/add_date="(.*?)"/i', $line, $m8)) { + $items[$i]['time'] = $m8[1]; + } else { + $items[$i]['time'] = time(); + } + + if (preg_match('/(public|published|pub)="(.*?)"/i', $line, $m9)) { + $items[$i]['pub'] = $str_bool($m9[2], false) ? 1 : 0; + } elseif (preg_match('/(private|shared)="(.*?)"/i', $line, $m10)) { + $items[$i]['pub'] = $str_bool($m10[2], true) ? 0 : 1; + } + + $i++; + } + } + ksort($items); + + return $items; +} + ?>