mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Documentation tweaks and CS fixes
This commit is contained in:
parent
6c34bf3c9c
commit
22cdc8916d
11 changed files with 55 additions and 48 deletions
26
README.md
26
README.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
The Arsse is a news aggregator server which implements multiple synchronization protocols, including [version 1.2][NCNv1] of [NextCloud News](https://github.com/nextcloud/news)' protocol and the [Tiny Tiny RSS][TTRSS] protocol ([details](#proto)). Unlike most other aggregator servers, The Arsse does not include a Web front-end (though one is planned as a separate project), and it relies on existing protocols to maximize compatibility with existing clients.
|
||||
|
||||
At present the software should be considered in an "alpha" state: though its core subsystems are covered by unit tests and should be free of major bugs, not everything has been rigorously tested. Additionally, though the NextCloud News protocol is fully supported, many features one would expect from other similar software have yet to be implemented. Areas of future work include:
|
||||
At present the software should be considered in an "alpha" state: though its core subsystems are covered by unit tests and should be free of major bugs, not everything has been rigorously tested. Additionally, many features one would expect from other similar software have yet to be implemented. Areas of future work include:
|
||||
|
||||
- Support for more database engines (PostgreSQL, MySQL, MariaDB)
|
||||
- Providing more sync protocols (Google Reader, Fever, others)
|
||||
|
@ -14,7 +14,7 @@ At present the software should be considered in an "alpha" state: though its cor
|
|||
The Arsse has the following requirements:
|
||||
|
||||
- A Linux server utilizing systemd and Nginx (tested on Ubuntu 16.04)
|
||||
- PHP 7.0.7 or newer with the following extensions:
|
||||
- PHP 7.0.7 or later with the following extensions:
|
||||
- [intl](http://php.net/manual/en/book.intl.php), [json](http://php.net/manual/en/book.json.php), [hash](http://php.net/manual/en/book.hash.php), and [pcre](http://php.net/manual/en/book.pcre.php)
|
||||
- [dom](http://php.net/manual/en/book.dom.php), [simplexml](http://php.net/manual/en/book.simplexml.php), and [iconv](http://php.net/manual/en/book.iconv.php) (for picoFeed)
|
||||
- [sqlite3](http://php.net/manual/en/book.sqlite3.php)
|
||||
|
@ -62,7 +62,7 @@ php ./arsse.php conf save-defaults "./config.defaults.php"
|
|||
|
||||
## License
|
||||
|
||||
The Arsse is made available under the permissive MIT license. See the `LICENSE` file included with the distribution or source code for exact legal text. Dependencies included in the distribution may be governed by other licenses.
|
||||
The Arsse is made available under the permissive MIT license. See the `LICENSE` and `AUTHORS` files included with the distribution or source code for exact legal text and copyright holders. Dependencies included in the distribution may be governed by other licenses.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -76,11 +76,11 @@ Please refer to `CONTRIBUTING.md` for guidelines on contributing code to The Ars
|
|||
|
||||
The Arsse does not guarantee it will handle type casting of input in the same way as reference implementations for its supported protocols. As a general rule, clients should endeavour to send only correct input.
|
||||
|
||||
The Arsse _does_, however, guarantee output to be of the same type. If it is not, this is [a bug][newIssue] and should be reported.
|
||||
The Arsse does, however, guarantee _output_ to be of the same type. If it is not, this is [a bug][newIssue] and should be reported.
|
||||
|
||||
#### Content sanitization
|
||||
|
||||
The Arsse makes use of the [picoFeed](https://github.com/miniflux/picoFeed/) newsfeed parsing library to sanitize article content. The exact sanitization rules may differ from those of reference implementations for protocols The Arsse supports.
|
||||
The Arsse makes use of the [picoFeed](https://github.com/miniflux/picoFeed/) newsfeed parsing library to sanitize article content. The exact sanitization parameters may differ from those of reference implementations for protocols The Arsse supports.
|
||||
|
||||
### <a name="proto-ncnv1"></a> NextCloud News v1.2
|
||||
|
||||
|
@ -97,18 +97,18 @@ As a general rule, The Arsse should yield the same output as the reference imple
|
|||
- When marking articles as starred the feed ID is ignored, as they are not needed to establish uniqueness
|
||||
- The feed updater ignores the `userId` parameter: feeds in The Arsse are deduplicated, and have no owner
|
||||
- The `/feeds/all` route lists only feeds which should be checked for updates, and it also returns all `userId` attributes as empty strings: feeds in The Arsse are deduplicated, and have no owner
|
||||
- The updater console commands mentioned in the protocol specification are not implemented
|
||||
- The updater console commands mentioned in the protocol specification are not implemented, as The Arsse does not implement the required NextCloud subsystems
|
||||
- The `lastLoginTimestamp` attribute of the user metadata is always the current time: The Arsse's implementation of the protocol is fully stateless
|
||||
|
||||
#### Ambiguities
|
||||
|
||||
- [The protocol][NCNv1] does not specify an output character encoding, but the reference server uses UTF-8; The Arsse also uses UTF-8
|
||||
- The protocol specifies that GET parameters are treated "the same" as request body parameters; it does not specify what to do in cases where they conflict. The Arsse chooses to give GET parameters precedence
|
||||
- The protocol does not define validity of folder and names other than to say that the empty string is invalid. The Arsse further considers any string composed only of whitesapce to be invalid
|
||||
- The protocol does not specify a return code for bulk-marking operations without a `newestItemId` provided; The Arsse returns `422`
|
||||
- The protocol does not specify what should be done when creating a feed in a folder which does not exist; the Arsse adds the feed to the root folder
|
||||
- The protocol does not specify what should be done when moving a feed to a folder which does not exist; The Arsse return `422`
|
||||
- The protocol does not specify what should be done when renaming a feed to an invalid title, nor what constitutes an invalid title; The Arsse uses the same rules as it does for folders, and returns `422` in cases of rejection
|
||||
- NCN does not specify an output character encoding, but the reference server uses UTF-8; The Arsse also uses UTF-8
|
||||
- NCN specifies that GET parameters are treated "the same" as request body parameters; it does not specify what to do in cases where they conflict. The Arsse chooses to give GET parameters precedence
|
||||
- NCN does not define validity of folder and names other than to say that the empty string is invalid. The Arsse further considers any string composed only of whitesapce to be invalid
|
||||
- NCN does not specify a return code for bulk-marking operations without a `newestItemId` provided; The Arsse returns `422`
|
||||
- NCN does not specify what should be done when creating a feed in a folder which does not exist; the Arsse adds the feed to the root folder
|
||||
- NCN does not specify what should be done when moving a feed to a folder which does not exist; The Arsse return `422`
|
||||
- NCN does not specify what should be done when renaming a feed to an invalid title, nor what constitutes an invalid title; The Arsse uses the same rules as it does for folders, and returns `422` in cases of rejection
|
||||
|
||||
### <a name="proto-ttrss"></a> Tiny Tiny RSS
|
||||
|
||||
|
|
|
@ -1004,17 +1004,17 @@ class Database {
|
|||
switch ($fields) {
|
||||
// NOTE: the cases all cascade into each other: a given verbosity level is always a superset of the previous one
|
||||
case self::LIST_FULL: // everything
|
||||
$columns = array_merge($columns,[
|
||||
$columns = array_merge($columns, [
|
||||
"(select note from arsse_marks where article is arsse_articles.id and subscription in (select sub from subscribed_feeds)) as note",
|
||||
]);
|
||||
case self::LIST_TYPICAL: // conservative, plus content
|
||||
$columns = array_merge($columns,[
|
||||
$columns = array_merge($columns, [
|
||||
"content",
|
||||
"arsse_enclosures.url as media_url", // enclosures are potentially large due to data: URLs
|
||||
"arsse_enclosures.type as media_type", // FIXME: enclosures should eventually have their own fetch method
|
||||
]);
|
||||
case self::LIST_CONSERVATIVE: // base metadata, plus anything that is not likely to be large text
|
||||
$columns = array_merge($columns,[
|
||||
$columns = array_merge($columns, [
|
||||
"arsse_articles.url as url",
|
||||
"arsse_articles.title as title",
|
||||
"(select coalesce(arsse_subscriptions.title,arsse_feeds.title) from arsse_feeds join arsse_subscriptions on arsse_subscriptions.feed is arsse_feeds.id where arsse_feeds.id is arsse_articles.feed) as subscription_title",
|
||||
|
@ -1025,7 +1025,7 @@ class Database {
|
|||
"url_title_hash||':'||url_content_hash||':'||title_content_hash as fingerprint",
|
||||
]);
|
||||
case self::LIST_MINIMAL: // base metadata (always included: required for context matching)
|
||||
$columns = array_merge($columns,[
|
||||
$columns = array_merge($columns, [
|
||||
// id, subscription, feed, modified_date, marked_date, unread, starred, edition
|
||||
"edited as edited_date",
|
||||
]);
|
||||
|
|
|
@ -16,7 +16,9 @@ class ResultAggregate extends AbstractResult {
|
|||
// actual public methods
|
||||
|
||||
public function changes() {
|
||||
return array_reduce($this->data, function($sum, $value) {return $sum + $value->changes();}, 0);
|
||||
return array_reduce($this->data, function ($sum, $value) {
|
||||
return $sum + $value->changes();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public function lastId() {
|
||||
|
|
|
@ -333,7 +333,9 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
'id' => "FEED:".self::FEED_ALL,
|
||||
'bare_id' => self::FEED_ALL,
|
||||
'icon' => "images/folder.png",
|
||||
'unread' => array_reduce($subs, function($sum, $value) {return $sum + $value['unread'];}, 0), // the sum of all feeds' unread is the total unread
|
||||
'unread' => array_reduce($subs, function ($sum, $value) {
|
||||
return $sum + $value['unread'];
|
||||
}, 0), // the sum of all feeds' unread is the total unread
|
||||
], $tSpecial),
|
||||
array_merge([ // Fresh articles
|
||||
'name' => Arsse::$lang->msg("API.TTRSS.Feed.Fresh"),
|
||||
|
@ -682,7 +684,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
$archived = 0; // the archived feed is non-functional in the TT-RSS protocol itself
|
||||
// build the list; exclude anything with zero unread if requested
|
||||
if (!$unread || $starred) {
|
||||
$out[] = [
|
||||
$out[] = [
|
||||
'id' => self::FEED_STARRED,
|
||||
'title' => Arsse::$lang->msg("API.TTRSS.Feed.Starred"),
|
||||
'unread' => (string) $starred, // output is a string in TTRSS
|
||||
|
|
|
@ -10,8 +10,6 @@ use JKingWeb\Arsse\Arsse;
|
|||
use JKingWeb\Arsse\REST\Response;
|
||||
|
||||
class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
|
@ -25,7 +23,7 @@ class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
$url = Arsse::$db->subscriptionFavicon((int) $match[1]);
|
||||
if ($url) {
|
||||
// strip out anything after literal line-end characters; this is to mitigate a potential header (e.g. cookie) injection from the URL
|
||||
if (($pos = strpos($url, "\r")) !== FALSE || ($pos = strpos($url, "\n")) !== FALSE) {
|
||||
if (($pos = strpos($url, "\r")) !== false || ($pos = strpos($url, "\n")) !== false) {
|
||||
$url = substr($url, 0, $pos);
|
||||
}
|
||||
return new Response(301, "", "", ["Location: $url"]);
|
||||
|
|
|
@ -6,7 +6,6 @@ use JKingWeb\Arsse\Test\Result;
|
|||
|
||||
/** @covers \JKingWeb\Arsse\Db\ResultAggregate<extended> */
|
||||
class TestResultAggregate extends Test\AbstractTest {
|
||||
|
||||
public function testGetChangeCountAndLastInsertId() {
|
||||
$in = [
|
||||
new Result([], 3, 4),
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace JKingWeb\Arsse;
|
|||
|
||||
/** @covers \JKingWeb\Arsse\Db\ResultEmpty<extended> */
|
||||
class TestResultEmpty extends Test\AbstractTest {
|
||||
|
||||
public function testGetChangeCountAndLastInsertId() {
|
||||
$r = new Db\ResultEmpty;
|
||||
$this->assertEquals(0, $r->changes());
|
||||
|
|
|
@ -942,9 +942,9 @@ LONG_STRING;
|
|||
|
||||
public function testAssignArticlesToALabel() {
|
||||
$list = [
|
||||
range(1,100),
|
||||
range(1,50),
|
||||
range(51,100),
|
||||
range(1, 100),
|
||||
range(1, 50),
|
||||
range(51, 100),
|
||||
];
|
||||
$in = [
|
||||
['op' => "setArticleLabel", 'sid' => "PriestsOfSyrinx", 'label_id' => -2112, 'article_ids' => implode(",", $list[0])],
|
||||
|
@ -1184,11 +1184,15 @@ LONG_STRING;
|
|||
}
|
||||
|
||||
protected function filterFolders(int $id = null): array {
|
||||
return array_filter($this->folders, function($value) use ($id) {return $value['parent']==$id;});
|
||||
return array_filter($this->folders, function ($value) use ($id) {
|
||||
return $value['parent']==$id;
|
||||
});
|
||||
}
|
||||
|
||||
protected function filterSubs(int $folder = null): array {
|
||||
return array_filter($this->subscriptions, function($value) use ($folder) {return $value['folder']==$folder;});
|
||||
return array_filter($this->subscriptions, function ($value) use ($folder) {
|
||||
return $value['folder']==$folder;
|
||||
});
|
||||
}
|
||||
|
||||
protected function reduceFolders(int $id = null) : int {
|
||||
|
@ -1196,7 +1200,11 @@ LONG_STRING;
|
|||
foreach ($this->filterFolders($id) as $f) {
|
||||
$out += $this->reduceFolders($f['id']);
|
||||
}
|
||||
$out += array_reduce(array_filter($this->subscriptions, function($value) use ($id) {return $value['folder']==$id;}), function($sum, $value) {return $sum + $value['unread'];}, 0);
|
||||
$out += array_reduce(array_filter($this->subscriptions, function ($value) use ($id) {
|
||||
return $value['folder']==$id;
|
||||
}), function ($sum, $value) {
|
||||
return $sum + $value['unread'];
|
||||
}, 0);
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
|
|
@ -289,7 +289,6 @@ trait SeriesSubscription {
|
|||
],
|
||||
];
|
||||
$this->assertResult($exp, Arsse::$db->subscriptionList($this->user, 2));
|
||||
|
||||
}
|
||||
|
||||
public function testListSubscriptionsInAMissingFolder() {
|
||||
|
@ -412,15 +411,15 @@ trait SeriesSubscription {
|
|||
$exp = "http://example.com/favicon.ico";
|
||||
$this->assertSame($exp, Arsse::$db->subscriptionFavicon(1));
|
||||
$this->assertSame($exp, Arsse::$db->subscriptionFavicon(2));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(3));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(4));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(3));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(4));
|
||||
// authorization shouldn't have any bearing on this function
|
||||
Phake::when(Arsse::$user)->authorize->thenReturn(false);
|
||||
$this->assertSame($exp, Arsse::$db->subscriptionFavicon(1));
|
||||
$this->assertSame($exp, Arsse::$db->subscriptionFavicon(2));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(3));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(4));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(3));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(4));
|
||||
// invalid IDs should simply return an empty string
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(-2112));
|
||||
$this->assertSame('', Arsse::$db->subscriptionFavicon(-2112));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue