beta4 changes #26

Merged
danieljsummers merged 7 commits from beta5 into main 2024-08-06 23:20:17 +00:00
4 changed files with 78 additions and 84 deletions
Showing only changes of commit adcfd9d02a - Show all commits

12
src/composer.lock generated
View File

@ -8,11 +8,11 @@
"packages": [ "packages": [
{ {
"name": "bit-badger/inspired-by-fsharp", "name": "bit-badger/inspired-by-fsharp",
"version": "v1.0.0-beta1", "version": "v1.0.0-beta2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp", "url": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp",
"reference": "efb3a4461edcb23e0dd82068adeb0591240870b0" "reference": "fad428a4e40b606987499b17bb2d5b7d4b04502d"
}, },
"require": { "require": {
"php": "^8.2" "php": "^8.2"
@ -49,15 +49,15 @@
"rss": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp.rss", "rss": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp.rss",
"source": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp" "source": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp"
}, },
"time": "2024-07-28T21:35:11+00:00" "time": "2024-07-29T17:58:33+00:00"
}, },
{ {
"name": "bit-badger/pdo-document", "name": "bit-badger/pdo-document",
"version": "v1.0.0-beta8", "version": "v1.0.0-beta9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://git.bitbadger.solutions/bit-badger/pdo-document", "url": "https://git.bitbadger.solutions/bit-badger/pdo-document",
"reference": "039283224a173bd3e8a9bc5de0caf349ba7b58e6" "reference": "9e0e663811d9dbbdb94a2c04ce8b874e91a7c85b"
}, },
"require": { "require": {
"bit-badger/inspired-by-fsharp": "^1", "bit-badger/inspired-by-fsharp": "^1",
@ -103,7 +103,7 @@
"rss": "https://git.bitbadger.solutions/bit-badger/pdo-document.rss", "rss": "https://git.bitbadger.solutions/bit-badger/pdo-document.rss",
"source": "https://git.bitbadger.solutions/bit-badger/pdo-document" "source": "https://git.bitbadger.solutions/bit-badger/pdo-document"
}, },
"time": "2024-07-29T00:08:44+00:00" "time": "2024-07-29T20:57:51+00:00"
}, },
{ {
"name": "netresearch/jsonmapper", "name": "netresearch/jsonmapper",

View File

@ -175,34 +175,31 @@ class Feed
* @param int $feedId The ID of the feed to be refreshed * @param int $feedId The ID of the feed to be refreshed
* @param string $url The URL of the feed to be refreshed * @param string $url The URL of the feed to be refreshed
* @return Result<true, string> True if successful, an error message if not * @return Result<true, string> True if successful, an error message if not
* @throws DocumentException If any is encountered
*/ */
public static function refreshFeed(int $feedId, string $url): Result public static function refreshFeed(int $feedId, string $url): Result
{ {
$tryRetrieve = ParsedFeed::retrieve($url); return ParsedFeed::retrieve($url)
if ($tryRetrieve->isError()) return $tryRetrieve; ->bind(function (ParsedFeed $feed) use ($feedId, $url) {
$feed = $tryRetrieve->getOK(); try {
$feedDoc = Find::byId(Table::Feed, $feedId, self::class);
if ($feedDoc->isNone()) return Result::Error('Could not derive date last checked for feed');
$lastChecked = date_create_immutable($feedDoc->get()->checked_on ?? WWW_EPOCH);
try { return self::updateItems($feedId, $feed, $lastChecked)
$feedDoc = Find::byId(Table::Feed, $feedId, self::class); ->bind(function () use ($feed, $feedId, $url) {
if ($feedDoc->isNone()) return Result::Error('Could not derive date last checked for feed'); $patch = [
$lastChecked = date_create_immutable($feedDoc->get()->checked_on ?? WWW_EPOCH); 'title' => $feed->title,
'updated_on' => $feed->updatedOn,
$itemUpdate = self::updateItems($feedId, $feed, $lastChecked); 'checked_on' => Data::formatDate('now')
if ($itemUpdate->isError()) return $itemUpdate; ];
if ($url !== $feed->url) $patch['url'] = $feed->url;
$patch = [ Patch::byId(Table::Feed, $feedId, $patch);
'title' => $feed->title, })
'updated_on' => $feed->updatedOn, ->bind(fn() => PURGE_TYPE === self::PurgeNone ? Result::OK(true) : self::purgeItems($feedId));
'checked_on' => Data::formatDate('now') } catch (DocumentException $ex) {
]; return Result::Error("$ex");
if ($url !== $feed->url) $patch['url'] = $feed->url; }
Patch::byId(Table::Feed, $feedId, $patch); });
} catch (DocumentException $ex) {
return Result::Error("$ex");
}
return PURGE_TYPE === self::PurgeNone ? Result::OK(true) : self::purgeItems($feedId);
} }
/** /**
@ -213,27 +210,25 @@ class Feed
*/ */
public static function add(string $url): Result public static function add(string $url): Result
{ {
$tryRetrieve = ParsedFeed::retrieve($url); return ParsedFeed::retrieve($url)
if ($tryRetrieve->isError()) return $tryRetrieve; ->bind(function (ParsedFeed $feed) {
$feed = $tryRetrieve->getOK(); try {
$fields = [Field::EQ('user_id', $_SESSION[Key::UserId]), Field::EQ('url', $feed->url)];
if (Exists::byFields(Table::Feed, $fields)) {
return Result::Error("Already subscribed to feed $feed->url");
}
try { Document::insert(Table::Feed, self::fromParsed($feed));
$fields = [Field::EQ('user_id', $_SESSION[Key::UserId]), Field::EQ('url', $feed->url)];
if (Exists::byFields(Table::Feed, $fields)) {
return Result::Error("Already subscribed to feed $feed->url");
}
Document::insert(Table::Feed, self::fromParsed($feed)); $doc = Find::firstByFields(Table::Feed, $fields, self::class);
if ($doc->isNone()) return Result::Error('Could not retrieve inserted feed');
$tryDoc = Find::firstByFields(Table::Feed, $fields, self::class); return self::updateItems($doc->get()->id, $feed, date_create_immutable(WWW_EPOCH))
if ($tryDoc->isNone()) return Result::Error('Could not retrieve inserted feed'); ->bind(fn() => Result::OK($doc->get()->id));
$doc = $tryDoc->get(); } catch (DocumentException $ex) {
return Result::Error("$ex");
$result = self::updateItems($doc->id, $feed, date_create_immutable(WWW_EPOCH)); }
return $result->isError() ? $result : Result::OK($doc->id); });
} catch (DocumentException $ex) {
return Result::Error("$ex");
}
} }
/** /**

View File

@ -239,39 +239,38 @@ readonly class ParsedFeed
*/ */
public static function retrieve(string $url): Result public static function retrieve(string $url): Result
{ {
$tryDoc = self::retrieveDocument($url); $doc = self::retrieveDocument($url)
if ($tryDoc->isError()) return $tryDoc; ->bind(fn(array $doc) => match ($doc['code']) {
200 => Result::OK($doc),
$doc = $tryDoc->getOK(); default => Result::Error(
if ($doc['code'] !== 200) { "Prospective feed URL $url returned HTTP Code {$doc['code']}: {$doc['content']}"),
return Result::Error("Prospective feed URL $url returned HTTP Code {$doc['code']}: {$doc['content']}"); })
} ->bind(function (array $doc) use ($url) {
$start = strtolower(strlen($doc['content']) >= 9 ? substr($doc['content'], 0, 9) : $doc['content']);
$start = strtolower(strlen($doc['content']) >= 9 ? substr($doc['content'], 0, 9) : $doc['content']); return $start === '<!doctype' || str_starts_with($start, '<html')
if ($start === '<!doctype' || str_starts_with($start, '<html')) { ? self::deriveFeedFromHTML($doc['content'])
$derivedURL = self::deriveFeedFromHTML($doc['content']); ->bind(function (string $feedURL) use ($url) {
if ($derivedURL->isError()) return $derivedURL; if (!str_starts_with($feedURL, 'http')) {
$feedURL = $derivedURL->getOK(); // Relative URL; feed should be retrieved in the context of the original URL
if (!str_starts_with($feedURL, 'http')) { $original = parse_url($url);
// Relative URL; feed should be retrieved in the context of the original URL $port = key_exists('port', $original) ? ":{$original['port']}" : '';
$original = parse_url($url); $feedURL = $original['scheme'] . '://' . $original['host'] . $port . $feedURL;
$port = key_exists('port', $original) ? ":{$original['port']}" : ''; }
$feedURL = $original['scheme'] . '://' . $original['host'] . $port . $feedURL; return self::retrieveDocument($feedURL);
} })
$tryDoc = self::retrieveDocument($feedURL); ->bind(fn($doc) => match ($doc['code']) {
if ($tryDoc->isError()) return $tryDoc; 200 => Result::OK($doc),
$doc = $tryDoc->getOK(); default => Result::Error(
if ($doc['code'] !== 200) { "Derived feed URL {$doc['url']} returned HTTP Code {$doc['code']}: {$doc['content']}"),
return Result::Error("Derived feed URL $url returned HTTP Code {$doc['code']}: {$doc['content']}"); })
} : Result::OK($doc);
} });
return $doc
$tryParse = self::parseFeed($doc['content']); ->bind(fn($doc) => self::parseFeed($doc['content']))
if ($tryParse->isError()) return $tryParse; ->bind(function (DOMDocument $parsed) use ($doc) {
$extract = $parsed->getElementsByTagNameNS(self::ATOM_NS, 'feed')->length > 0
$parsed = $tryParse->getOK(); ? self::fromAtom(...) : self::fromRSS(...);
$extract = $parsed->getElementsByTagNameNS(self::ATOM_NS, 'feed')->length > 0 return $extract($parsed, $doc['url']);
? self::fromAtom(...) : self::fromRSS(...); });
return $extract($parsed, $doc['url']);
} }
} }

View File

@ -44,10 +44,10 @@ switch ($_SERVER['REQUEST_METHOD']) {
? Feed::update($toEdit->get(), $_POST['url']) ? Feed::update($toEdit->get(), $_POST['url'])
: Result::Error("Feed $feedId not found"); : Result::Error("Feed $feedId not found");
} }
if ($result->isOK()) { $result->iter(function () {
add_info('Feed saved successfully'); add_info('Feed saved successfully');
frc_redirect('/feeds'); frc_redirect('/feeds');
} });
add_error($result->getError()); add_error($result->getError());
$feedId = 'error'; $feedId = 'error';
} catch (DocumentException $ex) { } catch (DocumentException $ex) {