From 0530ed0dc9aa6fd13b6d1e9eb505d47e8194cb53 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 9 Apr 2024 23:14:53 -0400 Subject: [PATCH] WIP on adding feed items (#4) --- src/lib/Data.php | 57 ++++++++++++++++++++++++++++++++++++++------- src/lib/Feed.php | 37 +++++++++++++++++++++++++++++ src/public/feed.php | 1 + 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/lib/Data.php b/src/lib/Data.php index e8e8b91..dcd599b 100644 --- a/src/lib/Data.php +++ b/src/lib/Data.php @@ -51,12 +51,14 @@ class Data { id INTEGER NOT NULL PRIMARY KEY, feed_id INTEGER NOT NULL, title TEXT NOT NULL, + item_guid TEXT NOT NULL, + item_link TEXT NOT NULL, published_on TEXT NOT NULL, updated_on TEXT, content TEXT NOT NULL, is_encoded BOOLEAN NOT NULL, - is_read BOOLEAN NOT NULL, - is_bookmarked BOOLEAN NOT NULL, + is_read BOOLEAN NOT NULL DEFAULT 0, + is_bookmarked BOOLEAN NOT NULL DEFAULT 0, FOREIGN KEY (feed_id) REFERENCES feed (id)) SQL; $db->exec($query); @@ -109,7 +111,7 @@ class Data { if ($updatedOn) { try { $updated = (new DateTimeImmutable($updatedOn))->format(DateTimeInterface::ATOM); - } catch (Exception $ex) { + } catch (Exception) { $updated = null; } } else { @@ -123,11 +125,48 @@ class Data { $query->bindValue(':updated', $updated); $query->bindValue(':checked', (new DateTimeImmutable())->format(DateTimeInterface::ATOM)); $result = $query->execute(); - if ($result) { - $idQuery = $db->prepare('SELECT last_insert_rowid()'); - $idResult = $idQuery->execute(); - if ($idResult) return $idResult->fetchArray(SQLITE3_NUM)[0]; - } - return -1; + return $result ? $db->lastInsertRowID() : -1; + } + + /** + * Does a feed item already exist? + * @param int $feedId The ID of the feed to which the item belongs + * @param string $guid The GUID from the RSS feed, uniquely identifying the item + * @return bool True if the item exists, false if not + */ + public static function itemExists(int $feedId, string $guid): bool { + $db = self::getConnection(); + $query = $db->prepare('SELECT COUNT(*) FROM item WHERE feed_id = :feed AND item_guid = :guid'); + $query->bindValue(':feed', $feedId); + $query->bindValue(':guid', $guid); + $result = $query->execute(); + return $result && $result->fetchArray(SQLITE3_NUM)[0] == 1; + } + + /** + * Add a feed item + * @param int $feedId The ID of the feed to which the item should be added + * @param string $guid The GUID from the RSS feed (uses link if `` not specified) + * @param string $link The link to this item + * @param string $title The title of the item + * @param string $published The date/time the item was published + * @param string $content The content of the item + * @param bool $isEncoded Whether the content has HTML (true) or is plaintext (false) + * @throws Exception If the published date is not valid + */ + public static function addItem(int $feedId, string $guid, string $link, string $title, string $published, + string $content, bool $isEncoded): void { + $db = self::getConnection(); + $query = $db->prepare( + 'INSERT INTO item (feed_id, item_guid, item_link, title, published_on, content, is_encoded)' + . ' VALUES (:feed, :guid, :link, :title, :published, :content, :encoded)'); + $query->bindValue(':feed', $feedId); + $query->bindValue(':guid', $guid); + $query->bindValue(':link', $link); + $query->bindValue(':title', $title); + $query->bindValue(':published', (new DateTimeImmutable($published))->format(DateTimeInterface::ATOM)); + $query->bindValue(':content', $content); + $query->bindValue(':encoded', $isEncoded); + $query->execute(); } } diff --git a/src/lib/Feed.php b/src/lib/Feed.php index 0a1cfd0..1b0e09a 100644 --- a/src/lib/Feed.php +++ b/src/lib/Feed.php @@ -52,6 +52,40 @@ class Feed { return $result; } + /** + * Update a feed's items + * @param int $feedId The ID of the feed to which these items belong + * @param SimpleXMLElement $channel The RSS feed items + * @return array [ 'ok' => true ] if successful, [ 'error' => message ] if not + */ + public static function updateItems(int $feedId, SimpleXMLElement $channel): array { + try { + for ($i = 0; $i < sizeof($channel->item); $i++) { + $item = $channel->item[$i]; + $itemGuid = (string)$item->guid ? $item->guid : $item->link; + $isNew = !Data::itemExists($feedId, $itemGuid); + if ($isNew) { + $title = (string)$item->title; + $link = (string)$item->link; + $published = (string)$item->pubDate; + // TODO: why is this getting all encoded content, and not just the one for the current item? + $encodedContent = $item->xpath('//content:encoded'); + if ($encodedContent) { + $content = (string) $encodedContent[$i]; + $isEncoded = true; + } else { + $content = $item->description; + $isEncoded = false; + } + Data::addItem($feedId, $itemGuid, $link, $title, $published, $content, $isEncoded); + } // TODO: else check updated date; may want to return that from the isNew check instead + } + } catch (Exception $ex) { + return [ 'error' => $ex->getMessage() ]; + } + return [ 'ok', true ]; + } + /** * Add an RSS feed * @param string $url The URL of the RSS feed to add @@ -64,6 +98,9 @@ class Feed { $channel = $feed['ok']->channel; $feedId = Data::addFeed($feed['url'], (string) $channel->title, (string) $channel->lastBuildDate); + $result = self::updateItems($feedId, $channel); + if (array_key_exists('error', $result)) return $result; + return [ 'ok' => true ]; } } diff --git a/src/public/feed.php b/src/public/feed.php index 59b4014..1092b7f 100644 --- a/src/public/feed.php +++ b/src/public/feed.php @@ -12,6 +12,7 @@ Security::verifyUser(); if ($_SERVER['REQUEST_METHOD'] == 'POST') { // TODO: get feed, add if new, reject if existing but not owned by this user, update otherwise $result = Feed::add($_POST['url']); + echo '
'; var_dump($result); echo '
'; $feed = [ 'id' => $_POST['id'], 'url' => $_POST['url'] ]; $title = 'TODO'; } else {