Browse Source

Add export-to-file wrapper for OPML

microsub
J. King 5 years ago
parent
commit
deea294f8a
  1. 2
      lib/AbstractException.php
  2. 10
      lib/ImportExport/Exception.php
  3. 14
      lib/ImportExport/OPML.php
  4. 10
      locale/en.php
  5. 9
      tests/cases/ImportExport/TestOPML.php
  6. 82
      tests/cases/ImportExport/TestOPMLFile.php
  7. 1
      tests/phpunit.xml

2
lib/AbstractException.php

@ -86,6 +86,8 @@ abstract class AbstractException extends \Exception {
"Feed/Exception.xmlEntity" => 10512,
"Feed/Exception.subscriptionNotFound" => 10521,
"Feed/Exception.unsupportedFeedFormat" => 10522,
"ImportExport/Exception.fileUnwritable" => 10604,
"ImportExport/Exception.fileUncreatable" => 10605,
];
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {

10
lib/ImportExport/Exception.php

@ -0,0 +1,10 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\ImportExport;
class Exception extends \JKingWeb\Arsse\AbstractException {
}

14
lib/ImportExport/OPML.php

@ -7,9 +7,13 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\ImportExport;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\User\Exception as UserException;
class OPML {
public function export(string $user, bool $flat = false): string {
if (!Arsse::$user->exists($user)) {
throw new UserException("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
$tags = [];
$folders = [];
$parents = [0 => null];
@ -68,4 +72,14 @@ class OPML {
// return the serialization
return $document->saveXML();
}
public function exportFile(string $file, string $user, bool $flat = false): bool {
$data = $this->export($user, $flat);
if (!@file_put_contents($file, $data)) {
// if it fails throw an exception
$err = file_exists($file) ? "fileUnwritable" : "fileUncreatable";
throw new Exception($err, ['file' => $file, 'format' => str_replace(__NAMESPACE__."\\", "", __CLASS__)]);
}
return true;
}
}

10
locale/en.php

@ -155,4 +155,14 @@ return [
'Exception.JKingWeb/Arsse/Feed/Exception.xmlEntity' => 'Refused to parse feed "{url}" because it contains an XXE attack',
'Exception.JKingWeb/Arsse/Feed/Exception.subscriptionNotFound' => 'Unable to find a feed at location "{url}"',
'Exception.JKingWeb/Arsse/Feed/Exception.unsupportedFeedFormat' => 'Feed "{url}" is of an unsupported format',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUncreatable' =>
'Insufficient permissions to write {type, select,
OPML {OPML}
other {"{type}"}
} export to file "{file}"',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUnwritable' =>
'Insufficient permissions to write {type, select,
OPML {OPML}
other {"{type}"}
} export to existing file "{file}"',
];

9
tests/cases/ImportExport/TestOPML.php

@ -79,7 +79,10 @@ OPML_EXPORT_SERIALIZATION;
OPML_EXPORT_SERIALIZATION;
public function setUp() {
self::clearData();
Arsse::$db = \Phake::mock(\JKingWeb\Arsse\Database::class);
Arsse::$user = \Phake::mock(\JKingWeb\Arsse\User::class);
\Phake::when(Arsse::$user)->exists->thenReturn(true);
}
public function testExportToOpml() {
@ -95,4 +98,10 @@ OPML_EXPORT_SERIALIZATION;
\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));
}
public function testExportToOpmlAMissingUser() {
\Phake::when(Arsse::$user)->exists->thenReturn(false);
$this->assertException("doesNotExist", "User");
(new OPML)->export("john.doe@example.com");
}
}

82
tests/cases/ImportExport/TestOPMLFile.php

@ -0,0 +1,82 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\ImportExport;
use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\Exception;
use org\bovigo\vfs\vfsStream;
/** @covers \JKingWeb\Arsse\ImportExport\OPML<extended> */
class TestOPMLFile extends \JKingWeb\Arsse\Test\AbstractTest {
protected $vfs;
protected $path;
protected $opml;
public function setUp() {
self::clearData();
// create a mock OPML processor with stubbed underlying import/export routines
$this->opml = \Phake::partialMock(OPML::class);
\Phake::when($this->opml)->export->thenReturn("OPML_FILE");
$this->vfs = vfsStream::setup("root", null, [
'exportGoodFile' => "",
'exportGoodDir' => [],
'exportBadFile' => "",
'exportBadDir' => [],
]);
$this->path = $this->vfs->url()."/";
// make the "bad" entries inaccessible
chmod($this->path."exportBadFile", 0000);
chmod($this->path."exportBadDir", 0000);
}
public function tearDown() {
$this->path = null;
$this->vfs = null;
$this->opml = null;
self::clearData();
}
/** @dataProvider provideFileExports */
public function testExportOpmlToAFile(string $file, string $user, bool $flat, $exp) {
$path = $this->path.$file;
try {
if ($exp instanceof \JKingWeb\Arsse\AbstractException) {
$this->assertException($exp);
$this->opml->exportFile($path, $user, $flat);
} else {
$this->assertSame($exp, $this->opml->exportFile($path, $user, $flat));
$this->assertSame("OPML_FILE", $this->vfs->getChild($file)->getContent());
}
} finally {
\Phake::verify($this->opml)->export($user, $flat);
}
}
public function provideFileExports() {
$createException = new Exception("fileUncreatable");
$writeException = new Exception("fileUnwritable");
return [
["exportGoodFile", "john.doe@example.com", true, true],
["exportGoodFile", "john.doe@example.com", false, true],
["exportGoodFile", "jane.doe@example.com", true, true],
["exportGoodFile", "jane.doe@example.com", false, true],
["exportGoodDir/file", "john.doe@example.com", true, true],
["exportGoodDir/file", "john.doe@example.com", false, true],
["exportGoodDir/file", "jane.doe@example.com", true, true],
["exportGoodDir/file", "jane.doe@example.com", false, true],
["exportBadFile", "john.doe@example.com", true, $writeException],
["exportBadFile", "john.doe@example.com", false, $writeException],
["exportBadFile", "jane.doe@example.com", true, $writeException],
["exportBadFile", "jane.doe@example.com", false, $writeException],
["exportBadDir/file", "john.doe@example.com", true, $createException],
["exportBadDir/file", "john.doe@example.com", false, $createException],
["exportBadDir/file", "jane.doe@example.com", true, $createException],
["exportBadDir/file", "jane.doe@example.com", false, $createException],
];
}
}

1
tests/phpunit.xml

@ -115,6 +115,7 @@
</testsuite>
<testsuite name="Import/Export">
<file>cases/ImportExport/TestOPML.php</file>
<file>cases/ImportExport/TestOPMLFile.php</file>
</testsuite>
</testsuites>
</phpunit>

Loading…
Cancel
Save