mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Add export-to-file wrapper for OPML
This commit is contained in:
parent
35e79d53a9
commit
deea294f8a
7 changed files with 128 additions and 0 deletions
|
@ -86,6 +86,8 @@ abstract class AbstractException extends \Exception {
|
||||||
"Feed/Exception.xmlEntity" => 10512,
|
"Feed/Exception.xmlEntity" => 10512,
|
||||||
"Feed/Exception.subscriptionNotFound" => 10521,
|
"Feed/Exception.subscriptionNotFound" => 10521,
|
||||||
"Feed/Exception.unsupportedFeedFormat" => 10522,
|
"Feed/Exception.unsupportedFeedFormat" => 10522,
|
||||||
|
"ImportExport/Exception.fileUnwritable" => 10604,
|
||||||
|
"ImportExport/Exception.fileUncreatable" => 10605,
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
||||||
|
|
10
lib/ImportExport/Exception.php
Normal file
10
lib/ImportExport/Exception.php
Normal file
|
@ -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 {
|
||||||
|
}
|
|
@ -7,9 +7,13 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\ImportExport;
|
namespace JKingWeb\Arsse\ImportExport;
|
||||||
|
|
||||||
use JKingWeb\Arsse\Arsse;
|
use JKingWeb\Arsse\Arsse;
|
||||||
|
use JKingWeb\Arsse\User\Exception as UserException;
|
||||||
|
|
||||||
class OPML {
|
class OPML {
|
||||||
public function export(string $user, bool $flat = false): string {
|
public function export(string $user, bool $flat = false): string {
|
||||||
|
if (!Arsse::$user->exists($user)) {
|
||||||
|
throw new UserException("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
}
|
||||||
$tags = [];
|
$tags = [];
|
||||||
$folders = [];
|
$folders = [];
|
||||||
$parents = [0 => null];
|
$parents = [0 => null];
|
||||||
|
@ -68,4 +72,14 @@ class OPML {
|
||||||
// return the serialization
|
// return the serialization
|
||||||
return $document->saveXML();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.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.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/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}"',
|
||||||
];
|
];
|
||||||
|
|
|
@ -79,7 +79,10 @@ OPML_EXPORT_SERIALIZATION;
|
||||||
OPML_EXPORT_SERIALIZATION;
|
OPML_EXPORT_SERIALIZATION;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
|
self::clearData();
|
||||||
Arsse::$db = \Phake::mock(\JKingWeb\Arsse\Database::class);
|
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() {
|
public function testExportToOpml() {
|
||||||
|
@ -95,4 +98,10 @@ OPML_EXPORT_SERIALIZATION;
|
||||||
\Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags));
|
\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));
|
$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
Normal file
82
tests/cases/ImportExport/TestOPMLFile.php
Normal file
|
@ -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],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,6 +115,7 @@
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="Import/Export">
|
<testsuite name="Import/Export">
|
||||||
<file>cases/ImportExport/TestOPML.php</file>
|
<file>cases/ImportExport/TestOPML.php</file>
|
||||||
|
<file>cases/ImportExport/TestOPMLFile.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
Loading…
Reference in a new issue