Browse Source

Prototype OPML handling

rpm
J. King 3 years ago
parent
commit
b4ae988b79
  1. 1
      docs/en/030_Supported_Protocols/005_Miniflux.md
  2. 13
      lib/AbstractException.php
  3. 40
      lib/REST/Miniflux/V1.php
  4. 7
      locale/en.php

1
docs/en/030_Supported_Protocols/005_Miniflux.md

@ -42,6 +42,7 @@ Miniflux version 2.0.28 is emulated, though not all features are implemented
- Creating a feed with the `scrape` property set to `true` might not return scraped content for the initial synchronization
- Querying articles for both read/unread and removed statuses will not return all removed articles
- Search strings will match partial words
- OPML import either succeeds or fails atomically: if one feed fails, no feeds are imported
# Behaviour of filtering (block and keep) rules

13
lib/AbstractException.php

@ -104,7 +104,12 @@ abstract class AbstractException extends \Exception {
"Rule/Exception.invalidPattern" => 10701,
];
protected $symbol;
protected $params;
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
$this->symbol = $msgID;
$this->params = $vars ?? [];
if ($msgID === "") {
$msg = "Exception.unknown";
$code = 10000;
@ -121,4 +126,12 @@ abstract class AbstractException extends \Exception {
}
parent::__construct($msg, $code, $e);
}
public function getSymbol(): string {
return $this->symbol;
}
public function getParams(): array {
return $this->aparams;
}
}

40
lib/REST/Miniflux/V1.php

@ -13,7 +13,8 @@ use JKingWeb\Arsse\Feed\Exception as FeedException;
use JKingWeb\Arsse\AbstractException;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\Exception as ImportException;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\URL;
use JKingWeb\Arsse\Misc\ValueInfo as V;
@ -25,6 +26,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\EmptyResponse;
use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\TextResponse as GenericResponse;
use Laminas\Diactoros\Uri;
class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
@ -141,8 +143,8 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
'GET' => ["getCategoryFeeds", false, true, false, false, []],
],
'/categories/1/mark-all-as-read' => [
'PUT' => ["markCategory", false, true, false, false, []],
],
'PUT' => ["markCategory", false, true, false, false, []],
'/discover' => [
'POST' => ["discoverSubscriptions", false, false, true, false, ["url"]],
],
@ -212,6 +214,11 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
public function __construct() {
}
/** @codeCoverageIgnore */
protected function getInstance(string $class) {
return new $class;
}
protected function authenticate(ServerRequestInterface $req): bool {
// first check any tokens; this is what Miniflux does
if ($req->hasHeader("X-Auth-Token")) {
@ -261,9 +268,6 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
}
if ($reqBody) {
if ($func === "opmlImport") {
if (!HTTP::matchType($req, "", ...[self::ACCEPTED_TYPES_OPML])) {
return new ErrorResponse("", 415, ['Accept' => implode(", ", self::ACCEPTED_TYPES_OPML)]);
}
$args[] = (string) $req->getBody();
} else {
$data = (string) $req->getBody();
@ -1177,6 +1181,32 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new EmptyResponse(204);
}
protected function opmlImport(string $data): ResponseInterface {
try {
$this->getInstance(OPML::class)->import(Arsse::$user->id, $data);
} catch (ImportException $e) {
switch ($e->getCode()) {
case 10611:
return new ErrorResponse("InvalidBodyXML", 400);
case 10612:
return new ErrorResponse("InvalidBodyOPML", 422);
case 10613:
return new ErrorResponse("InvalidImportCategory", 422);
case 10614:
return new ErrorResponse("DuplicateImportCatgory", 422);
case 10615:
return new ErrorResponse("InvalidImportLabel", 422);
}
} catch (FeedException $e) {
return new ErrorResponse(["FailedImportFeed", 'url' => $e->getParams()['url'], 'code' => $e->getCode()], 502);
}
return new Response(['message' => Arsse::$lang->msg("ImportSuccess")]);
}
protected function opmlExport(): ResponseInterface {
return new GenericResponse($this->getInstance(OPML::class)->export(Arsse::$user->id), 200, ['Content-Type' => "application/xml"]);
}
public static function tokenGenerate(string $user, string $label): string {
// Miniflux produces tokenss in base64url alphabet
$t = str_replace(["+", "/"], ["-", "_"], base64_encode(random_bytes(self::TOKEN_LENGTH)));

7
locale/en.php

@ -8,12 +8,15 @@ return [
'CLI.Auth.Failure' => 'Authentication failed',
'API.Miniflux.DefaultCategoryName' => "All",
'API.Miniflux.ImportSuccess' => 'Feeds imported successfully',
'API.Miniflux.Error.401' => 'Access Unauthorized',
'API.Miniflux.Error.403' => 'Access Forbidden',
'API.Miniflux.Error.404' => 'Resource Not Found',
'API.Miniflux.Error.MissingInputValue' => 'Required key "{field}" was not present in input',
'API.Miniflux.Error.DuplicateInputValue' => 'Key "{field}" accepts only one value',
'API.Miniflux.Error.InvalidBodyJSON' => 'Invalid JSON payload: {0}',
'API.Miniflux.Error.InvalidBodyXML' => 'Invalid XML payload',
'API.Miniflux.Error.InvalidBodyOPML' => 'Payload is not a valid OPML document',
'API.Miniflux.Error.InvalidInputType' => 'Input key "{field}" of type {actual} was expected as {expected}',
'API.Miniflux.Error.InvalidInputValue' => 'Supplied value is not valid for input key "{field}"',
'API.Miniflux.Error.Fetch404' => 'Resource not found (404), this feed doesn\'t exists anymore, check the feed URL',
@ -28,6 +31,10 @@ return [
'API.Miniflux.Error.DuplicateUser' => 'The user name "{user}" already exists',
'API.Miniflux.Error.DuplicateFeed' => 'This feed already exists.',
'API.Miniflux.Error.InvalidTitle' => 'Invalid feed title',
'API.Miniflux.Error.InvalidImportCategory' => 'Payload contains an invalid category name',
'API.Miniflux.Error.DuplicateImportCategory' => 'Payload contains the same category name twice',
'API.Miniflux.Error.FailedImportFeed' => 'Unable to import feed at URL "{url}" (code {code}',
'API.Miniflux.Error.InvalidImportLabel' => 'Payload contains an invalid label name',
'API.TTRSS.Category.Uncategorized' => 'Uncategorized',
'API.TTRSS.Category.Special' => 'Special',

Loading…
Cancel
Save