diff --git a/lib/ImportExport/OPML.php b/lib/ImportExport/OPML.php index 032d7532..6c650c8f 100644 --- a/lib/ImportExport/OPML.php +++ b/lib/ImportExport/OPML.php @@ -44,20 +44,20 @@ class OPML { $el->setAttribute("text", $r['name']); $folders[$r['id']] = $el; } - // insert each folder into its parent node; for the root folder the parent is the document root node - foreach ($folders as $id => $el) { - $parent = $parents[$id] ?? $document->documentElement; - $parent->appendChild($el); - } + } + // insert each folder into its parent node; for the root folder the parent is the document root node + foreach ($folders as $id => $el) { + $parent = $folders[$parents[$id]] ?? $document->documentElement; + $parent->appendChild($el); } // create a DOM node for each subscription and insert them directly into their folder DOM node foreach (Arsse::$db->subscriptionList($user) as $r) { $el = $document->createElement(("outline")); - $el->setAttribute("text", $r['title']); $el->setAttribute("type", "rss"); + $el->setAttribute("text", $r['title']); $el->setAttribute("xmlUrl", $r['url']); // include the category attribute only if there are tags - if (sizeof($tags[$r['id']])) { + if (isset($tags[$r['id']]) && sizeof($tags[$r['id']])) { $el->setAttribute("category", implode(",", $tags[$r['id']])); } // if flat output was requested subscriptions are inserted into the root folder diff --git a/tests/cases/ImportExport/TestOPML.php b/tests/cases/ImportExport/TestOPML.php new file mode 100644 index 00000000..387400c2 --- /dev/null +++ b/tests/cases/ImportExport/TestOPML.php @@ -0,0 +1,98 @@ + */ +class TestOPML extends \JKingWeb\Arsse\Test\AbstractTest { + protected $folders = [ + ['id' => 5, 'parent' => 3, 'children' => 0, 'feeds' => 1, 'name' => "Local"], + ['id' => 6, 'parent' => 3, 'children' => 0, 'feeds' => 2, 'name' => "National"], + ['id' => 4, 'parent' => null, 'children' => 0, 'feeds' => 0, 'name' => "Photography"], + ['id' => 3, 'parent' => null, 'children' => 2, 'feeds' => 0, 'name' => "Politics"], + ['id' => 2, 'parent' => 1, 'children' => 0, 'feeds' => 1, 'name' => "Rocketry"], + ['id' => 1, 'parent' => null, 'children' => 1, 'feeds' => 1, 'name' => "Science"], + ]; + protected $subscriptions = [ + ['id' => 3, 'folder' => 1, 'top_folder' => 1, 'unread' => 2, 'updated' => "2016-05-23 06:40:02", 'err_msg' => 'argh', 'title' => 'Ars Technica', 'url' => "http://example.com/3", 'favicon' => 'http://example.com/3.png'], + ['id' => 4, 'folder' => 6, 'top_folder' => 3, 'unread' => 6, 'updated' => "2017-10-09 15:58:34", 'err_msg' => '', 'title' => 'CBC News', 'url' => "http://example.com/4", 'favicon' => 'http://example.com/4.png'], + ['id' => 6, 'folder' => null, 'top_folder' => null, 'unread' => 0, 'updated' => "2010-02-12 20:08:47", 'err_msg' => '', 'title' => 'Eurogamer', 'url' => "http://example.com/6", 'favicon' => 'http://example.com/6.png'], + ['id' => 1, 'folder' => 2, 'top_folder' => 1, 'unread' => 5, 'updated' => "2017-09-15 22:54:16", 'err_msg' => '', 'title' => 'NASA JPL', 'url' => "http://example.com/1", 'favicon' => null], + ['id' => 5, 'folder' => 6, 'top_folder' => 3, 'unread' => 12, 'updated' => "2017-07-07 17:07:17", 'err_msg' => '', 'title' => 'Ottawa Citizen', 'url' => "http://example.com/5", 'favicon' => ''], + ['id' => 2, 'folder' => 5, 'top_folder' => 3, 'unread' => 10, 'updated' => "2011-11-11 11:11:11", 'err_msg' => 'oops', 'title' => 'Toronto Star', 'url' => "http://example.com/2", 'favicon' => 'http://example.com/2.png'], + ]; + protected $tags = [ + ['id' => 1, 'name' => "Canada", 'subscription' => 2], + ['id' => 1, 'name' => "Canada", 'subscription' => 4], + ['id' => 1, 'name' => "Canada", 'subscription' => 5], + ['id' => 2, 'name' => "Politics", 'subscription' => 4], + ['id' => 2, 'name' => "Politics", 'subscription' => 5], + ['id' => 3, 'name' => "Science, etc", 'subscription' => 1], + ['id' => 3, 'name' => "Science, etc", 'subscription' => 3], + // Eurogamer is untagged + ]; + protected $serialization = << + + + + + + + + + + + + + + + + + + + + + + +OPML_EXPORT_SERIALIZATION; + protected $serializationFlat = << + + + + + + + + + + + +OPML_EXPORT_SERIALIZATION; + + public function setUp() { + Arsse::$db = \Phake::mock(\JKingWeb\Arsse\Database::class); + } + + public function testExportToOpml() { + \Phake::when(Arsse::$db)->folderList("john.doe@example.com")->thenReturn(new Result($this->folders)); + \Phake::when(Arsse::$db)->subscriptionList("john.doe@example.com")->thenReturn(new Result($this->subscriptions)); + \Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags)); + $this->assertXmlStringEqualsXmlString($this->serialization, (new OPML)->export("john.doe@example.com")); + } + + public function testExportToFlatOpml() { + \Phake::when(Arsse::$db)->folderList("john.doe@example.com")->thenReturn(new Result($this->folders)); + \Phake::when(Arsse::$db)->subscriptionList("john.doe@example.com")->thenReturn(new Result($this->subscriptions)); + \Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags)); + $this->assertXmlStringEqualsXmlString($this->serializationFlat, (new OPML)->export("john.doe@example.com", true)); + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 7c698ab7..fd5429fa 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -113,5 +113,8 @@ cases/Service/TestService.php cases/CLI/TestCLI.php + + cases/ImportExport/TestOPML.php +