- Simplified folder removal; now properly relies on foreign keys for dependency resolution
- simplified *propertiesSet() methods by offloading input validation and query building to a generic function
- Implemented function to get the properties of a single folder (useful for internal use)
- Implemented a function to set the properties of a folder
throw new Db\ExceptionInput("missing", ["action" => __FUNCTION__, "field" => "name"]);
} else if(!strlen(trim($data['name']))) {
throw new Db\ExceptionInput("whitespace", ["action" => __FUNCTION__, "field" => "name"]);
}
// common table expression to list all descendant folders of the target folder
$cte = "RECURSIVE folders(id) as (SELECT id from arsse_folders where owner is ? and id is ? union select arsse_folders.id from arsse_folders join folders on arsse_folders.parent=folders.id) ";
$changes = 0;
$this->db->begin();
// first delete any feed subscriptions contained within the folder tree (this may not be necessary because of foreign keys)
$changes += $this->db->prepare("WITH $cte"."DELETE FROM arsse_subscriptions where folder in(select id from folders)", "str", "int")->run($user, $id)->changes();
// next delete the folders themselves
$changes += $this->db->prepare("WITH $cte"."DELETE FROM arsse_folders where id in(select id from folders)", "str", "int")->run($user, $id)->changes();
// if a parent is specified, make sure it exists and belongs to the user; get its root (first-level) folder if it's a nested folder
$p = $this->db->prepare(
"WITH RECURSIVE folders(id) as (SELECT id from arsse_folders where owner is ? and id is ? union select arsse_folders.id from arsse_folders join folders on arsse_folders.parent=folders.id) ".
"SELECT id,root,(id not in (select id from folders)) as valid from arsse_folders where owner is ? and id is ?",
// if the parent does not have a root specified (because it is a first-level folder) use the parent ID as the root ID
$root = $p['root']===null ? $parent : $p['root'];
}
}
$data['parent'] = $parent;
$data['root'] = $root;
// check to make sure the target folder name/location would not create a duplicate (we must di this check because null is not distinct in SQL)
$existing = $this->db->prepare("SELECT id from arsse_folders where owner is ? and parent is ? and name is ?", "str", "int", "str")->run($user, $data['parent'], $data['name'])->getValue();
if(!is_null($existing) && $existing != $id) {
throw new Db\ExceptionInput("constraintViolation"); // FIXME: There needs to be a practical message here
$this->db->prepare('INSERT INTO arsse_categories(article,name) values(?,?)', 'int', 'str')->run($id, $c);
}
}
@ -438,7 +502,7 @@ class Database {
public function updateFeeds(): int {
$feeds = $this->db->query('SELECT id, url, username, password, DATEFORMAT("http", modified) AS lastmodified, etag FROM arsse_feeds')->getAll();
foreach($feeds as $f) {
foreach($feeds as $f) {
// Feed object throws an exception when there are problems, but that isn't ideal
// here. When an exception is occurred it should update the database with the
// error instead of failing.
@ -454,34 +518,34 @@ class Database {
}
// If the feed has been updated then update the database.
if($feed->resource->isModified()) {
if($feed->resource->isModified()) {
$feed->parse();
$this->db->begin();
$articles = $this->db->prepare('SELECT id, url, title, author, DATEFORMAT("http", edited) AS edited_date, guid, content, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? ORDER BY id', 'int')->run($f['id'])->getAll();
foreach($feed->data->items as $i) {
foreach($feed->data->items as $i) {
// Iterate through the articles in the database to determine a match for the one
// in the just-parsed feed.
$match = null;
foreach($articles as $a) {
foreach($articles as $a) {
// If the id exists and is equal to one in the database then this is the post.
if($i->id) {
if($i->id === $a['guid']) {
if($i->id) {
if($i->id === $a['guid']) {
$match = $a;
}
}
// Otherwise if the id doesn't exist and any of the hashes match then this is