1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Fix up the rest of the subscriptionUpdate function

This commit is contained in:
J. King 2022-12-27 14:20:13 -05:00
parent cc2f3ea996
commit 7414d3844e
3 changed files with 29 additions and 96 deletions

View file

@ -1211,21 +1211,21 @@ class Database {
/** Attempts to refresh a subscribed newsfeed, returning an indication of success /** Attempts to refresh a subscribed newsfeed, returning an indication of success
* *
* @param string|null $user The user whose subscribed newsfeed is to be updated; this may be null to facilitate refreshing feeds from the CLI * @param string|null $user The user whose subscribed newsfeed is to be updated; this may be null to facilitate refreshing feeds from the CLI
* @param integer $id The numerical identifier of the subscription to refresh * @param integer $subID The numerical identifier of the subscription to refresh
* @param boolean $throwError Whether to throw an exception on failure in addition to storing error information in the database * @param boolean $throwError Whether to throw an exception on failure in addition to storing error information in the database
*/ */
public function subscriptionUpdate(?string $user, $id, bool $throwError = false): bool { public function subscriptionUpdate(?string $user, $subID, bool $throwError = false): bool {
// check to make sure the feed exists // check to make sure the feed exists
if (!V::id($id)) { if (!V::id($subID)) {
throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id, 'type' => "int > 0"]); throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id, 'type' => "int > 0"]);
} }
$f = $this->db->prepareArray( $f = $this->db->prepareArray(
"SELECT "SELECT
url, las_mod as modified, etag, err_count, scrape as scrapers url, las_mod as modified, etag, err_count, scrape as scrapers, keep_rule, block_rule
FROM arsse_subscriptions FROM arsse_subscriptions
where id = ?", where id = ?",
["int"] ["int"]
)->run($id)->getRow(); )->run($subID)->getRow();
if (!$f) { if (!$f) {
throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]); throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]);
} }
@ -1235,10 +1235,10 @@ class Database {
// here. When an exception is thrown it should update the database with the // here. When an exception is thrown it should update the database with the
// error instead of failing; if other exceptions are thrown, we should simply roll back // error instead of failing; if other exceptions are thrown, we should simply roll back
try { try {
$feed = new Feed((int) $id, $f['url'], (string) Date::transform($f['modified'], "http", "sql"), $f['etag'], $f['username'], $f['password'], $scrape); $feed = new Feed((int) $subID, $f['url'], (string) Date::transform($f['modified'], "http", "sql"), $f['etag'], $f['username'], $f['password'], $scrape);
if (!$feed->modified) { if (!$feed->modified) {
// if the feed hasn't changed, just compute the next fetch time and record it // if the feed hasn't changed, just compute the next fetch time and record it
$this->db->prepare("UPDATE arsse_subscriptions SET updated = CURRENT_TIMESTAMP, next_fetch = ? WHERE id = ?", 'datetime', 'int')->run($feed->nextFetch, $id); $this->db->prepare("UPDATE arsse_subscriptions SET updated = CURRENT_TIMESTAMP, next_fetch = ? WHERE id = ?", 'datetime', 'int')->run($feed->nextFetch, $subID);
return false; return false;
} }
} catch (Feed\Exception $e) { } catch (Feed\Exception $e) {
@ -1246,10 +1246,10 @@ class Database {
$this->db->prepareArray( $this->db->prepareArray(
"UPDATE arsse_subscriptions SET updated = CURRENT_TIMESTAMP, next_fetch = ?, err_count = err_count + 1, err_msg = ? WHERE id = ?", "UPDATE arsse_subscriptions SET updated = CURRENT_TIMESTAMP, next_fetch = ?, err_count = err_count + 1, err_msg = ? WHERE id = ?",
['datetime', 'str', 'int'] ['datetime', 'str', 'int']
)->run(Feed::nextFetchOnError($f['err_count']), $e->getMessage(), $id); )->run(Feed::nextFetchOnError($f['err_count']), $e->getMessage(), $subID);
if ($throwError) { if ($throwError) {
throw $e; throw $e;
} }
return false; return false;
} }
//prepare the necessary statements to perform the update //prepare the necessary statements to perform the update
@ -1260,19 +1260,29 @@ class Database {
} }
if (sizeof($feed->newItems)) { if (sizeof($feed->newItems)) {
$qInsertArticle = $this->db->prepareArray( $qInsertArticle = $this->db->prepareArray(
"INSERT INTO arsse_articles(url,title,author,published,edited,guid,content,url_title_hash,url_content_hash,title_content_hash,feed,content_scraped) values(?,?,?,?,?,?,?,?,?,?,?,?)", "INSERT INTO arsse_articles(url,title,author,published,edited,guid,url_title_hash,url_content_hash,title_content_hash,subscription,hidden) values(?,?,?,?,?,?,?,?,?,?,?)",
["str", "str", "str", "datetime", "datetime", "str", "str", "str", "str", "str", "int", "str"] ["str", "str", "str", "datetime", "datetime", "str", "str", "str", "str", "int", "bool"]
); );
} }
if (sizeof($feed->changedItems)) { if (sizeof($feed->changedItems)) {
$qDeleteEnclosures = $this->db->prepare("DELETE FROM arsse_enclosures WHERE article = ?", 'int'); $qDeleteEnclosures = $this->db->prepare("DELETE FROM arsse_enclosures WHERE article = ?", 'int');
$qDeleteCategories = $this->db->prepare("DELETE FROM arsse_categories WHERE article = ?", 'int'); $qDeleteCategories = $this->db->prepare("DELETE FROM arsse_categories WHERE article = ?", 'int');
$qClearReadMarks = $this->db->prepare("UPDATE arsse_marks SET \"read\" = 0, modified = CURRENT_TIMESTAMP WHERE article = ? and \"read\" = 1", 'int');
$qUpdateArticle = $this->db->prepareArray( $qUpdateArticle = $this->db->prepareArray(
"UPDATE arsse_articles SET url = ?, title = ?, author = ?, published = ?, edited = ?, modified = CURRENT_TIMESTAMP, guid = ?, content = ?, url_title_hash = ?, url_content_hash = ?, title_content_hash = ?, content_scraped = ? WHERE id = ?", "UPDATE arsse_articles SET \"read\" = 0, hidden = ?, url = ?, title = ?, author = ?, published = ?, edited = ?, modified = CURRENT_TIMESTAMP, guid = ?, content = ?, url_title_hash = ?, url_content_hash = ?, title_content_hash = ?, content_scraped = ? WHERE id = ?",
["str", "str", "str", "datetime", "datetime", "str", "str", "str", "str", "str", "str", "int"] ["bool", "str", "str", "str", "datetime", "datetime", "str", "str", "str", "str", "str", "str", "int"]
); );
} }
// prepare the keep and block rules
try {
$keep = Rule::prep($f['keep_rule'] ?? "");
} catch (RuleException $e) {
$keep = "";
}
try {
$block = Rule::prep($f['block_rule'] ?? "");
} catch (RuleException $e) {
$block = "";
}
// determine if the feed icon needs to be updated, and update it if appropriate // determine if the feed icon needs to be updated, and update it if appropriate
$tr = $this->db->begin(); $tr = $this->db->begin();
$icon = null; $icon = null;
@ -1299,12 +1309,11 @@ class Database {
$article->publishedDate, $article->publishedDate,
$article->updatedDate, $article->updatedDate,
$article->id, $article->id,
$article->content,
$article->urlTitleHash, $article->urlTitleHash,
$article->urlContentHash, $article->urlContentHash,
$article->titleContentHash, $article->titleContentHash,
$feedID, $subID,
$article->scrapedContent ?? null !Rule::apply($keep, $block, $article->title, $article->categories)
)->lastId(); )->lastId();
// note the new ID for later use // note the new ID for later use
$articleMap[$k] = $articleID; $articleMap[$k] = $articleID;
@ -1322,6 +1331,7 @@ class Database {
// next update existing artricles which have been edited // next update existing artricles which have been edited
foreach ($feed->changedItems as $articleID => $article) { foreach ($feed->changedItems as $articleID => $article) {
$qUpdateArticle->run( $qUpdateArticle->run(
!Rule::apply($keep, $block, $article->title, $article->categories),
$article->url, $article->url,
$article->title, $article->title,
$article->author, $article->author,
@ -1346,34 +1356,10 @@ class Database {
} }
// assign a new edition ID to this version of the article // assign a new edition ID to this version of the article
$qInsertEdition->run($articleID); $qInsertEdition->run($articleID);
$qClearReadMarks->run($articleID);
}
// hide or unhide any filtered articles
foreach ($feed->filteredItems as $user => $filterData) {
$hide = [];
$unhide = [];
foreach ($filterData['new'] as $index => $keep) {
if (!$keep) {
$hide[] = $articleMap[$index];
}
}
foreach ($filterData['changed'] as $article => $keep) {
if (!$keep) {
$hide[] = $article;
} else {
$unhide[] = $article;
}
}
if ($hide) {
$this->articleMark($user, ['hidden' => true], (new Context)->articles($hide), false);
}
if ($unhide) {
$this->articleMark($user, ['hidden' => false], (new Context)->articles($unhide), false);
}
} }
// lastly update the feed database itself with updated information. // lastly update the feed database itself with updated information.
$this->db->prepareArray( $this->db->prepareArray(
"UPDATE arsse_feeds SET title = ?, source = ?, updated = CURRENT_TIMESTAMP, modified = ?, etag = ?, err_count = 0, err_msg = '', next_fetch = ?, size = ?, icon = ? WHERE id = ?", "UPDATE arsse_subscriptions SET feed_title = ?, source = ?, updated = CURRENT_TIMESTAMP, last_mod = ?, etag = ?, err_count = 0, err_msg = '', next_fetch = ?, size = ?, icon = ? WHERE id = ?",
["str", "str", "datetime", "strict str", "datetime", "int", "int", "int"] ["str", "str", "datetime", "strict str", "datetime", "int", "int", "int"]
)->run( )->run(
$feed->data->title, $feed->data->title,
@ -1383,7 +1369,7 @@ class Database {
$feed->nextFetch, $feed->nextFetch,
sizeof($feed->data->items), sizeof($feed->data->items),
$icon, $icon,
$feedID $subID
); );
$tr->commit(); $tr->commit();
return true; return true;
@ -1410,30 +1396,6 @@ class Database {
return $out; return $out;
} }
/** Retrieves the set of filters users have applied to a given feed
*
* The result is an associative array whose keys are usernames, values
* being an array in turn with the following keys:
*
* - "keep": The "keep" rule as a prepared pattern; any articles which fail to match this rule are hidden
* - "block": The block rule as a prepared pattern; any articles which match this rule are hidden
*/
public function feedRulesGet(int $feedID): array {
$out = [];
$result = $this->db->prepare("SELECT owner, coalesce(keep_rule, '') as keep, coalesce(block_rule, '') as block from arsse_subscriptions where feed = ? and (coalesce(keep_rule, '') || coalesce(block_rule, '')) <> '' order by owner", "int")->run($feedID);
foreach ($result as $row) {
try {
$keep = Rule::prep($row['keep']);
$block = Rule::prep($row['block']);
} catch (RuleException $e) {
// invalid rules should not normally appear in the database, but it's possible
continue;
}
$out[$row['owner']] = ['keep' => $keep, 'block' => $block];
}
return $out;
}
/** Retrieves various identifiers for the latest $count articles in the given newsfeed. The identifiers are: /** Retrieves various identifiers for the latest $count articles in the given newsfeed. The identifiers are:
* *
* - "id": The database record key for the article * - "id": The database record key for the article

View file

@ -452,18 +452,4 @@ class Feed {
} }
} }
} }
protected function computeFilterRules(int $feedID): void {
$rules = Arsse::$db->feedRulesGet($feedID);
foreach ($rules as $user => $r) {
$stats = ['new' => [], 'changed' => []];
foreach ($this->newItems as $index => $item) {
$stats['new'][$index] = Rule::apply($r['keep'], $r['block'], $item->title, $item->categories);
}
foreach ($this->changedItems as $index => $item) {
$stats['changed'][$index] = Rule::apply($r['keep'], $r['block'], $item->title, $item->categories);
}
$this->filteredItems[$user] = $stats;
}
}
} }

View file

@ -147,21 +147,6 @@ trait SeriesFeed {
$this->assertResult([['id' => 1]], Arsse::$db->feedMatchIds(1, ['e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda'])); // this ID appears in both feed 1 and feed 2; only one result should be returned $this->assertResult([['id' => 1]], Arsse::$db->feedMatchIds(1, ['e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda'])); // this ID appears in both feed 1 and feed 2; only one result should be returned
} }
/** @dataProvider provideFilterRules */
public function testGetRules(int $in, array $exp): void {
$this->assertSame($exp, Arsse::$db->feedRulesGet($in));
}
public function provideFilterRules(): iterable {
return [
[1, ['jane.doe@example.com' => ['keep' => "`^(?i)[a-z]+`u", 'block' => "`3|6`u"], 'john.doe@example.com' => ['keep' => "", 'block' => "`^Sport$`u"]]],
[2, []],
[3, ['john.doe@example.com' => ['keep' => '`\w+`u', 'block' => ""]]],
[4, []],
[5, ['john.doe@example.com' => ['keep' => "", 'block' => "`and/or`u"]]],
];
}
public function testUpdateAFeed(): void { public function testUpdateAFeed(): void {
// update a valid feed with both new and changed items // update a valid feed with both new and changed items
Arsse::$db->feedUpdate(1); Arsse::$db->feedUpdate(1);