]> git.wh0rd.org - tt-rss.git/blobdiff - classes/opml.php
strip_harmful_tags: remove data- attributes
[tt-rss.git] / classes / opml.php
index f7bab4fff1523c2ff91e0d63cfeeb3a5c6117035..5b7690375ed762c4a52c6fa4880888e695f92f39 100644 (file)
-<?php\r
-class Opml extends Protected_Handler {\r
-\r
-       function csrf_ignore($method) {\r
-               $csrf_ignored = array("export", "import");\r
-\r
-               return array_search($method, $csrf_ignored) !== false;\r
-       }\r
-\r
-       function export() {\r
-               $output_name = $_REQUEST["filename"];\r
-               if (!$output_name) $output_name = "TinyTinyRSS.opml";\r
-\r
-               $show_settings = $_REQUEST["settings"];\r
-\r
-               $owner_uid = $_SESSION["uid"];\r
-               return $this->opml_export($output_name, $owner_uid, false, ($show_settings == 1));\r
-       }\r
-\r
-       function import() {\r
-               $owner_uid = $_SESSION["uid"];\r
-\r
-               header('Content-Type: text/html; charset=utf-8');\r
-\r
-               print "<html>\r
-                       <head>\r
-                               <link rel=\"stylesheet\" href=\"utility.css\" type=\"text/css\">\r
-                               <title>".__("OPML Utility")."</title>\r
-                               <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\r
-                       </head>\r
-                       <body>\r
-                       <div class=\"floatingLogo\"><img src=\"images/logo_wide.png\"></div>\r
-                       <h1>".__('OPML Utility')."</h1>";\r
-\r
-               add_feed_category($this->link, "Imported feeds");\r
-\r
-               $this->opml_notice(__("Importing OPML..."));\r
-               $this->opml_import($owner_uid);\r
-\r
-               print "<br><form method=\"GET\" action=\"prefs.php\">\r
-                       <input type=\"submit\" value=\"".__("Return to preferences")."\">\r
-                       </form>";\r
-\r
-               print "</body></html>";\r
-\r
-\r
-       }\r
-\r
-       // Export\r
-\r
-       private function opml_export_category($owner_uid, $cat_id, $hide_private_feeds=false) {\r
-\r
-               if ($cat_id) {\r
-                       $cat_qpart = "parent_cat = '$cat_id'";\r
-                       $feed_cat_qpart = "cat_id = '$cat_id'";\r
-               } else {\r
-                       $cat_qpart = "parent_cat IS NULL";\r
-                       $feed_cat_qpart = "cat_id IS NULL";\r
-               }\r
-\r
-               if ($hide_private_feeds)\r
-                       $hide_qpart = "(private IS false AND auth_login = '' AND auth_pass = '')";\r
-               else\r
-                       $hide_qpart = "true";\r
-\r
-               $out = "";\r
-\r
-               if ($cat_id) {\r
-                       $result = db_query($this->link, "SELECT title FROM ttrss_feed_categories WHERE id = '$cat_id'\r
-                               AND owner_uid = '$owner_uid'");\r
-                       $cat_title = db_fetch_result($result, 0, "title");\r
-               }\r
-\r
-               if ($cat_title) $out .= "<outline title=\"$cat_title\">\n";\r
-\r
-               $result = db_query($this->link, "SELECT id,title\r
-                       FROM ttrss_feed_categories WHERE\r
-                       $cat_qpart AND owner_uid = '$owner_uid' ORDER BY order_id, title");\r
-\r
-               while ($line = db_fetch_assoc($result)) {\r
-                       $title = htmlspecialchars($line["title"]);\r
-                       $out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds);\r
-               }\r
-\r
-               $feeds_result = db_query($this->link, "select title, feed_url, site_url\r
-                               from ttrss_feeds where $feed_cat_qpart AND owner_uid = '$owner_uid' AND $hide_qpart\r
-                               order by order_id, title");\r
-\r
-               while ($fline = db_fetch_assoc($feeds_result)) {\r
-                       $title = htmlspecialchars($fline["title"]);\r
-                       $url = htmlspecialchars($fline["feed_url"]);\r
-                       $site_url = htmlspecialchars($fline["site_url"]);\r
-\r
-                       if ($site_url) {\r
-                               $html_url_qpart = "htmlUrl=\"$site_url\"";\r
-                       } else {\r
-                               $html_url_qpart = "";\r
-                       }\r
-\r
-                       $out .= "<outline text=\"$title\" xmlUrl=\"$url\" $html_url_qpart/>\n";\r
-               }\r
-\r
-               if ($cat_title) $out .= "</outline>\n";\r
-\r
-               return $out;\r
-       }\r
-\r
-       function opml_export($name, $owner_uid, $hide_private_feeds=false, $include_settings=true) {\r
-               if (!$owner_uid) return;\r
-\r
-               if (!isset($_REQUEST["debug"])) {\r
-                       header("Content-type: application/xml+opml");\r
-                       header("Content-Disposition: attachment; filename=" . $name );\r
-               } else {\r
-                       header("Content-type: text/xml");\r
-               }\r
-\r
-               $out = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">";\r
-\r
-               $out .= "<opml version=\"1.0\">";\r
-               $out .= "<head>\r
-                       <dateCreated>" . date("r", time()) . "</dateCreated>\r
-                       <title>Tiny Tiny RSS Feed Export</title>\r
-               </head>";\r
-               $out .= "<body>";\r
-\r
-               $out .= $this->opml_export_category($owner_uid, false, $hide_private_feeds);\r
-\r
-               # export tt-rss settings\r
-\r
-               if ($include_settings) {\r
-                       $out .= "<outline title=\"tt-rss-prefs\" schema-version=\"".SCHEMA_VERSION."\">";\r
-\r
-                       $result = db_query($this->link, "SELECT pref_name, value FROM ttrss_user_prefs WHERE\r
-                          profile IS NULL AND owner_uid = " . $_SESSION["uid"] . " ORDER BY pref_name");\r
-\r
-                       while ($line = db_fetch_assoc($result)) {\r
-                               $name = $line["pref_name"];\r
-                               $value = htmlspecialchars($line["value"]);\r
-\r
-                               $out .= "<outline pref-name=\"$name\" value=\"$value\"/>";\r
-                       }\r
-\r
-                       $out .= "</outline>";\r
-\r
-                       $out .= "<outline title=\"tt-rss-labels\" schema-version=\"".SCHEMA_VERSION."\">";\r
-\r
-                       $result = db_query($this->link, "SELECT * FROM ttrss_labels2 WHERE\r
-                               owner_uid = " . $_SESSION['uid']);\r
-\r
-                       while ($line = db_fetch_assoc($result)) {\r
-                               $name = htmlspecialchars($line['caption']);\r
-                               $fg_color = htmlspecialchars($line['fg_color']);\r
-                               $bg_color = htmlspecialchars($line['bg_color']);\r
-\r
-                               $out .= "<outline label-name=\"$name\" label-fg-color=\"$fg_color\" label-bg-color=\"$bg_color\"/>";\r
-\r
-                       }\r
-\r
-                       $out .= "</outline>";\r
-\r
-                       $out .= "<outline title=\"tt-rss-filters\" schema-version=\"".SCHEMA_VERSION."\">";\r
-\r
-                       $result = db_query($this->link, "SELECT filter_type,\r
-                                       reg_exp,\r
-                                       action_id,\r
-                                       enabled,\r
-                                       action_param,\r
-                                       inverse,\r
-                                       filter_param,\r
-                                       cat_filter,\r
-                                       ttrss_feeds.feed_url AS feed_url,\r
-                                       ttrss_feed_categories.title AS cat_title\r
-                                       FROM ttrss_filters\r
-                                               LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)\r
-                                               LEFT JOIN ttrss_feed_categories ON (ttrss_filters.cat_id = ttrss_feed_categories.id)\r
-                                       WHERE\r
-                                               ttrss_filters.owner_uid = " . $_SESSION['uid']);\r
-\r
-                       while ($line = db_fetch_assoc($result)) {\r
-                               $name = htmlspecialchars($line['reg_exp']);\r
-\r
-                               foreach (array('enabled', 'inverse', 'cat_filter') as $b) {\r
-                                       $line[$b] = sql_bool_to_bool($line[$b]);\r
-                               }\r
-\r
-                               $filter = json_encode($line);\r
-\r
-                               $out .= "<outline filter-name=\"$name\">$filter</outline>";\r
-\r
-                       }\r
-\r
-\r
-                       $out .= "</outline>";\r
-               }\r
-\r
-               $out .= "</body></opml>";\r
-\r
-               // Format output.\r
-               $doc = new DOMDocument();\r
-               $doc->formatOutput = true;\r
-               $doc->preserveWhiteSpace = false;\r
-               $doc->loadXML($out);\r
-               $res = $doc->saveXML();\r
-\r
-               // saveXML uses a two-space indent.  Change to tabs.\r
-               $res = preg_replace_callback('/^(?:  )+/mu',\r
-                       create_function(\r
-                               '$matches',\r
-                               'return str_repeat("\t", intval(strlen($matches[0])/2));'),\r
-                       $res);\r
-\r
-               print $res;\r
-       }\r
-\r
-       // Import\r
-\r
-       private function opml_import_feed($doc, $node, $cat_id, $owner_uid) {\r
-               $attrs = $node->attributes;\r
-\r
-               $feed_title = db_escape_string($attrs->getNamedItem('text')->nodeValue);\r
-               if (!$feed_title) $feed_title = db_escape_string($attrs->getNamedItem('title')->nodeValue);\r
-\r
-               $feed_url = db_escape_string($attrs->getNamedItem('xmlUrl')->nodeValue);\r
-               if (!$feed_url) $feed_url = db_escape_string($attrs->getNamedItem('xmlURL')->nodeValue);\r
-\r
-               $site_url = db_escape_string($attrs->getNamedItem('htmlUrl')->nodeValue);\r
-\r
-               if ($feed_url && $feed_title) {\r
-                       $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE\r
-                               feed_url = '$feed_url' AND owner_uid = '$owner_uid'");\r
-\r
-                       if (db_num_rows($result) == 0) {\r
-                               #$this->opml_notice("[FEED] [$feed_title/$feed_url] dst_CAT=$cat_id");\r
-                               $this->opml_notice(T_sprintf("Adding feed: %s", $feed_title));\r
-\r
-                               $query = "INSERT INTO ttrss_feeds\r
-                                       (title, feed_url, owner_uid, cat_id, site_url, order_id) VALUES\r
-                                       ('$feed_title', '$feed_url', '$owner_uid',\r
-                                       '$cat_id', '$site_url', 0)";\r
-                               db_query($this->link, $query);\r
-\r
-                       } else {\r
-                               $this->opml_notice(T_sprintf("Duplicate feed: %s", $feed_title));\r
-                       }\r
-               }\r
-       }\r
-\r
-       private function opml_import_label($doc, $node, $owner_uid) {\r
-               $attrs = $node->attributes;\r
-               $label_name = db_escape_string($attrs->getNamedItem('label-name')->nodeValue);\r
-\r
-               if ($label_name) {\r
-                       $fg_color = db_escape_string($attrs->getNamedItem('label-fg-color')->nodeValue);\r
-                       $bg_color = db_escape_string($attrs->getNamedItem('label-bg-color')->nodeValue);\r
-\r
-                       if (!label_find_id($this->link, $label_name, $_SESSION['uid'])) {\r
-                               $this->opml_notice(T_sprintf("Adding label %s", htmlspecialchars($label_name)));\r
-                               label_create($this->link, $label_name, $fg_color, $bg_color);\r
-                       } else {\r
-                               $this->opml_notice(T_sprintf("Duplicate label: %s", htmlspecialchars($label_name)));\r
-                       }\r
-               }\r
-       }\r
-\r
-       private function opml_import_preference($doc, $node, $owner_uid) {\r
-               $attrs = $node->attributes;\r
-               $pref_name = db_escape_string($attrs->getNamedItem('pref-name')->nodeValue);\r
-\r
-               if ($pref_name) {\r
-                       $pref_value = db_escape_string($attrs->getNamedItem('value')->nodeValue);\r
-\r
-                       $this->opml_notice(T_sprintf("Setting preference key %s to %s",\r
-                               $pref_name, $pref_value));\r
-\r
-                       set_pref($this->link, $pref_name, $pref_value);\r
-               }\r
-       }\r
-\r
-       private function opml_import_filter($doc, $node, $owner_uid) {\r
-               $attrs = $node->attributes;\r
-\r
-               $filter_name = db_escape_string($attrs->getNamedItem('filter-name')->nodeValue);\r
-\r
-               if ($filter_name) {\r
-\r
-               $filter = json_decode($node->nodeValue, true);\r
-\r
-                       if ($filter) {\r
-                               $reg_exp = db_escape_string($filter['reg_exp']);\r
-                               $filter_type = (int)$filter['filter_type'];\r
-                               $action_id = (int)$filter['action_id'];\r
-\r
-                               $result = db_query($this->link, "SELECT id FROM ttrss_filters WHERE\r
-                                       reg_exp = '$reg_exp' AND\r
-                                       filter_type = '$filter_type' AND\r
-                                       action_id = '$action_id' AND\r
-                                       owner_uid = " .$_SESSION['uid']);\r
-\r
-                               if (db_num_rows($result) == 0) {\r
-                                       $enabled = bool_to_sql_bool($filter['enabled']);\r
-                                       $action_param = db_escape_string($filter['action_param']);\r
-                                       $inverse = bool_to_sql_bool($filter['inverse']);\r
-                                       $filter_param = db_escape_string($filter['filter_param']);\r
-                                       $cat_filter = bool_to_sql_bool($filter['cat_filter']);\r
-\r
-                                       $feed_url = db_escape_string($filter['feed_url']);\r
-                                       $cat_title = db_escape_string($filter['cat_title']);\r
-\r
-                                       $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE\r
-                                               feed_url = '$feed_url' AND owner_uid = $owner_uid");\r
-\r
-                                       if (db_num_rows($result) != 0) {\r
-                                               $feed_id = db_fetch_result($result, 0, "id");\r
-                                       } else {\r
-                                               $feed_id = "NULL";\r
-                                       }\r
-\r
-                                       $result = db_query($this->link, "SELECT id FROM ttrss_feed_categories WHERE\r
-                                               title = '$cat_title' AND  owner_uid = $owner_uid");\r
-\r
-                                       if (db_num_rows($result) != 0) {\r
-                                               $cat_id = db_fetch_result($result, 0, "id");\r
-                                       } else {\r
-                                               $cat_id = "NULL";\r
-                                       }\r
-\r
-                                       $this->opml_notice(T_sprintf("Adding filter %s", htmlspecialchars($reg_exp)));\r
-\r
-                                       $query = "INSERT INTO ttrss_filters (filter_type, action_id,\r
-                                                       enabled, inverse, action_param, filter_param,\r
-                                                       cat_filter, feed_id,\r
-                                                       cat_id, reg_exp,\r
-                                                       owner_uid)\r
-                                               VALUES ($filter_type, $action_id,\r
-                                                       $enabled, $inverse, '$action_param', '$filter_param',\r
-                                                       $cat_filter, $feed_id,\r
-                                                       $cat_id, '$reg_exp', ".\r
-                                                       $_SESSION['uid'].")";\r
-\r
-                                       db_query($this->link, $query);\r
-\r
-                               } else {\r
-                                       $this->opml_notice(T_sprintf("Duplicate filter %s", htmlspecialchars($reg_exp)));\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       private function opml_import_category($doc, $root_node, $owner_uid, $parent_id) {\r
-               $body = $doc->getElementsByTagName('body');\r
-\r
-               $default_cat_id = (int) get_feed_category($this->link, 'Imported feeds', false);\r
-\r
-               if ($root_node) {\r
-                       $cat_title = db_escape_string($root_node->attributes->getNamedItem('title')->nodeValue);\r
-\r
-                       if (!in_array($cat_title, array("tt-rss-filters", "tt-rss-labels", "tt-rss-prefs"))) {\r
-                               $cat_id = get_feed_category($this->link, $cat_title, $parent_id);\r
-                               db_query($this->link, "BEGIN");\r
-                               if ($cat_id === false) {\r
-                                       add_feed_category($this->link, $cat_title, $parent_id);\r
-                                       $cat_id = get_feed_category($this->link, $cat_title, $parent_id);\r
-                               }\r
-                               db_query($this->link, "COMMIT");\r
-                       } else {\r
-                               $cat_id = 0;\r
-                       }\r
-\r
-                       $outlines = $root_node->childNodes;\r
-\r
-               } else {\r
-                       $xpath = new DOMXpath($doc);\r
-                       $outlines = $xpath->query("//opml/body/outline");\r
-\r
-                       $cat_id = 0;\r
-               }\r
-\r
-               #$this->opml_notice("[CAT] $cat_title id: $cat_id P_id: $parent_id");\r
-               $this->opml_notice(T_sprintf("Processing category: %s", $cat_title ? $cat_title : __("Uncategorized")));\r
-\r
-               foreach ($outlines as $node) {\r
-                       if ($node->hasAttributes() && strtolower($node->tagName) == "outline") {\r
-                               $attrs = $node->attributes;\r
-                               $node_cat_title = db_escape_string($attrs->getNamedItem('title')->nodeValue);\r
-\r
-                               if ($node->hasChildNodes() && $node_cat_title) {\r
-                                       $this->opml_import_category($doc, $node, $owner_uid, $cat_id);\r
-                               } else {\r
-\r
-                                       if (!$cat_id) {\r
-                                               $dst_cat_id = $default_cat_id;\r
-                                       } else {\r
-                                               $dst_cat_id = $cat_id;\r
-                                       }\r
-\r
-                                       switch ($cat_title) {\r
-                                       case "tt-rss-prefs":\r
-                                               $this->opml_import_preference($doc, $node, $owner_uid);\r
-                                               break;\r
-                                       case "tt-rss-labels":\r
-                                               $this->opml_import_label($doc, $node, $owner_uid);\r
-                                               break;\r
-                                       case "tt-rss-filters":\r
-                                               $this->opml_import_filter($doc, $node, $owner_uid);\r
-                                               break;\r
-                                       default:\r
-                                               $this->opml_import_feed($doc, $node, $dst_cat_id, $owner_uid);\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       function opml_import($owner_uid) {\r
-               if (!$owner_uid) return;\r
-\r
-               $debug = isset($_REQUEST["debug"]);\r
-               $doc = false;\r
-\r
-               #if ($debug) $doc = DOMDocument::load("/tmp/test.opml");\r
-\r
-               if (is_file($_FILES['opml_file']['tmp_name'])) {\r
-                       $doc = DOMDocument::load($_FILES['opml_file']['tmp_name']);\r
-               } else if (!$doc) {\r
-                       print_error(__('Error: please upload OPML file.'));\r
-                       return;\r
-               }\r
-\r
-               if ($doc) {\r
-                       $this->opml_import_category($doc, false, $owner_uid, false);\r
-               } else {\r
-                       print_error(__('Error while parsing document.'));\r
-               }\r
-       }\r
-\r
-       private function opml_notice($msg) {\r
-               print "$msg<br/>";\r
-       }\r
-\r
-}\r
-?>\r
+<?php
+class Opml extends Handler_Protected {
+
+       function csrf_ignore($method) {
+               $csrf_ignored = array("export", "import");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
+       function export() {
+               $output_name = $_REQUEST["filename"];
+               if (!$output_name) $output_name = "TinyTinyRSS.opml";
+
+               $show_settings = $_REQUEST["settings"];
+
+               $owner_uid = $_SESSION["uid"];
+
+               $rc = $this->opml_export($output_name, $owner_uid, false, ($show_settings == 1));
+
+               return $rc;
+       }
+
+       function import() {
+               $owner_uid = $_SESSION["uid"];
+
+               header('Content-Type: text/html; charset=utf-8');
+
+               print "<html>
+                       <head>
+                               ".stylesheet_tag("css/default.css")."
+                               <title>".__("OPML Utility")."</title>
+                               <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
+                       </head>
+                       <body class='claro ttrss_utility'>
+                       <div class=\"floatingLogo\"><img src=\"images/logo_small.png\"></div>
+                       <h1>".__('OPML Utility')."</h1><div class='content'>";
+
+               add_feed_category("Imported feeds");
+
+               $this->opml_notice(__("Importing OPML..."));
+
+               $this->opml_import($owner_uid);
+
+               print "<br><form method=\"GET\" action=\"prefs.php\">
+                       <input type=\"submit\" value=\"".__("Return to preferences")."\">
+                       </form>";
+
+               print "</div></body></html>";
+
+
+       }
+
+       // Export
+
+       private function opml_export_category($owner_uid, $cat_id, $hide_private_feeds=false) {
+
+               $cat_id = (int) $cat_id;
+
+               if ($hide_private_feeds)
+                       $hide_qpart = "(private IS false AND auth_login = '' AND auth_pass = '')";
+               else
+                       $hide_qpart = "true";
+
+               $out = "";
+
+               if ($cat_id) {
+                       $sth = $this->pdo->prepare("SELECT title FROM ttrss_feed_categories WHERE id = ?
+                               AND owner_uid = ?");
+                       $sth->execute([$cat_id, $owner_uid]);
+                       $row = $sth->fetch();
+                       $cat_title = htmlspecialchars($row['title']);
+               }
+
+               if ($cat_title) $out .= "<outline text=\"$cat_title\">\n";
+
+               $sth = $this->pdo->prepare("SELECT id,title
+                       FROM ttrss_feed_categories WHERE
+                               (parent_cat = :cat OR (:cat = 0 AND parent_cat IS NULL)) AND
+                               owner_uid = :uid ORDER BY order_id, title");
+
+               $sth->execute([':cat' => $cat_id, ':uid' => $owner_uid]);
+
+               while ($line = $sth->fetch()) {
+                       $out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds);
+               }
+
+               $fsth = $this->pdo->prepare("select title, feed_url, site_url
+                               FROM ttrss_feeds WHERE
+                                       (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) AND owner_uid = :uid AND $hide_qpart
+                               ORDER BY order_id, title");
+
+               $fsth->execute([':cat' => $cat_id, ':uid' => $owner_uid]);
+
+               while ($fline = $fsth->fetch()) {
+                       $title = htmlspecialchars($fline["title"]);
+                       $url = htmlspecialchars($fline["feed_url"]);
+                       $site_url = htmlspecialchars($fline["site_url"]);
+
+                       if ($site_url) {
+                               $html_url_qpart = "htmlUrl=\"$site_url\"";
+                       } else {
+                               $html_url_qpart = "";
+                       }
+
+                       $out .= "<outline type=\"rss\" text=\"$title\" xmlUrl=\"$url\" $html_url_qpart/>\n";
+               }
+
+               if ($cat_title) $out .= "</outline>\n";
+
+               return $out;
+       }
+
+       function opml_export($name, $owner_uid, $hide_private_feeds=false, $include_settings=true) {
+               if (!$owner_uid) return;
+
+               if (!isset($_REQUEST["debug"])) {
+                       header("Content-type: application/xml+opml");
+                       header("Content-Disposition: attachment; filename=" . $name );
+               } else {
+                       header("Content-type: text/xml");
+               }
+
+               $out = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">";
+
+               $out .= "<opml version=\"1.0\">";
+               $out .= "<head>
+                       <dateCreated>" . date("r", time()) . "</dateCreated>
+                       <title>Tiny Tiny RSS Feed Export</title>
+               </head>";
+               $out .= "<body>";
+
+               $out .= $this->opml_export_category($owner_uid, 0, $hide_private_feeds);
+
+               # export tt-rss settings
+
+               if ($include_settings) {
+                       $out .= "<outline text=\"tt-rss-prefs\" schema-version=\"".SCHEMA_VERSION."\">";
+
+                       $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs WHERE
+                          profile IS NULL AND owner_uid = ? ORDER BY pref_name");
+                       $sth->execute([$owner_uid]);
+
+                       while ($line = $sth->fetch()) {
+                               $name = $line["pref_name"];
+                               $value = htmlspecialchars($line["value"]);
+
+                               $out .= "<outline pref-name=\"$name\" value=\"$value\"/>";
+                       }
+
+                       $out .= "</outline>";
+
+                       $out .= "<outline text=\"tt-rss-labels\" schema-version=\"".SCHEMA_VERSION."\">";
+
+                       $sth = $this->pdo->prepare("SELECT * FROM ttrss_labels2 WHERE
+                               owner_uid = ?");
+                       $sth->execute([$owner_uid]);
+
+                       while ($line = $sth->fetch()) {
+                               $name = htmlspecialchars($line['caption']);
+                               $fg_color = htmlspecialchars($line['fg_color']);
+                               $bg_color = htmlspecialchars($line['bg_color']);
+
+                               $out .= "<outline label-name=\"$name\" label-fg-color=\"$fg_color\" label-bg-color=\"$bg_color\"/>";
+
+                       }
+
+                       $out .= "</outline>";
+
+                       $out .= "<outline text=\"tt-rss-filters\" schema-version=\"".SCHEMA_VERSION."\">";
+
+                       $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2
+                               WHERE owner_uid = ? ORDER BY id");
+                       $sth->execute([$owner_uid]);
+
+                       while ($line = $sth->fetch()) {
+                               $line["rules"] = array();
+                               $line["actions"] = array();
+
+                               $tmph = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules
+                                       WHERE filter_id = ?");
+                               $tmph->execute([$line['id']]);
+
+                               while ($tmp_line = $tmph->fetch()) {
+                                       unset($tmp_line["id"]);
+                                       unset($tmp_line["filter_id"]);
+
+                                       $cat_filter = $tmp_line["cat_filter"];
+
+                                       if (!$tmp_line["match_on"]) {
+                        if ($cat_filter && $tmp_line["cat_id"] || $tmp_line["feed_id"]) {
+                            $tmp_line["feed"] = Feeds::getFeedTitle(
+                                $cat_filter ? $tmp_line["cat_id"] : $tmp_line["feed_id"],
+                                $cat_filter);
+                        } else {
+                            $tmp_line["feed"] = "";
+                        }
+                    } else {
+                                           $match = [];
+                                           foreach (json_decode($tmp_line["match_on"], true) as $feed_id) {
+
+                            if (strpos($feed_id, "CAT:") === 0) {
+                                $feed_id = (int)substr($feed_id, 4);
+                                if ($feed_id) {
+                                    array_push($match, [Feeds::getCategoryTitle($feed_id), true, false]);
+                                } else {
+                                    array_push($match, [0, true, true]);
+                                }
+                            } else {
+                                if ($feed_id) {
+                                    array_push($match, [Feeds::getFeedTitle((int)$feed_id), false, false]);
+                                } else {
+                                    array_push($match, [0, false, true]);
+                                }
+                            }
+                        }
+
+                        $tmp_line["match"] = $match;
+                                           unset($tmp_line["match_on"]);
+                    }
+
+                                       unset($tmp_line["feed_id"]);
+                                       unset($tmp_line["cat_id"]);
+
+                                       array_push($line["rules"], $tmp_line);
+                               }
+
+                               $tmph = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions
+                                       WHERE filter_id = ?");
+                               $tmph->execute([$line['id']]);
+
+                               while ($tmp_line = $tmph->fetch()) {
+                                       unset($tmp_line["id"]);
+                                       unset($tmp_line["filter_id"]);
+
+                                       array_push($line["actions"], $tmp_line);
+                               }
+
+                               unset($line["id"]);
+                               unset($line["owner_uid"]);
+                               $filter = json_encode($line);
+
+                               $out .= "<outline filter-type=\"2\"><![CDATA[$filter]]></outline>";
+
+                       }
+
+
+                       $out .= "</outline>";
+               }
+
+               $out .= "</body></opml>";
+
+               // Format output.
+               $doc = new DOMDocument();
+               $doc->formatOutput = true;
+               $doc->preserveWhiteSpace = false;
+               $doc->loadXML($out);
+
+               $xpath = new DOMXpath($doc);
+               $outlines = $xpath->query("//outline[@title]");
+
+               // cleanup empty categories
+               foreach ($outlines as $node) {
+                       if ($node->getElementsByTagName('outline')->length == 0)
+                               $node->parentNode->removeChild($node);
+               }
+
+               $res = $doc->saveXML();
+
+/*             // saveXML uses a two-space indent.  Change to tabs.
+               $res = preg_replace_callback('/^(?:  )+/mu',
+                       create_function(
+                               '$matches',
+                               'return str_repeat("\t", intval(strlen($matches[0])/2));'),
+                       $res); */
+
+               print $res;
+       }
+
+       // Import
+
+       private function opml_import_feed($node, $cat_id, $owner_uid) {
+               $attrs = $node->attributes;
+
+               $feed_title = mb_substr($attrs->getNamedItem('text')->nodeValue, 0, 250);
+               if (!$feed_title) $feed_title = mb_substr($attrs->getNamedItem('title')->nodeValue, 0, 250);
+
+               $feed_url = $attrs->getNamedItem('xmlUrl')->nodeValue;
+               if (!$feed_url) $feed_url = $attrs->getNamedItem('xmlURL')->nodeValue;
+
+               $site_url = mb_substr($attrs->getNamedItem('htmlUrl')->nodeValue, 0, 250);
+
+               if ($feed_url) {
+                       $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE
+                               feed_url = ? AND owner_uid = ?");
+                       $sth->execute([$feed_url, $owner_uid]);
+
+                       if (!$feed_title) $feed_title = '[Unknown]';
+
+                       if (!$sth->fetch()) {
+                               #$this->opml_notice("[FEED] [$feed_title/$feed_url] dst_CAT=$cat_id");
+                               $this->opml_notice(T_sprintf("Adding feed: %s", $feed_title == '[Unknown]' ? $feed_url : $feed_title));
+
+                               if (!$cat_id) $cat_id = null;
+
+                               $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds
+                                       (title, feed_url, owner_uid, cat_id, site_url, order_id) VALUES
+                                       (?, ?, ?, ?, ?, 0)");
+
+                               $sth->execute([$feed_title, $feed_url, $owner_uid, $cat_id, $site_url]);
+
+                       } else {
+                               $this->opml_notice(T_sprintf("Duplicate feed: %s", $feed_title == '[Unknown]' ? $feed_url : $feed_title));
+                       }
+               }
+       }
+
+       private function opml_import_label($node, $owner_uid) {
+               $attrs = $node->attributes;
+               $label_name = $attrs->getNamedItem('label-name')->nodeValue;
+
+               if ($label_name) {
+                       $fg_color = $attrs->getNamedItem('label-fg-color')->nodeValue;
+                       $bg_color = $attrs->getNamedItem('label-bg-color')->nodeValue;
+
+                       if (!Labels::find_id($label_name, $_SESSION['uid'])) {
+                               $this->opml_notice(T_sprintf("Adding label %s", htmlspecialchars($label_name)));
+                               Labels::create($label_name, $fg_color, $bg_color, $owner_uid);
+                       } else {
+                               $this->opml_notice(T_sprintf("Duplicate label: %s", htmlspecialchars($label_name)));
+                       }
+               }
+       }
+
+       private function opml_import_preference($node) {
+               $attrs = $node->attributes;
+               $pref_name = $attrs->getNamedItem('pref-name')->nodeValue;
+
+               if ($pref_name) {
+                       $pref_value = $attrs->getNamedItem('value')->nodeValue;
+
+                       $this->opml_notice(T_sprintf("Setting preference key %s to %s",
+                               $pref_name, $pref_value));
+
+                       set_pref($pref_name, $pref_value);
+               }
+       }
+
+       private function opml_import_filter($node) {
+               $attrs = $node->attributes;
+
+               $filter_type = $attrs->getNamedItem('filter-type')->nodeValue;
+
+               if ($filter_type == '2') {
+                       $filter = json_decode($node->nodeValue, true);
+
+                       if ($filter) {
+                               $match_any_rule = bool_to_sql_bool($filter["match_any_rule"]);
+                               $enabled = bool_to_sql_bool($filter["enabled"]);
+                               $inverse = bool_to_sql_bool($filter["inverse"]);
+                               $title = $filter["title"];
+
+                               //print "F: $title, $inverse, $enabled, $match_any_rule";
+
+                               $sth = $this->pdo->prepare("INSERT INTO ttrss_filters2 (match_any_rule,enabled,inverse,title,owner_uid)
+                                       VALUES (?, ?, ?, ?, ?)");
+
+                               $sth->execute([$match_any_rule, $enabled, $inverse, $title, $_SESSION['uid']]);
+
+                               $sth = $this->pdo->prepare("SELECT MAX(id) AS id FROM ttrss_filters2 WHERE
+                                       owner_uid = ?");
+                               $sth->execute([$_SESSION['uid']]);
+
+                               $row = $sth->fetch();
+                               $filter_id = $row['id'];
+
+                               if ($filter_id) {
+                                       $this->opml_notice(T_sprintf("Adding filter..."));
+
+                                       foreach ($filter["rules"] as $rule) {
+                                               $feed_id = null;
+                                               $cat_id = null;
+
+                                               if ($rule["match"]) {
+
+                            $match_on = [];
+
+                                                   foreach ($rule["match"] as $match) {
+                                                       list ($name, $is_cat, $is_id) = $match;
+
+                                                       if ($is_id) {
+                                                           array_push($match_on, ($is_cat ? "CAT:" : "") . $name);
+                                } else {
+
+                                                           $match_id = false;
+
+                                    if (!$is_cat) {
+                                        $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
+                                               WHERE title = ? AND owner_uid = ?");
+
+                                        $tsth->execute([$name, $_SESSION['uid']]);
+
+                                        if ($row = $tsth->fetch()) {
+                                            $match_id = $row['id'];
+                                        }
+                                    } else {
+                                        $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories
+                                               WHERE title = ? AND owner_uid = ?");
+                                                                               $tsth->execute([$name, $_SESSION['uid']]);
+
+                                                                               if ($row = $tsth->fetch()) {
+                                                                                       $match_id = $row['id'];
+                                                                               }
+                                    }
+
+                                    if ($match_id) array_push($match_on, $match_id);
+                                }
+                            }
+
+                            $reg_exp = $rule["reg_exp"];
+                            $filter_type = (int)$rule["filter_type"];
+                            $inverse = bool_to_sql_bool($rule["inverse"]);
+                            $match_on = json_encode($match_on);
+
+                            $usth = $this->pdo->prepare("INSERT INTO ttrss_filters2_rules
+                                                               (feed_id,cat_id,match_on,filter_id,filter_type,reg_exp,cat_filter,inverse)
+                                VALUES
+                                (NULL, NULL, ?, ?, ?, ?, false, ?)");
+                            $usth->execute([$match_on, $filter_id, $filter_type, $reg_exp, $inverse]);
+
+                        } else {
+
+                            if (!$rule["cat_filter"]) {
+                                $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
+                                    WHERE title = ? AND owner_uid = ?");
+
+                                $tsth->execute([$rule['feed'], $_SESSION['uid']]);
+
+                                if ($row = $tsth->fetch()) {
+                                    $feed_id = $row['id'];
+                                }
+                            } else {
+                                                               $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories
+                                    WHERE title = ? AND owner_uid = ?");
+
+                                                               $tsth->execute([$rule['feed'], $_SESSION['uid']]);
+
+                                                               if ($row = $tsth->fetch()) {
+                                                                       $feed_id = $row['id'];
+                                                               }
+                            }
+
+                            $cat_filter = bool_to_sql_bool($rule["cat_filter"]);
+                            $reg_exp = $rule["reg_exp"];
+                            $filter_type = (int)$rule["filter_type"];
+                            $inverse = bool_to_sql_bool($rule["inverse"]);
+
+                            $usth = $this->pdo->prepare("INSERT INTO ttrss_filters2_rules
+                                                               (feed_id,cat_id,filter_id,filter_type,reg_exp,cat_filter,inverse)
+                                VALUES
+                                (?, ?, ?, ?, ?, ?, ?)");
+                            $usth->execute([$feed_id, $cat_id, $filter_id, $filter_type, $reg_exp, $cat_filter, $inverse]);
+                        }
+                                       }
+
+                                       foreach ($filter["actions"] as $action) {
+
+                                               $action_id = (int)$action["action_id"];
+                                               $action_param = $action["action_param"];
+
+                                               $usth = $this->pdo->prepare("INSERT INTO ttrss_filters2_actions
+                                                       (filter_id,action_id,action_param)
+                                                       VALUES
+                                                       (?, ?, ?)");
+                                               $usth->execute([$filter_id, $action_id, $action_param]);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       private function opml_import_category($doc, $root_node, $owner_uid, $parent_id) {
+               $default_cat_id = (int) $this->get_feed_category('Imported feeds', false);
+
+               if ($root_node) {
+                       $cat_title = mb_substr($root_node->attributes->getNamedItem('text')->nodeValue, 0, 250);
+
+                       if (!$cat_title)
+                               $cat_title = mb_substr($root_node->attributes->getNamedItem('title')->nodeValue, 0, 250);
+
+                       if (!in_array($cat_title, array("tt-rss-filters", "tt-rss-labels", "tt-rss-prefs"))) {
+                               $cat_id = $this->get_feed_category($cat_title, $parent_id);
+
+                               if ($cat_id === false) {
+                                       add_feed_category($cat_title, $parent_id);
+                                       $cat_id = $this->get_feed_category($cat_title, $parent_id);
+                               }
+
+                       } else {
+                               $cat_id = 0;
+                       }
+
+                       $outlines = $root_node->childNodes;
+
+               } else {
+                       $xpath = new DOMXpath($doc);
+                       $outlines = $xpath->query("//opml/body/outline");
+
+                       $cat_id = 0;
+               }
+
+               #$this->opml_notice("[CAT] $cat_title id: $cat_id P_id: $parent_id");
+               $this->opml_notice(T_sprintf("Processing category: %s", $cat_title ? $cat_title : __("Uncategorized")));
+
+               foreach ($outlines as $node) {
+                       if ($node->hasAttributes() && strtolower($node->tagName) == "outline") {
+                               $attrs = $node->attributes;
+                               $node_cat_title = $attrs->getNamedItem('text')->nodeValue;
+
+                               if (!$node_cat_title)
+                                       $node_cat_title = $attrs->getNamedItem('title')->nodeValue;
+
+                               $node_feed_url = $attrs->getNamedItem('xmlUrl')->nodeValue;
+
+                               if ($node_cat_title && !$node_feed_url) {
+                                       $this->opml_import_category($doc, $node, $owner_uid, $cat_id);
+                               } else {
+
+                                       if (!$cat_id) {
+                                               $dst_cat_id = $default_cat_id;
+                                       } else {
+                                               $dst_cat_id = $cat_id;
+                                       }
+
+                                       switch ($cat_title) {
+                                       case "tt-rss-prefs":
+                                               $this->opml_import_preference($node);
+                                               break;
+                                       case "tt-rss-labels":
+                                               $this->opml_import_label($node, $owner_uid);
+                                               break;
+                                       case "tt-rss-filters":
+                                               $this->opml_import_filter($node);
+                                               break;
+                                       default:
+                                               $this->opml_import_feed($node, $dst_cat_id, $owner_uid);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       function opml_import($owner_uid) {
+               if (!$owner_uid) return;
+
+               $doc = false;
+
+               if ($_FILES['opml_file']['error'] != 0) {
+                       print_error(T_sprintf("Upload failed with error code %d",
+                               $_FILES['opml_file']['error']));
+                       return;
+               }
+
+               if (is_uploaded_file($_FILES['opml_file']['tmp_name'])) {
+                       $tmp_file = tempnam(CACHE_DIR . '/upload', 'opml');
+
+                       $result = move_uploaded_file($_FILES['opml_file']['tmp_name'],
+                               $tmp_file);
+
+                       if (!$result) {
+                               print_error(__("Unable to move uploaded file."));
+                               return;
+                       }
+               } else {
+                       print_error(__('Error: please upload OPML file.'));
+                       return;
+               }
+
+               if (is_file($tmp_file)) {
+                       $doc = new DOMDocument();
+                       libxml_disable_entity_loader(false);
+                       $doc->load($tmp_file);
+                       libxml_disable_entity_loader(true);
+                       unlink($tmp_file);
+               } else if (!$doc) {
+                       print_error(__('Error: unable to find moved OPML file.'));
+                       return;
+               }
+
+               if ($doc) {
+                       $this->pdo->beginTransaction();
+                       $this->opml_import_category($doc, false, $owner_uid, false);
+                       $this->pdo->commit();
+               } else {
+                       print_error(__('Error while parsing document.'));
+               }
+       }
+
+       private function opml_notice($msg) {
+               print "$msg<br/>";
+       }
+
+       static function opml_publish_url(){
+
+               $url_path = get_self_url_prefix();
+               $url_path .= "/opml.php?op=publish&key=" .
+                       get_feed_access_key('OPML:Publish', false, $_SESSION["uid"]);
+
+               return $url_path;
+       }
+
+       function get_feed_category($feed_cat, $parent_cat_id = false) {
+
+               $parent_cat_id = (int) $parent_cat_id;
+
+               $sth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories
+                       WHERE title = :title
+                       AND (parent_cat = :parent OR (:parent = 0 AND parent_cat IS NULL))
+                       AND owner_uid = :uid");
+
+               $sth->execute([':title' => $feed_cat, ':parent' => $parent_cat_id, ':uid' => $_SESSION['uid']]);
+
+               if ($row = $sth->fetch()) {
+                       return $row['id'];
+               } else {
+                       return false;
+               }
+       }
+
+
+}