diff --git a/ext/relatationships/main.php b/ext/relatationships/main.php index 5304e3a5..eaae08b9 100644 --- a/ext/relatationships/main.php +++ b/ext/relatationships/main.php @@ -1,13 +1,29 @@ + * Author: Angus Johnston * License: GPLv2 * Description: Allow posts to have relationships (parent/child). */ +class ImageRelationshipSetEvent extends Event +{ + public $child_id; + public $parent_id; + + + public function __construct(int $child_id, int $parent_id) + { + $this->child_id = $child_id; + $this->parent_id = $parent_id; + } +} + + class Relationships extends Extension { + public const NAME = "Relationships"; + protected $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; public function onInitExt(InitExtEvent $event) @@ -35,7 +51,7 @@ class Relationships extends Extension { if (isset($_POST['tag_edit__tags']) ? !preg_match('/parent[=|:]/', $_POST["tag_edit__tags"]) : true) { //Ignore tag_edit__parent if tags contain parent metatag if (isset($_POST["tag_edit__parent"]) ? ctype_digit($_POST["tag_edit__parent"]) : false) { - $this->set_parent($event->image->id, (int) $_POST["tag_edit__parent"]); + send_event(new ImageRelationshipSetEvent($event->image->id,(int) $_POST["tag_edit__parent"])); } else { $this->remove_parent($event->image->id); } @@ -75,12 +91,12 @@ class Relationships extends Extension if ($parentID == "none" || $parentID == "0") { $this->remove_parent($event->id); } else { - $this->set_parent($event->id, $parentID); + send_event(new ImageRelationshipSetEvent($event->id, $parentID)); } } elseif (preg_match("/^child[=|:]([0-9]+)$/", $event->term, $matches) && $event->parse) { $childID = $matches[1]; - $this->set_child($event->id, $childID); + send_event(new ImageRelationshipSetEvent($childID, $event->id)); } if (!empty($matches)) { @@ -102,29 +118,45 @@ class Relationships extends Extension } if ($event->image->parent_id !== null) { - $database->execute("UPDATE images SET has_children = (SELECT * FROM (SELECT CASE WHEN (COUNT(*) - 1) > 0 THEN 1 ELSE 0 END FROM images WHERE parent_id = :pid) AS sub) - WHERE id = :pid", ["pid"=>$event->image->parent_id]); + $this->set_has_children($event->image->parent_id); } } - private function set_parent(int $imageID, int $parentID) + public function onImageRelationshipSet(ImageRelationshipSetEvent $event) { global $database; - if ($database->get_row("SELECT 1 FROM images WHERE id = :pid", ["pid"=>$parentID])) { - $database->execute("UPDATE images SET parent_id = :pid WHERE id = :iid", ["pid"=>$parentID, "iid"=>$imageID]); - $database->execute("UPDATE images SET has_children = TRUE WHERE id = :pid", ["pid"=>$parentID]); + $old_parent = $database->get_one("SELECT parent_id FROM images WHERE id = :cid", ["cid"=>$event->child_id]); + + if($old_parent!=$event->parent_id) { + if ($database->get_row("SELECT 1 FROM images WHERE id = :pid", ["pid" => $event->parent_id])) { + + $result = $database->execute("UPDATE images SET parent_id = :pid WHERE id = :cid", ["pid" => $event->parent_id, "cid" => $event->child_id]); + + if ($result->rowCount() > 0) { + $database->execute("UPDATE images SET has_children = TRUE WHERE id = :pid", ["pid" => $event->parent_id]); + + if($old_parent!=null) { + $this->set_has_children($old_parent); + } + } + } } } - private function set_child(int $parentID, int $childID) + + public static function get_children(Image $image, int $omit = null): array { global $database; - - if ($database->get_row("SELECT 1 FROM images WHERE id = :cid", ["cid"=>$childID])) { - $database->execute("UPDATE images SET parent_id = :pid WHERE id = :cid", ["cid"=>$childID, "pid"=>$parentID]); - $database->execute("UPDATE images SET has_children = TRUE WHERE id = :pid", ["pid"=>$parentID]); + $results = $database->get_all_iterable("SELECT * FROM images WHERE parent_id = :pid ", ["pid"=>$image->id]); + $output = []; + foreach ($results as $result) { + if($result["id"]==$omit) { + continue; + } + $output[] = new Image($result); } + return $output; } private function remove_parent(int $imageID) @@ -134,8 +166,23 @@ class Relationships extends Extension if ($parentID) { $database->execute("UPDATE images SET parent_id = NULL WHERE id = :iid", ["iid"=>$imageID]); - $database->execute("UPDATE images SET has_children = (SELECT * FROM (SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM images WHERE parent_id = :pid) AS sub) - WHERE id = :pid", ["pid"=>$parentID]); + $this->set_has_children($parentID); } } + + private function set_has_children(int $parent_id) + { + global $database; + + // Doesn't work on pgsql +// $database->execute("UPDATE images SET has_children = (SELECT * FROM (SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM images WHERE parent_id = :pid) AS sub) +// WHERE id = :pid", ["pid"=>$parentID]); + + $database->execute( + "UPDATE images SET has_children = EXISTS (SELECT 1 FROM images WHERE parent_id = :pid) WHERE id = :pid", + ["pid"=>$parent_id]); + } + + + } diff --git a/ext/relatationships/test.php b/ext/relatationships/test.php new file mode 100644 index 00000000..b486378a --- /dev/null +++ b/ext/relatationships/test.php @@ -0,0 +1,192 @@ +log_in_as_user(); + + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "pbx"); + $image_id_3 = $this->post_image("tests/favicon.png", "pbx"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertNull($image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertFalse($image_1->has_children); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_3->has_children); + + $this->get_page("post/view/$image_id_2"); + $this->assert_title("Image $image_id_2: pbx"); + + $this->markTestIncomplete(); + + $this->set_field("tag_edit__parent", $image_id_1); + $this->click("Set"); + + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertEquals($image_id_1,$image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertTrue($image_1->has_children); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_3->has_children); + + + // Test changing to a different parent + + $this->get_page("post/view/$image_id_2"); + $this->assert_title("Image $image_id_2: pbx"); + + $this->markTestIncomplete(); + + $this->set_field("tag_edit__parent", $image_id_3); + $this->click("Set"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertEquals($image_id_3,$image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_2->has_children); + $this->assertTrue($image_3->has_children); + + + // Test setting parent to none + + $this->get_page("post/view/$image_id_2"); + $this->assert_title("Image $image_id_2: pbx"); + + $this->markTestIncomplete(); + + $this->set_field("tag_edit__parent", ""); + $this->click("Set"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertNull($image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_3->has_children); + + + $this->log_out(); + + $this->log_in_as_admin(); + $this->delete_image($image_id_1); + $this->delete_image($image_id_2); + $this->delete_image($image_id_3); + $this->log_out(); + } + + public function testSetParentByTag() + { + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "pbx"); + $image_id_3 = $this->post_image("tests/favicon.png", "pbx"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertNull($image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertFalse($image_1->has_children); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_3->has_children); + + // Test settings parent:# + + $this->get_page("post/view/$image_id_2"); + $this->assert_title("Image $image_id_2: pbx"); + + $this->markTestIncomplete(); + + $this->set_field("tag_edit__tags", "pbx parent:$image_id_1"); + $this->click("Set"); + + $this->assert_title("Image $image_id_2: pbx"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertEquals($image_id_1,$image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertTrue($image_1->has_children); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_3->has_children); + + // Test settings child:# + + $this->get_page("post/view/$image_id_3"); + $this->assert_title("Image $image_id_3: pbx"); + + $this->markTestIncomplete(); + + $this->set_field("tag_edit__tags", "pbx child:$image_id_1"); + $this->click("Set"); + + $this->assert_title("Image $image_id_3: pbx"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertEquals($image_id_3,$image_1->parent_id); + $this->assertEquals($image_id_1,$image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertTrue($image_1->has_children); + $this->assertFalse($image_2->has_children); + $this->assertTrue($image_3->has_children); + + // Test settings parent:none + + $this->get_page("post/view/$image_id_1"); + $this->assert_title("Image $image_id_1: pbx"); + + $this->markTestIncomplete(); + + $this->set_field("tag_edit__tags", "pbx parent:none"); + $this->click("Set"); + + $this->assert_title("Image $image_id_1: pbx"); + + $image_1 = Image::by_id($image_id_1); + $image_2 = Image::by_id($image_id_2); + $image_3 = Image::by_id($image_id_3); + + $this->assertNull($image_1->parent_id); + $this->assertEquals($image_id_1,$image_2->parent_id); + $this->assertNull($image_3->parent_id); + $this->assertTrue($image_1->has_children); + $this->assertFalse($image_2->has_children); + $this->assertFalse($image_3->has_children); + + $this->log_out(); + + $this->log_in_as_admin(); + $this->delete_image($image_id_1); + $this->delete_image($image_id_2); + $this->delete_image($image_id_3); + $this->log_out(); + } +}