From 6c2637d973a8887bdccf4cdbd15e30643605c576 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 24 Dec 2012 15:03:19 +0400 Subject: move data import/export to a separate plugin --- plugins/import_export/README.txt | 1 + plugins/import_export/import_export.js | 123 +++++++++ plugins/import_export/import_export.php | 433 ++++++++++++++++++++++++++++++++ 3 files changed, 557 insertions(+) create mode 100644 plugins/import_export/README.txt create mode 100644 plugins/import_export/import_export.js create mode 100644 plugins/import_export/import_export.php (limited to 'plugins/import_export') diff --git a/plugins/import_export/README.txt b/plugins/import_export/README.txt new file mode 100644 index 000000000..2808db231 --- /dev/null +++ b/plugins/import_export/README.txt @@ -0,0 +1 @@ +Performs import and export of tt-rss user data using neutral XML format. diff --git a/plugins/import_export/import_export.js b/plugins/import_export/import_export.js new file mode 100644 index 000000000..b3b760f15 --- /dev/null +++ b/plugins/import_export/import_export.js @@ -0,0 +1,123 @@ +function exportData() { + try { + + var query = "backend.php?op=pluginhandler&plugin=import_export&method=exportData"; + + if (dijit.byId("dataExportDlg")) + dijit.byId("dataExportDlg").destroyRecursive(); + + var exported = 0; + + dialog = new dijit.Dialog({ + id: "dataExportDlg", + title: __("Export Data"), + style: "width: 600px", + prepare: function() { + + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: "?op=pluginhandler&plugin=import_export&method=exportrun&offset=" + exported, + onComplete: function(transport) { + try { + var rv = JSON.parse(transport.responseText); + + if (rv && rv.exported != undefined) { + if (rv.exported > 0) { + + exported += rv.exported; + + $("export_status_message").innerHTML = + " " + + "Exported %d articles, please wait...".replace("%d", + exported); + + setTimeout('dijit.byId("dataExportDlg").prepare()', 2000); + + } else { + + $("export_status_message").innerHTML = + __("Finished, exported %d articles. You can download the data here.") + .replace("%d", exported) + .replace("%u", "backend.php?op=pluginhandler&plugin=import_export&subop=exportget"); + + exported = 0; + + } + + } else { + $("export_status_message").innerHTML = + "Error occured, could not export data."; + } + } catch (e) { + exception_error("exportData", e, transport.responseText); + } + + notify(''); + + } }); + + }, + execute: function() { + if (this.validate()) { + + + + } + }, + href: query}); + + dialog.show(); + + + } catch (e) { + exception_error("exportData", e); + } +} + +function dataImportComplete(iframe) { + try { + if (!iframe.contentDocument.body.innerHTML) return false; + + Element.hide(iframe); + + notify(''); + + if (dijit.byId('dataImportDlg')) + dijit.byId('dataImportDlg').destroyRecursive(); + + var content = iframe.contentDocument.body.innerHTML; + + dialog = new dijit.Dialog({ + id: "dataImportDlg", + title: __("Data Import"), + style: "width: 600px", + onCancel: function() { + + }, + content: content}); + + dialog.show(); + + } catch (e) { + exception_error("dataImportComplete", e); + } +} + +function importData() { + + var file = $("export_file"); + + if (file.value.length == 0) { + alert(__("Please choose the file first.")); + return false; + } else { + notify_progress("Importing, please wait...", true); + + Element.show("data_upload_iframe"); + + return true; + } +} + + diff --git a/plugins/import_export/import_export.php b/plugins/import_export/import_export.php new file mode 100644 index 000000000..7a843c45b --- /dev/null +++ b/plugins/import_export/import_export.php @@ -0,0 +1,433 @@ +link = $host->get_link(); + $this->host = $host; + + $host->add_hook($host::HOOK_PREFS_TAB, $this); + $host->add_command("xml-import", "USER FILE: import articles from XML", $this); + } + + function xml_import($args) { + array_shift($args); + + $username = $args[count($args) - 2]; + $filename = $args[count($args) - 1]; + + if (!$username) { + print "error: please specify username.\n"; + return; + } + + if (!is_file($filename)) { + print "error: input filename ($filename) doesn't exist.\n"; + return; + } + + _debug("importing $filename for user $username...\n"); + + $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE login = '$username'"); + + if (db_num_rows($result) == 0) { + print "error: could not find user $username.\n"; + return; + } + + $owner_uid = db_fetch_result($result, 0, "id"); + + $this->perform_data_import($this->link, $filename, $owner_uid); + } + + function save() { + $example_value = db_escape_string($_POST["example_value"]); + + echo "Value set to $example_value (not really)"; + } + + function get_prefs_js() { + return file_get_contents(dirname(__FILE__) . "/import_export.js"); + } + + function hook_prefs_tab($args) { + if ($args != "prefFeeds") return; + + print "
"; + + print "

" . __("Article archive") . "

"; + + print "

" . __("You can export and import your Starred and Archived articles for safekeeping or when migrating between tt-rss instances.") . "

"; + + print " "; + + print "
"; + + print ""; + + print "
+   + + + + "; + + + print "
"; # pane + } + + function csrf_ignore($method) { + return in_array($method, array("exportget")); + } + + function before($method) { + return $_SESSION["uid"] != false; + } + + function after() { + return true; + } + + function exportget() { + $exportname = CACHE_DIR . "/export/" . + sha1($_SESSION['uid'] . $_SESSION['login']) . ".xml"; + + if (file_exists($exportname)) { + header("Content-type: text/xml"); + + if (function_exists('gzencode')) { + header("Content-Disposition: attachment; filename=TinyTinyRSS_exported.xml.gz"); + echo gzencode(file_get_contents($exportname)); + } else { + header("Content-Disposition: attachment; filename=TinyTinyRSS_exported.xml"); + echo file_get_contents($exportname); + } + } else { + echo "File not found."; + } + } + + function exportrun() { + $offset = (int) db_escape_string($_REQUEST['offset']); + $exported = 0; + $limit = 250; + + if ($offset < 10000 && is_writable(CACHE_DIR . "/export")) { + $result = db_query($this->link, "SELECT + ttrss_entries.guid, + ttrss_entries.title, + content, + marked, + published, + score, + note, + link, + tag_cache, + label_cache, + ttrss_feeds.title AS feed_title, + ttrss_feeds.feed_url AS feed_url, + ttrss_entries.updated + FROM + ttrss_user_entries LEFT JOIN ttrss_feeds ON (ttrss_feeds.id = feed_id), + ttrss_entries + WHERE + (marked = true OR feed_id IS NULL) AND + ref_id = ttrss_entries.id AND + ttrss_user_entries.owner_uid = " . $_SESSION['uid'] . " + ORDER BY ttrss_entries.id LIMIT $limit OFFSET $offset"); + + $exportname = sha1($_SESSION['uid'] . $_SESSION['login']); + + if ($offset == 0) { + $fp = fopen(CACHE_DIR . "/export/$exportname.xml", "w"); + fputs($fp, ""); + } else { + $fp = fopen(CACHE_DIR . "/export/$exportname.xml", "a"); + } + + if ($fp) { + + while ($line = db_fetch_assoc($result)) { + fputs($fp, "
"); + + foreach ($line as $k => $v) { + fputs($fp, "<$k>"); + } + + fputs($fp, "
"); + } + + $exported = db_num_rows($result); + + if ($exported < $limit && $exported > 0) { + fputs($fp, "
"); + } + + fclose($fp); + } + + } + + print json_encode(array("exported" => $exported)); + } + + function perform_data_import($link, $filename, $owner_uid) { + + $num_imported = 0; + $num_processed = 0; + $num_feeds_created = 0; + + $doc = @DOMDocument::load($filename); + + if (!$doc) { + $contents = file_get_contents($filename); + + if ($contents) { + $data = @gzuncompress($contents); + } + + if (!$data) { + $data = @gzdecode($contents); + } + + if ($data) + $doc = DOMDocument::loadXML($data); + } + + if ($doc) { + + $xpath = new DOMXpath($doc); + + $container = $doc->firstChild; + + if ($container && $container->hasAttribute('schema-version')) { + $schema_version = $container->getAttribute('schema-version'); + + if ($schema_version != SCHEMA_VERSION) { + print "

" .__("Could not import: incorrect schema version.") . "

"; + return; + } + + } else { + print "

" . __("Could not import: unrecognized document format.") . "

"; + return; + } + + $articles = $xpath->query("//article"); + + foreach ($articles as $article_node) { + if ($article_node->childNodes) { + + $ref_id = 0; + + $article = array(); + + foreach ($article_node->childNodes as $child) { + if ($child->nodeName != 'label_cache') + $article[$child->nodeName] = db_escape_string($child->nodeValue); + else + $article[$child->nodeName] = $child->nodeValue; + } + + //print_r($article); + + if ($article['guid']) { + + ++$num_processed; + + //db_query($link, "BEGIN"); + + //print 'GUID:' . $article['guid'] . "\n"; + + $result = db_query($link, "SELECT id FROM ttrss_entries + WHERE guid = '".$article['guid']."'"); + + if (db_num_rows($result) == 0) { + + $result = db_query($link, + "INSERT INTO ttrss_entries + (title, + guid, + link, + updated, + content, + content_hash, + no_orig_date, + date_updated, + date_entered, + comments, + num_comments, + author) + VALUES + ('".$article['title']."', + '".$article['guid']."', + '".$article['link']."', + '".$article['updated']."', + '".$article['content']."', + '".sha1($article['content'])."', + false, + NOW(), + NOW(), + '', + '0', + '')"); + + $result = db_query($link, "SELECT id FROM ttrss_entries + WHERE guid = '".$article['guid']."'"); + + if (db_num_rows($result) != 0) { + $ref_id = db_fetch_result($result, 0, "id"); + } + + } else { + $ref_id = db_fetch_result($result, 0, "id"); + } + + //print "Got ref ID: $ref_id\n"; + + if ($ref_id) { + + $feed_url = $article['feed_url']; + $feed_title = $article['feed_title']; + + $feed = 'NULL'; + + if ($feed_url && $feed_title) { + $result = db_query($link, "SELECT id FROM ttrss_feeds + WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'"); + + if (db_num_rows($result) != 0) { + $feed = db_fetch_result($result, 0, "id"); + } else { + // try autocreating feed in Uncategorized... + + $result = db_query($link, "INSERT INTO ttrss_feeds (owner_uid, + feed_url, title) VALUES ($owner_uid, '$feed_url', '$feed_title')"); + + $result = db_query($link, "SELECT id FROM ttrss_feeds + WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'"); + + if (db_num_rows($result) != 0) { + ++$num_feeds_created; + + $feed = db_fetch_result($result, 0, "id"); + } + } + } + + if ($feed != 'NULL') + $feed_qpart = "feed_id = $feed"; + else + $feed_qpart = "feed_id IS NULL"; + + //print "$ref_id / $feed / " . $article['title'] . "\n"; + + $result = db_query($link, "SELECT int_id FROM ttrss_user_entries + WHERE ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND $feed_qpart"); + + if (db_num_rows($result) == 0) { + + $marked = bool_to_sql_bool(sql_bool_to_bool($article['marked'])); + $published = bool_to_sql_bool(sql_bool_to_bool($article['published'])); + $score = (int) $article['score']; + + $tag_cache = $article['tag_cache']; + $label_cache = db_escape_string($article['label_cache']); + $note = $article['note']; + + //print "Importing " . $article['title'] . "
"; + + ++$num_imported; + + $result = db_query($link, + "INSERT INTO ttrss_user_entries + (ref_id, owner_uid, feed_id, unread, last_read, marked, + published, score, tag_cache, label_cache, uuid, note) + VALUES ($ref_id, $owner_uid, $feed, false, + NULL, $marked, $published, $score, '$tag_cache', + '$label_cache', '', '$note')"); + + $label_cache = json_decode($label_cache, true); + + if (is_array($label_cache) && $label_cache["no-labels"] != 1) { + foreach ($label_cache as $label) { + + label_create($link, $label[1], + $label[2], $label[3], $owner_uid); + + label_add_article($link, $ref_id, $label[1], $owner_uid); + + } + } + + //db_query($link, "COMMIT"); + } + } + } + } + } + + print "

" . + T_sprintf("Finished: %d articles processed, %d imported, %d feeds created.", + $num_processed, $num_imported, $num_feeds_created) . + "

"; + + } else { + + print "

" . __("Could not load XML document.") . "

"; + + } + } + + function exportData() { + + print "

You need to prepare exported data first by clicking the button below.

"; + + print "
"; + print ""; + + print ""; + + print "
"; + + + } + + function dataImport() { + header("Content-Type: text/html"); # required for iframe + + print "
"; + + if (is_file($_FILES['export_file']['tmp_name'])) { + + $this->perform_data_import($this->link, $_FILES['export_file']['tmp_name'], $_SESSION['uid']); + + } else { + print "

" . T_sprintf("Could not upload file. You might need to adjust upload_max_filesize + in PHP.ini (current value = %s)", ini_get("upload_max_filesize")) . " or use CLI import tool.

"; + + } + + print ""; + + print "
"; + + } + + +} +?> -- cgit v1.2.3