/** Returns an indexed array of numeric identifiers for newsfeeds which should be refreshed */
public function feedListStale(): array {
$feeds = $this->db->query("SELECT id from arsse_feeds where next_fetch <= CURRENT_TIMESTAMP")->getAll();
return array_column($feeds, 'id');
}
/** Attempts to refresh a newsfeed, returning an indication of success
*
* @param integer $feedID The numerical identifier of the newsfeed to refresh
* @param boolean $throwError Whether to throw an exception on failure in addition to storing error information in the database
*/
public function feedUpdate($feedID, bool $throwError = false): bool {
// check to make sure the feed exists
if (!ValueInfo::id($feedID)) {
@ -956,6 +962,10 @@ class Database {
return true;
}
/** Deletes orphaned newsfeeds from the database
*
* Newsfeeds are orphaned if no users are subscribed to them. Deleting a newsfeed also deletes its articles
*/
public function feedCleanup(): bool {
$tr = $this->begin();
// first unmark any feeds which are no longer orphaned
@ -973,6 +983,18 @@ class Database {
return $out;
}
/** Retrieves various identifiers for the latest $count articles in the given newsfeed. The identifiers are:
*
* - "id": The database record key for the article
* - "guid": The (theoretically) unique identifier for the article
* - "edited": The time at which the article was last edited, per the newsfeed
* - "url_title_hash": A cryptographic hash of the article URL and its title
* - "url_content_hash": A cryptographic hash of the article URL and its content
* - "title_content_hash": A cryptographic hash of the article title and its content
*
* @param integer $feedID The numeric identifier of the feed
* @param integer $count The number of records to return
*/
public function feedMatchLatest(int $feedID, int $count): Db\Result {
return $this->db->prepare(
"SELECT id, edited, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed = ? ORDER BY modified desc, id desc limit ?",
@ -981,6 +1003,21 @@ class Database {
)->run($feedID, $count);
}
/** Retrieves various identifiers for articles in the given newsfeed which match the input identifiers. The output identifiers are:
*
* - "id": The database record key for the article
* - "guid": The (theoretically) unique identifier for the article
* - "edited": The time at which the article was last edited, per the newsfeed
* - "url_title_hash": A cryptographic hash of the article URL and its title
* - "url_content_hash": A cryptographic hash of the article URL and its content
* - "title_content_hash": A cryptographic hash of the article title and its content
*
* @param integer $feedID The numeric identifier of the feed
* @param array $ids An array of GUIDs of articles
* @param array $hashesUT An array of hashes of articles' URL and title
* @param array $hashesUC An array of hashes of articles' URL and content
* @param array $hashesTC An array of hashes of articles' title and content
/** Returns a map between all the given edition identifiers and their associated article identifiers */
public function editionArticle(int ...$edition): array {
$out = [];
$context = (new Context)->editions($edition);
@ -1484,6 +1587,13 @@ class Database {
}
}
/** Creates a label, and returns its numeric identifier
*
* Labels are discrete objects in the database and can be associated with multiple articles; an article may in turn be associated with multiple labels
*
* @param string $user The user who will own the created label
* @param array $data An associative array defining the label's properties; currently only "name" is understood
*/
public function labelAdd(string $user, array $data): int {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
@ -1496,6 +1606,18 @@ class Database {
return $this->db->prepare("INSERT INTO arsse_labels(owner,name) values(?,?)", "str", "str")->run($user, $name)->lastId();
}
/** Lists a user's article labels
*
* The following keys are included in each record:
*
* - "id": The label's numeric identifier
* - "name" The label's textual name
* - "articles": The count of articles which have the label assigned to them
* - "read": How many of the total articles assigned to the label are read
*
* @param string $user The user whose labels are to be listed
* @param boolean $includeEmpty Whether to include (true) or supress (false) labels which have no articles assigned to them
*/
public function labelList(string $user, bool $includeEmpty = true): Db\Result {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
@ -1518,6 +1640,14 @@ class Database {
)->run($user, !$includeEmpty);
}
/** Deletes a label from the database
*
* Any articles associated with the label remains untouched
*
* @param string $user The owner of the label to remove
* @param integer|string $id The numeric identifier or name of the label
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelRemove(string $user, $id, bool $byName = false): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
@ -1532,6 +1662,19 @@ class Database {
return true;
}
/** Retrieves the properties of a label
*
* The following keys are included in the output array:
*
* - "id": The label's numeric identifier
* - "name" The label's textual name
* - "articles": The count of articles which have the label assigned to them
* - "read": How many of the total articles assigned to the label are read
*
* @param string $user The owner of the label to remove
* @param integer|string $id The numeric identifier or name of the label
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelPropertiesGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
@ -1558,6 +1701,13 @@ class Database {
return $out;
}
/** Sets the properties of a label
*
* @param string $user The owner of the label to query
* @param integer|string $id The numeric identifier or name of the label
* @param array $data An associative array defining the label's properties; currently only "name" is understood
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelPropertiesSet(string $user, $id, array $data, bool $byName = false): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
@ -1583,6 +1733,12 @@ class Database {
return $out;
}
/** Returns an indexed array of article identifiers assigned to a label
*
* @param string $user The owner of the label to query
* @param integer|string $id The numeric identifier or name of the label
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelArticlesGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
@ -1603,6 +1759,14 @@ class Database {
}
}
/** Makes or breaks associations between a given label and articles matching the given query context
*
* @param string $user The owner of the label
* @param integer|string $id The numeric identifier or name of the label
* @param Context $context The query context matching the desired articles
* @param boolean $remove Whether to remove (true) rather than add (true) an association with the articles matching the context
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelArticlesSet(string $user, $id, Context $context = null, bool $remove = false, bool $byName = false): int {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
@ -1643,6 +1807,16 @@ class Database {
return $out;
}
/** Ensures the specified label identifier or name is valid (and optionally whether it exists) and raises an exception otherwise
*
* Returns an associative array containing the id, name of the label if it exists
*
* @param string $user The user who owns the label to be validated
* @param integer|string $id The numeric identifier or name of the label to validate
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
* @param boolean $checkDb Whether to check whether the label exists (true) or only if the identifier or name is syntactically valid (false)
* @param boolean $subject Whether the label is the subject rather than the object of the operation being performed; this only affects the semantics of the error message if validation fails