|
|
@@ -0,0 +1,634 @@
|
|
|
+<?php
|
|
|
+/**
|
|
|
+ * Fever API for FreshRSS
|
|
|
+ * Version 0.1
|
|
|
+ * Author: Kevin Papst / https://github.com/kevinpapst
|
|
|
+ *
|
|
|
+ * Inspired by:
|
|
|
+ * TinyTinyRSS Fever API plugin @dasmurphy
|
|
|
+ * See https://github.com/dasmurphy/tinytinyrss-fever-plugin
|
|
|
+ */
|
|
|
+
|
|
|
+// ================================================================================================
|
|
|
+// BOOTSTRAP FreshRSS
|
|
|
+require(__DIR__ . '/../../constants.php');
|
|
|
+require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
|
|
|
+Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php');
|
|
|
+
|
|
|
+// check if API is enabled globally
|
|
|
+FreshRSS_Context::$system_conf = Minz_Configuration::get('system');
|
|
|
+if (!FreshRSS_Context::$system_conf->api_enabled) {
|
|
|
+ Minz_Log::warning('serviceUnavailable() ' . debugInfo(), API_LOG);
|
|
|
+ header('HTTP/1.1 503 Service Unavailable');
|
|
|
+ header('Content-Type: text/plain; charset=UTF-8');
|
|
|
+ die('Service Unavailable!');
|
|
|
+}
|
|
|
+
|
|
|
+ini_set('session.use_cookies', '0');
|
|
|
+register_shutdown_function('session_destroy');
|
|
|
+Minz_Session::init('FreshRSS');
|
|
|
+// ================================================================================================
|
|
|
+
|
|
|
+
|
|
|
+class FeverAPI_EntryDAO extends FreshRSS_EntryDAO
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function countFever()
|
|
|
+ {
|
|
|
+ $values = array(
|
|
|
+ 'total' => 0,
|
|
|
+ 'min' => 0,
|
|
|
+ 'max' => 0,
|
|
|
+ );
|
|
|
+ $sql = 'SELECT COUNT(id) as `total`, MIN(id) as `min`, MAX(id) as `max` FROM `' . $this->prefix . 'entry`';
|
|
|
+ $stm = $this->bd->prepare($sql);
|
|
|
+ $stm->execute();
|
|
|
+ $result = $stm->fetchAll(PDO::FETCH_ASSOC);
|
|
|
+
|
|
|
+ if (!empty($result[0])) {
|
|
|
+ $values = $result[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $values;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $prefix
|
|
|
+ * @param array $values
|
|
|
+ * @param array $bindArray
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ protected function bindParamArray($prefix, $values, &$bindArray)
|
|
|
+ {
|
|
|
+ $str = '';
|
|
|
+ for ($i = 0; $i < count($values); $i++) {
|
|
|
+ $str .= ':' . $prefix . $i . ',';
|
|
|
+ $bindArray[$prefix . $i] = $values[$i];
|
|
|
+ }
|
|
|
+ return rtrim($str, ',');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param array $feed_ids
|
|
|
+ * @param array $entry_ids
|
|
|
+ * @param int|null $max_id
|
|
|
+ * @param int|null $since_id
|
|
|
+ * @return FreshRSS_Entry[]
|
|
|
+ */
|
|
|
+ public function findEntries(array $feed_ids, array $entry_ids, $max_id, $since_id)
|
|
|
+ {
|
|
|
+ $values = array();
|
|
|
+ $order = '';
|
|
|
+
|
|
|
+ $sql = 'SELECT id, guid, title, author, '
|
|
|
+ . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
+ . ', link, date, is_read, is_favorite, id_feed, tags '
|
|
|
+ . 'FROM `' . $this->prefix . 'entry` WHERE';
|
|
|
+
|
|
|
+ if (!empty($entry_ids)) {
|
|
|
+ $bindEntryIds = $this->bindParamArray("id", $entry_ids, $values);
|
|
|
+ $sql .= " id IN($bindEntryIds)";
|
|
|
+ } else if (!empty($max_id)) {
|
|
|
+ $sql .= ' id < :id';
|
|
|
+ $values[':id'] = $max_id;
|
|
|
+ $order = ' ORDER BY id DESC';
|
|
|
+ } else {
|
|
|
+ $sql .= ' id > :id';
|
|
|
+ $values[':id'] = $since_id;
|
|
|
+ $order = ' ORDER BY id ASC';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($feed_ids)) {
|
|
|
+ $bindFeedIds = $this->bindParamArray("feed", $feed_ids, $values);
|
|
|
+ $sql .= " AND id_feed IN($bindFeedIds)";
|
|
|
+ }
|
|
|
+
|
|
|
+ $sql .= $order;
|
|
|
+ $sql .= ' LIMIT 50';
|
|
|
+
|
|
|
+ $stm = $this->bd->prepare($sql);
|
|
|
+ $stm->execute($values);
|
|
|
+ $result = $stm->fetchAll(PDO::FETCH_ASSOC);
|
|
|
+
|
|
|
+ $entries = array();
|
|
|
+ foreach ($result as $dao) {
|
|
|
+ $entries[] = self::daoToEntry($dao);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $entries;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Class FeverAPI
|
|
|
+ */
|
|
|
+class FeverAPI
|
|
|
+{
|
|
|
+ const API_LEVEL = 3;
|
|
|
+ const STATUS_OK = 1;
|
|
|
+ const STATUS_ERR = 0;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Authenticate the user
|
|
|
+ *
|
|
|
+ * API Password sent from client is the result of the md5 sum of
|
|
|
+ * your FreshRSS "username:your-api-password" combination
|
|
|
+ */
|
|
|
+ private function authenticate()
|
|
|
+ {
|
|
|
+ FreshRSS_Context::$user_conf = null;
|
|
|
+ Minz_Session::_param('currentUser');
|
|
|
+ $feverKey = empty($_POST['api_key']) ? '' : substr(trim($_POST['api_key']), 0, 128);
|
|
|
+ if (ctype_xdigit($feverKey)) {
|
|
|
+ $feverKey = strtolower($feverKey);
|
|
|
+ $username = @file_get_contents(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::$system_conf->salt) . '-' . $feverKey . '.txt', false);
|
|
|
+ if ($username != false) {
|
|
|
+ $username = trim($username);
|
|
|
+ $user_conf = get_user_configuration($username);
|
|
|
+ if ($user_conf != null && $feverKey === $user_conf->feverKey) {
|
|
|
+ FreshRSS_Context::$user_conf = $user_conf;
|
|
|
+ Minz_Session::_param('currentUser', $username);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function isAuthenticatedApiUser()
|
|
|
+ {
|
|
|
+ $this->authenticate();
|
|
|
+
|
|
|
+ if (FreshRSS_Context::$user_conf !== null) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return FreshRSS_FeedDAO
|
|
|
+ */
|
|
|
+ protected function getDaoForFeeds()
|
|
|
+ {
|
|
|
+ return new FreshRSS_FeedDAO();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return FreshRSS_CategoryDAO
|
|
|
+ */
|
|
|
+ protected function getDaoForCategories()
|
|
|
+ {
|
|
|
+ return new FreshRSS_CategoryDAO();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return FeverAPI_EntryDAO
|
|
|
+ */
|
|
|
+ protected function getDaoForEntries()
|
|
|
+ {
|
|
|
+ return new FeverAPI_EntryDAO();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This does all the processing, since the fever api does not have a specific variable that specifies the operation
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ public function process()
|
|
|
+ {
|
|
|
+ $response_arr = array();
|
|
|
+
|
|
|
+ if (!$this->isAuthenticatedApiUser()) {
|
|
|
+ throw new Exception('No user given or user is not allowed to access API');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["groups"])) {
|
|
|
+ $response_arr["groups"] = $this->getGroups();
|
|
|
+ $response_arr["feeds_groups"] = $this->getFeedsGroup();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["feeds"])) {
|
|
|
+ $response_arr["feeds"] = $this->getFeeds();
|
|
|
+ $response_arr["feeds_groups"] = $this->getFeedsGroup();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["favicons"])) {
|
|
|
+ $response_arr["favicons"] = $this->getFavicons();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["items"])) {
|
|
|
+ $response_arr["total_items"] = $this->getTotalItems();
|
|
|
+ $response_arr["items"] = $this->getItems();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["links"])) {
|
|
|
+ $response_arr["links"] = $this->getLinks();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["unread_item_ids"])) {
|
|
|
+ $response_arr["unread_item_ids"] = $this->getUnreadItemIds();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["saved_item_ids"])) {
|
|
|
+ $response_arr["saved_item_ids"] = $this->getSavedItemIds();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["mark"], $_REQUEST["as"], $_REQUEST["id"]) && is_numeric($_REQUEST["id"])) {
|
|
|
+ $method_name = "set" . ucfirst($_REQUEST["mark"]) . "As" . ucfirst($_REQUEST["as"]);
|
|
|
+ $allowedMethods = array(
|
|
|
+ 'setFeedAsRead', 'setGroupAsRead', 'setItemAsRead',
|
|
|
+ 'setItemAsSaved', 'setItemAsUnread', 'setItemAsUnsaved'
|
|
|
+ );
|
|
|
+ if (in_array($method_name, $allowedMethods)) {
|
|
|
+ $id = intval($_REQUEST["id"]);
|
|
|
+ switch (strtolower($_REQUEST["mark"])) {
|
|
|
+ case 'item':
|
|
|
+ $this->{$method_name}($id);
|
|
|
+ break;
|
|
|
+ case 'feed':
|
|
|
+ case 'group':
|
|
|
+ $before = (isset($_REQUEST["before"])) ? $_REQUEST["before"] : null;
|
|
|
+ $this->{$method_name}($id, $before);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch ($_REQUEST["as"]) {
|
|
|
+ case "read":
|
|
|
+ case "unread":
|
|
|
+ $response_arr["unread_item_ids"] = $this->getUnreadItemIds();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'saved':
|
|
|
+ case 'unsaved':
|
|
|
+ $response_arr["saved_item_ids"] = $this->getSavedItemIds();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $response_arr;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the complete JSON, with 'api_version' and status as 'auth'.
|
|
|
+ *
|
|
|
+ * @param int $status
|
|
|
+ * @param array $reply
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public function wrap($status, array $reply = array())
|
|
|
+ {
|
|
|
+ $arr = array('api_version' => self::API_LEVEL, 'auth' => $status);
|
|
|
+
|
|
|
+ if ($status === self::STATUS_OK) {
|
|
|
+ $arr['last_refreshed_on_time'] = (string) $this->lastRefreshedOnTime();
|
|
|
+ $arr = array_merge($arr, $reply);
|
|
|
+ }
|
|
|
+
|
|
|
+ return json_encode($arr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * every authenticated method includes last_refreshed_on_time
|
|
|
+ *
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ protected function lastRefreshedOnTime()
|
|
|
+ {
|
|
|
+ $lastUpdate = 0;
|
|
|
+
|
|
|
+ $dao = $this->getDaoForFeeds();
|
|
|
+ $entries = $dao->listFeedsOrderUpdate(-1, 1);
|
|
|
+ $feed = current($entries);
|
|
|
+
|
|
|
+ if (!empty($feed)) {
|
|
|
+ $lastUpdate = $feed->lastUpdate();
|
|
|
+ }
|
|
|
+
|
|
|
+ return $lastUpdate;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function getFeeds()
|
|
|
+ {
|
|
|
+ $feeds = array();
|
|
|
+
|
|
|
+ $dao = $this->getDaoForFeeds();
|
|
|
+ $myFeeds = $dao->listFeeds();
|
|
|
+
|
|
|
+ /** @var FreshRSS_Feed $feed */
|
|
|
+ foreach ($myFeeds as $feed) {
|
|
|
+ $feeds[] = array(
|
|
|
+ "id" => $feed->id(),
|
|
|
+ "favicon_id" => $feed->id(),
|
|
|
+ "title" => $feed->name(),
|
|
|
+ "url" => $feed->url(),
|
|
|
+ "site_url" => $feed->website(),
|
|
|
+ "is_spark" => 0, // unsupported
|
|
|
+ "last_updated_on_time" => $feed->lastUpdate()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return $feeds;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function getGroups()
|
|
|
+ {
|
|
|
+ $groups = array();
|
|
|
+
|
|
|
+ $dao = $this->getDaoForCategories();
|
|
|
+ $categories = $dao->listCategories(false, false);
|
|
|
+
|
|
|
+ /** @var FreshRSS_Category $category */
|
|
|
+ foreach ($categories as $category) {
|
|
|
+ $groups[] = array(
|
|
|
+ 'id' => $category->id(),
|
|
|
+ 'title' => $category->name()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return $groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function getFavicons()
|
|
|
+ {
|
|
|
+ $favicons = array();
|
|
|
+
|
|
|
+ $dao = $this->getDaoForFeeds();
|
|
|
+ $myFeeds = $dao->listFeeds();
|
|
|
+
|
|
|
+ $salt = FreshRSS_Context::$system_conf->salt;
|
|
|
+
|
|
|
+ /** @var FreshRSS_Feed $feed */
|
|
|
+ foreach ($myFeeds as $feed) {
|
|
|
+
|
|
|
+ $id = hash('crc32b', $salt . $feed->url());
|
|
|
+ $filename = DATA_PATH . '/favicons/' . $id . '.ico';
|
|
|
+ if (!file_exists($filename)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $favicons[] = array(
|
|
|
+ "id" => $feed->id(),
|
|
|
+ "data" => image_type_to_mime_type(exif_imagetype($filename)) . ";base64," . base64_encode(file_get_contents($filename))
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return $favicons;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ protected function getTotalItems()
|
|
|
+ {
|
|
|
+ $total_items = 0;
|
|
|
+
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $result = $dao->countFever();
|
|
|
+
|
|
|
+ if (!empty($result)) {
|
|
|
+ $total_items = $result['total'];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $total_items;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function getFeedsGroup()
|
|
|
+ {
|
|
|
+ $groups = array();
|
|
|
+ $ids = array();
|
|
|
+
|
|
|
+ $dao = $this->getDaoForFeeds();
|
|
|
+ $myFeeds = $dao->listFeeds();
|
|
|
+
|
|
|
+ /** @var FreshRSS_Feed $feed */
|
|
|
+ foreach ($myFeeds as $feed) {
|
|
|
+ $ids[$feed->category()][] = $feed->id();
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach($ids as $category => $feedIds) {
|
|
|
+ $groups[] = array(
|
|
|
+ 'group_id' => $category,
|
|
|
+ 'feed_ids' => implode(',', $feedIds)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return $groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AFAIK there is no 'hot links' alternative in FreshRSS
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function getLinks()
|
|
|
+ {
|
|
|
+ return array();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param array $ids
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ protected function entriesToIdList($ids = array())
|
|
|
+ {
|
|
|
+ return implode(',', array_values($ids));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ protected function getUnreadItemIds()
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $entries = $dao->listIdsWhere('a', '', FreshRSS_Entry::STATE_NOT_READ, 'ASC', 0);
|
|
|
+ return $this->entriesToIdList($entries);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ protected function getSavedItemIds()
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $entries = $dao->listIdsWhere('a', '', FreshRSS_Entry::STATE_FAVORITE, 'ASC', 0);
|
|
|
+ return $this->entriesToIdList($entries);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function setItemAsRead($id)
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $dao->markRead($id, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function setItemAsUnread($id)
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $dao->markRead($id, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function setItemAsSaved($id)
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $dao->markFavorite($id, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function setItemAsUnsaved($id)
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $dao->markFavorite($id, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function getItems()
|
|
|
+ {
|
|
|
+ $feed_ids = array();
|
|
|
+ $entry_ids = array();
|
|
|
+ $max_id = null;
|
|
|
+ $since_id = null;
|
|
|
+
|
|
|
+ if (isset($_REQUEST["feed_ids"]) || isset($_REQUEST["group_ids"])) {
|
|
|
+ if (isset($_REQUEST["feed_ids"])) {
|
|
|
+ $feed_ids = explode(",", $_REQUEST["feed_ids"]);
|
|
|
+ }
|
|
|
+
|
|
|
+ $dao = $this->getDaoForCategories();
|
|
|
+ if (isset($_REQUEST["group_ids"])) {
|
|
|
+ $group_ids = explode(",", $_REQUEST["group_ids"]);
|
|
|
+ foreach ($group_ids as $id) {
|
|
|
+ /** @var FreshRSS_Category $category */
|
|
|
+ $category = $dao->searchById($id);
|
|
|
+ /** @var FreshRSS_Feed $feed */
|
|
|
+ foreach ($category->feeds() as $feed) {
|
|
|
+ $feeds[] = $feed->id();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $feed_ids = array_unique($feeds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_REQUEST["max_id"])) {
|
|
|
+ // use the max_id argument to request the previous $item_limit items
|
|
|
+ if (is_numeric($_REQUEST["max_id"])) {
|
|
|
+ $max = ($_REQUEST["max_id"] > 0) ? intval($_REQUEST["max_id"]) : 0;
|
|
|
+ if ($max) {
|
|
|
+ $max_id = $max;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (isset($_REQUEST["with_ids"])) {
|
|
|
+ $entry_ids = explode(",", $_REQUEST["with_ids"]);
|
|
|
+ } else {
|
|
|
+ // use the since_id argument to request the next $item_limit items
|
|
|
+ $since_id = isset($_REQUEST["since_id"]) && is_numeric($_REQUEST["since_id"]) ? intval($_REQUEST["since_id"]) : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ $items = array();
|
|
|
+
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ $entries = $dao->findEntries($feed_ids, $entry_ids, $max_id, $since_id);
|
|
|
+
|
|
|
+ // Load list of extensions and enable the "system" ones.
|
|
|
+ Minz_ExtensionManager::init();
|
|
|
+
|
|
|
+ foreach($entries as $item) {
|
|
|
+ /** @var FreshRSS_Entry $entry */
|
|
|
+ $entry = Minz_ExtensionManager::callHook('entry_before_display', $item);
|
|
|
+ if (is_null($entry)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $items[] = array(
|
|
|
+ "id" => $entry->id(),
|
|
|
+ "feed_id" => $entry->feed(false),
|
|
|
+ "title" => $entry->title(),
|
|
|
+ "author" => $entry->author(),
|
|
|
+ "html" => $entry->content(),
|
|
|
+ "url" => $entry->link(),
|
|
|
+ "is_saved" => $entry->isFavorite() ? 1 : 0,
|
|
|
+ "is_read" => $entry->isRead() ? 1 : 0,
|
|
|
+ "created_on_time" => $entry->date(true)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return $items;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * TODO replace by a dynamic fetch for id <= $before timestamp
|
|
|
+ *
|
|
|
+ * @param int $beforeTimestamp
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ protected function convertBeforeToId($beforeTimestamp)
|
|
|
+ {
|
|
|
+ // if before is zero, set it to now so feeds all items are read from before this point in time
|
|
|
+ if ($beforeTimestamp == 0) {
|
|
|
+ $before = time();
|
|
|
+ }
|
|
|
+ $before = PHP_INT_MAX;
|
|
|
+
|
|
|
+ return $before;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function setFeedAsRead($id, $before)
|
|
|
+ {
|
|
|
+ $before = $this->convertBeforeToId($before);
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+ return $dao->markReadFeed($id, $before);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function setGroupAsRead($id, $before)
|
|
|
+ {
|
|
|
+ $dao = $this->getDaoForEntries();
|
|
|
+
|
|
|
+ // special case to mark all items as read
|
|
|
+ if ($id === 0) {
|
|
|
+ $result = $dao->countFever();
|
|
|
+
|
|
|
+ if (!empty($result)) {
|
|
|
+ return $dao->markReadEntries($result['max']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $before = $this->convertBeforeToId($before);
|
|
|
+ return $dao->markReadCat($id, $before);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ================================================================================================
|
|
|
+// refresh is not allowed yet, probably we find a way to support it later
|
|
|
+if (isset($_REQUEST["refresh"])) {
|
|
|
+ Minz_Log::warning('Refresh items for fever API - notImplemented()', API_LOG);
|
|
|
+ header('HTTP/1.1 501 Not Implemented');
|
|
|
+ header('Content-Type: text/plain; charset=UTF-8');
|
|
|
+ die('Not Implemented!');
|
|
|
+}
|
|
|
+
|
|
|
+// Start the Fever API handling
|
|
|
+$handler = new FeverAPI();
|
|
|
+
|
|
|
+header("Content-Type: application/json; charset=UTF-8");
|
|
|
+
|
|
|
+if (!$handler->isAuthenticatedApiUser()) {
|
|
|
+ echo $handler->wrap(FeverAPI::STATUS_ERR, array());
|
|
|
+} else {
|
|
|
+ echo $handler->wrap(FeverAPI::STATUS_OK, $handler->process());
|
|
|
+}
|