diff --git a/core/basethemelet.class.php b/core/basethemelet.class.php index 83632d08..27b5d818 100644 --- a/core/basethemelet.class.php +++ b/core/basethemelet.class.php @@ -44,7 +44,6 @@ class BaseThemelet { $h_thumb_link = $image->get_thumb_link(); $h_tip = html_escape($image->get_tooltip()); $h_tags = strtolower($image->get_tag_list()); - $base = get_base_href(); $ext = strtolower($image->ext); // If the file doesn't support thumbnail generation, show it at max size. @@ -55,7 +54,13 @@ class BaseThemelet { $tsize = get_thumbnail_size($image->width, $image->height); } - return "". + $custom_classes = ""; + if(class_exists("Relationships")){ + if($image->parent_id !== NULL){ $custom_classes .= "shm-thumb-has_parent "; } + if($image->has_children == TRUE){ $custom_classes .= "shm-thumb-has_child "; } + } + + return "". "$h_tip". "\n"; } diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 6d5cd1cd..a65e9bc1 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -479,15 +479,9 @@ class Image { $this->delete_tags_from_image(); // insert each new tags foreach($tags as $tag) { - if(preg_match("/^source[=|:](.*)$/i", $tag, $matches)) { - $this->set_source($matches[1]); - continue; - } - if(preg_match("/^pool[=|:](.*)$/i", $tag, $matches)) { - if(class_exists("Pools")) { - $pls = new Pools(); - $pls->add_post_from_tag($matches[1], $this->id); - } + $ttpe = new TagTermParseEvent($tag, $this->id); + send_event($ttpe); + if($ttpe->is_metatag()) { continue; } diff --git a/ext/index/main.php b/ext/index/main.php index 9cf5abfc..3c5e66b8 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -141,6 +141,11 @@ *
  • pool=(PoolID, any, none) -- search for images in a pool by PoolID. *
  • pool_by_name=PoolName -- search for images in a pool by PoolName. underscores are replaced with spaces * + *
  • Post Relationships + * * */ diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index 2da74577..1e11f057 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -256,6 +256,20 @@ class NumericScore extends Extension { } } + public function onTagTermParse(TagTermParseEvent $event) { + $matches = array(); + + if(preg_match("/^vote[=|:](up|down|remove)$/", $event->term, $matches)) { + global $user; + $score = ($matches[1] == "up" ? 1 : ($matches[1] == "down" ? -1 : 0)); + if(!$user->is_anonymous()) { + send_event(new NumericScoreSetEvent($event->id, $user, $score)); + } + } + + if(!empty($matches)) $event->metatag = true; + } + private function install() { global $database; global $config; diff --git a/ext/pools/main.php b/ext/pools/main.php index 6bcd8760..7f08554f 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -315,21 +315,26 @@ class Pools extends Extension { } } - public function add_post_from_tag(/*str*/ $poolTag, /*int*/ $imageID){ - $poolTag = str_replace("_", " ", $poolTag); - //First check if pool tag is a title - if(ctype_digit($poolTag)){ - //If string only contains numeric characters, assume it is $poolID - if($this->get_single_pool($poolTag)){ //Make sure pool exists - $this->add_post($poolTag, $imageID); + public function onTagTermParse(TagTermParseEvent $event) { + $matches = array(); + + if(preg_match("/^pool[=|:](.*)$/i", $event->term, $matches)) { + global $user; + $poolTag = (string) str_replace("_", " ", $matches[1]); + + $pool = null; + if(ctype_digit($poolTag)){ //If only digits, assume PoolID + $pool = $this->get_single_pool($poolTag); + }else{ //assume PoolTitle + $pool = $this->get_single_pool_from_title($poolTag); } - }else{ - //If string doesn't contain only numeric characters, check to see if tag is title. - $pool = $this->get_single_pool_from_title($poolTag); - if($pool){ - $this->add_post($pool['id'], $imageID); + + if($pool ? $this->have_permission($user, $pool) : FALSE){ + $this->add_post($pool['id'], $event->id, true); } } + + if(!empty($matches)) $event->metatag = true; } /* ------------------------------------------------- */ @@ -832,9 +837,9 @@ class Pools extends Extension { /* * HERE WE ADD A SIMPLE POST FROM POOL - * USED WITH FOREACH IN revert_history() + * USED WITH FOREACH IN revert_history() & onTagTermParse() */ - private function add_post(/*int*/ $poolID, /*int*/ $imageID) { + private function add_post(/*int*/ $poolID, /*int*/ $imageID, $history=false) { global $database; if(!$this->check_post($poolID, $imageID)) { @@ -845,19 +850,29 @@ class Pools extends Extension { } $database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", array("pid"=>$poolID)); + + if($history){ + $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)); + $this->add_history($poolID, 1, $imageID, $count); + } } /* * HERE WE REMOVE A SIMPLE POST FROM POOL - * USED WITH FOREACH IN revert_history() + * USED WITH FOREACH IN revert_history() & onTagTermParse() */ - private function delete_post(/*int*/ $poolID, /*int*/ $imageID) { + private function delete_post(/*int*/ $poolID, /*int*/ $imageID, $history=false) { global $database; $database->execute("DELETE FROM pool_images WHERE pool_id = :pid AND image_id = :iid", array("pid"=>$poolID, "iid"=>$imageID)); $database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", array("pid"=>$poolID)); + + if($history){ + $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)); + $this->add_history($poolID, 0, $imageID, $count); + } } } diff --git a/ext/pools/theme.php b/ext/pools/theme.php index 444e76b5..138cc0eb 100644 --- a/ext/pools/theme.php +++ b/ext/pools/theme.php @@ -412,8 +412,15 @@ class PoolsTheme extends Themelet { $html .= ""; + $nav_html = ' + Index +
    Create Pool +
    Pool Changes + '; + $page->set_title("Recent Changes"); $page->set_heading("Recent Changes"); + $page->add_block(new Block("Navigation", $nav_html, "left", 10)); $page->add_block(new Block("Recent Changes", $html, "main", 10)); $this->display_paginator($page, "pool/updated", null, $pageNumber, $totalPages); diff --git a/ext/relatationships/main.php b/ext/relatationships/main.php new file mode 100644 index 00000000..b1be3c36 --- /dev/null +++ b/ext/relatationships/main.php @@ -0,0 +1,123 @@ + + * License: GPLv2 + * Description: Allow posts to have relationships (parent/child). + */ + +class Relationships extends Extension { + public function onInitExt(InitExtEvent $event) { + global $config, $database; + + // Create the database tables + if ($config->get_int("ext_relationships_version") < 1){ + $database->Execute("ALTER TABLE images ADD parent_id INT NULL, ADD INDEX (parent_id);"); + $database->Execute("ALTER TABLE images ADD has_children BOOL DEFAULT FALSE NOT NULL;"); + + $config->set_int("ext_relationships_version", 1); + log_info("relationships", "extension installed"); + } + } + + public function onImageInfoSet(ImageInfoSetEvent $event) { + global $user; + 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"]); + }else{ + $this->remove_parent($event->image->id); + } + } + } + + public function onDisplayingImage(DisplayingImageEvent $event) { + $this->theme->relationship_info($event->image); + } + + public function onSearchTermParse(SearchTermParseEvent $event) { + $matches = array(); + if(preg_match("/^parent[=|:]([0-9]+|any|none)$/", $event->term, $matches)) { + $parentID = $matches[1]; + + if(preg_match("/^(any|none)$/", $parentID)){ + $not = ($parentID == "any" ? "NOT" : ""); + $event->add_querylet(new Querylet("images.parent_id IS $not NULL")); + }else{ + $event->add_querylet(new Querylet("images.parent_id = :pid", array("pid"=>$parentID))); + } + } + else if(preg_match("/^child[=|:](any|none)$/", $event->term, $matches)) { + $not = ($matches[1] == "any" ? "=" : "!="); + $event->add_querylet(new Querylet("images.has_children $not TRUE")); + } + } + + public function onTagTermParse(TagTermParseEvent $event) { + $matches = array(); + + if(preg_match("/^parent[=|:]([0-9]+|none)$/", $event->term, $matches)) { + $parentID = $matches[1]; + + if($parentID == "none" || $parentID == "0"){ + $this->remove_parent($event->id); + }else{ + $this->set_parent($event->id, $parentID); + } + } + else if(preg_match("/^child[=|:]([0-9]+)$/", $event->term, $matches)) { + $childID = $matches[1]; + + $this->set_child($event->id, $childID); + } + + if(!empty($matches)) $event->metatag = true; + } + + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { + $event->add_part($this->theme->get_parent_editor_html($event->image), 45); + } + + public function onImageDeletion(ImageDeletionEvent $event) { + global $database; + + if($event->image->has_children){ + $database->execute("UPDATE images SET parent_id = NULL WHERE parent_id = :iid", array("iid"=>$event->image->id)); + } + + 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", array("pid"=>$event->image->parent_id)); + } + } + + private function set_parent(/*int*/ $imageID, /*int*/ $parentID){ + global $database; + + if($database->get_row("SELECT 1 FROM images WHERE id = :pid", array("pid"=>$parentID))){ + $database->execute("UPDATE images SET parent_id = :pid WHERE id = :iid", array("pid"=>$parentID, "iid"=>$imageID)); + $database->execute("UPDATE images SET has_children = TRUE WHERE id = :pid", array("pid"=>$parentID)); + } + } + + private function set_child(/*int*/ $parentID, /*int*/ $childID){ + global $database; + + if($database->get_row("SELECT 1 FROM images WHERE id = :cid", array("cid"=>$childID))){ + $database->execute("UPDATE images SET parent_id = :pid WHERE id = :cid", array("cid"=>$childID, "pid"=>$parentID)); + $database->execute("UPDATE images SET has_children = TRUE WHERE id = :pid", array("pid"=>$parentID)); + } + } + + private function remove_parent(/*int*/ $imageID){ + global $database; + $parentID = $database->get_one("SELECT parent_id FROM images WHERE id = :iid", array("iid"=>$imageID)); + + if($parentID){ + $database->execute("UPDATE images SET parent_id = NULL WHERE id = :iid", array("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", array("pid"=>$parentID)); + } + } +} +?> diff --git a/ext/relatationships/style.css b/ext/relatationships/style.css new file mode 100644 index 00000000..cb7e5633 --- /dev/null +++ b/ext/relatationships/style.css @@ -0,0 +1,15 @@ +.thumb IMG { + border-width: 2px !important; +} + +.shm-thumb-has_child img { + border-color: lime !important; +} + +.shm-thumb-has_parent img { + border-color: #cc0 !important; +} + +.shm-thumb-has_child.shm-thumb-has_parent img { + border-color: lime #cc0 #cc0 lime !important; +} diff --git a/ext/relatationships/theme.php b/ext/relatationships/theme.php new file mode 100644 index 00000000..b40c6289 --- /dev/null +++ b/ext/relatationships/theme.php @@ -0,0 +1,45 @@ +parent_id !== NULL){ + $a = "parent post"; + $page->add_block(new Block(null, "This post belongs to a $a.", "main", 5)); + } + + if($image->has_children == TRUE){ + $ids = $database->get_col("SELECT id FROM images WHERE parent_id = :iid", array("iid"=>$image->id)); + + $html = "This post has ".(count($ids) > 1 ? "child posts" : "a child post").""; + $html .= " (post "; + foreach($ids as $id){ + $html .= "#{$id}, "; + } + $html = rtrim($html, ", ").")."; + + $page->add_block(new Block(null, $html, "main", 6)); + } + } + + public function get_parent_editor_html(Image $image) { + global $user; + $h_parent_id = $image->parent_id; + $s_parent_id = $h_parent_id ?: "None."; + + $html = "\n". + " Parent\n". + " \n". + (!$user->is_anonymous() ? + " {$s_parent_id}\n". + " \n" + : + $s_parent_id + ). + " \n". + "\n"; + return $html; + } +} +?> diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 0f1655a1..30867f84 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -3,6 +3,34 @@ * Name: Tag Editor * Author: Shish * Description: Allow images to have tags assigned to them + * Documentation: + * Here is a list of the tagging metatags available out of the box; + * Shimmie extensions may provide other metatags: + * + *

    Metatags can be followed by ":" rather than "=" if you prefer. + *
    I.E: "source:http://example.com", "source=http://example.com" etc. + *

    Some tagging metatags provided by extensions: + *

    */ /* @@ -72,6 +100,25 @@ class LockSetEvent extends Event { } } +/* + * TagTermParseEvent: + * Signal that a tag term needs parsing + */ +class TagTermParseEvent extends Event { + var $term = null; + var $id = null; + var $metatag = false; + + public function TagTermParseEvent($term, $id) { + $this->term = $term; + $this->id = $id; + } + + public function is_metatag() { + return $this->metatag; + } +} + class TagEdit extends Extension { public function onPageRequest(PageRequestEvent $event) { global $user, $page; @@ -112,7 +159,9 @@ class TagEdit extends Extension { send_event(new TagSetEvent($event->image, $_POST['tag_edit__tags'])); } if($this->can_source($event->image) && isset($_POST['tag_edit__source'])) { - send_event(new SourceSetEvent($event->image, $_POST['tag_edit__source'])); + if(isset($_POST['tag_edit__tags']) ? !preg_match('/source[=|:]/', $_POST["tag_edit__tags"]) : TRUE){ + send_event(new SourceSetEvent($event->image, $_POST['tag_edit__source'])); + } } if($user->can("edit_image_lock")) { $locked = isset($_POST['tag_edit__locked']) && $_POST['tag_edit__locked']=="on"; @@ -169,6 +218,16 @@ class TagEdit extends Extension { $event->add_part($this->theme->get_lock_editor_html($event->image), 42); } + public function onTagTermParse(TagTermParseEvent $event) { + $matches = array(); + + if(preg_match("/^source[=|:](.*)$/i", $event->term, $matches)) { + $source = ($matches[1] !== "none" ? $matches[1] : null); + send_event(new SourceSetEvent(Image::by_id($event->id), $source)); + } + + if(!empty($matches)) $event->metatag = true; + } private function can_tag(Image $image) { global $config, $user; diff --git a/themes/danbooru/themelet.class.php b/themes/danbooru/themelet.class.php index db9b4116..3fbbb24f 100644 --- a/themes/danbooru/themelet.class.php +++ b/themes/danbooru/themelet.class.php @@ -1,26 +1,5 @@ id}"); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); - $i_id = int_escape($image->id); - $h_tags = strtolower($image->get_tag_list()); - - // If file is flash or svg then sets thumbnail to max size. - if($image->ext == 'swf' || $image->ext == 'svg') { - $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); - } - else{ - $tsize = get_thumbnail_size($image->width, $image->height); - } - - return "$h_tip"; - } - - public function display_paginator(Page $page, $base, $query, $page_number, $total_pages) { if($total_pages == 0) $total_pages = 1; $body = $this->build_paginator($page_number, $total_pages, $base, $query); diff --git a/themes/danbooru2/themelet.class.php b/themes/danbooru2/themelet.class.php index db9b4116..3fbbb24f 100644 --- a/themes/danbooru2/themelet.class.php +++ b/themes/danbooru2/themelet.class.php @@ -1,26 +1,5 @@ id}"); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); - $i_id = int_escape($image->id); - $h_tags = strtolower($image->get_tag_list()); - - // If file is flash or svg then sets thumbnail to max size. - if($image->ext == 'swf' || $image->ext == 'svg') { - $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); - } - else{ - $tsize = get_thumbnail_size($image->width, $image->height); - } - - return "$h_tip"; - } - - public function display_paginator(Page $page, $base, $query, $page_number, $total_pages) { if($total_pages == 0) $total_pages = 1; $body = $this->build_paginator($page_number, $total_pages, $base, $query); diff --git a/themes/futaba/themelet.class.php b/themes/futaba/themelet.class.php index 5fc79d3c..3db6c45e 100644 --- a/themes/futaba/themelet.class.php +++ b/themes/futaba/themelet.class.php @@ -1,30 +1,5 @@ id}"); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); - $i_id = int_escape($image->id); - $h_tags = strtolower($image->get_tag_list()); - - // If file is flash or svg then sets thumbnail to max size. - if($image->ext == 'swf' || $image->ext == 'svg') { - $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); - } - else { - $tsize = get_thumbnail_size($image->width, $image->height); - } - - return "$h_tip"; - } - - /** * Add a generic paginator */ diff --git a/themes/lite/themelet.class.php b/themes/lite/themelet.class.php index 8788d9a2..3eadf464 100644 --- a/themes/lite/themelet.class.php +++ b/themes/lite/themelet.class.php @@ -1,34 +1,5 @@ id; - $h_view_link = make_link('post/view/'.$i_id); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); - $h_tags = strtolower($image->get_tag_list()); - $base = get_base_href(); - - // If file is flash or svg then sets thumbnail to max size. - if($image->ext === 'swf' || $image->ext === 'svg'){ - $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); - } - else{ - $tsize = get_thumbnail_size($image->width, $image->height); - } - - return '
    \n"; - } - - /** * Put something in a rounded rectangle box; specific to the default theme */ @@ -40,7 +11,6 @@ class Themelet extends BaseThemelet { "; } - /** * Add a generic paginator */