diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php
index 6efbe328..7fa8b0b7 100644
--- a/core/imageboard/tag.php
+++ b/core/imageboard/tag.php
@@ -110,6 +110,7 @@ class Tag
$term = str_replace('_', '\_', $term);
$term = str_replace('%', '\%', $term);
$term = str_replace('*', '%', $term);
+ $term = str_replace("?", "_", $term);
return $term;
}
}
diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php
index 004dbc10..9b012b13 100644
--- a/ext/ouroboros_api/main.php
+++ b/ext/ouroboros_api/main.php
@@ -585,7 +585,7 @@ class OuroborosAPI extends Extension
ORDER BY SCORE_STRNORM(substr(tag, 1, 1)) LIMIT :start, :max_items
"
),
- ['tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit]
+ ['tags_min' => $config->get_int(TagListConfig::TAGS_MIN), 'start' => $start, 'max_items' => $limit]
);
break;
case 'count':
@@ -596,7 +596,7 @@ class OuroborosAPI extends Extension
WHERE count >= :tags_min
ORDER BY count DESC, tag ASC LIMIT :start, :max_items
",
- ['tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit]
+ ['tags_min' => $config->get_int(TagListConfig::TAGS_MIN), 'start' => $start, 'max_items' => $limit]
);
break;
case 'date':
@@ -607,7 +607,7 @@ class OuroborosAPI extends Extension
WHERE count >= :tags_min
ORDER BY count DESC, tag ASC LIMIT :start, :max_items
",
- ['tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit]
+ ['tags_min' => $config->get_int(TagListConfig::TAGS_MIN), 'start' => $start, 'max_items' => $limit]
);
break;
}
diff --git a/ext/tag_list/config.php b/ext/tag_list/config.php
new file mode 100644
index 00000000..009be176
--- /dev/null
+++ b/ext/tag_list/config.php
@@ -0,0 +1,31 @@
+ TagListConfig::TYPE_TAGS,
+ "Show related" => TagListConfig::TYPE_RELATED
+ ];
+
+ public const SORT_ALPHABETICAL = "alphabetical";
+ public const SORT_TAG_COUNT = "tagcount";
+
+ public const SORT_CHOICES = [
+ "Tag Count" => TagListConfig::SORT_TAG_COUNT,
+ "Alphabetical" => TagListConfig::SORT_ALPHABETICAL
+ ];
+}
\ No newline at end of file
diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php
index bd3d976f..f32cedb1 100644
--- a/ext/tag_list/main.php
+++ b/ext/tag_list/main.php
@@ -1,18 +1,21 @@
set_default_int("tag_list_length", 15);
- $config->set_default_int("popular_tag_list_length", 15);
- $config->set_default_int("tags_min", 3);
- $config->set_default_string("info_link", 'http://en.wikipedia.org/wiki/$tag');
- $config->set_default_string("tag_list_image_type", 'related');
- $config->set_default_string("tag_list_related_sort", 'alphabetical');
- $config->set_default_string("tag_list_popular_sort", 'tagcount');
- $config->set_default_bool("tag_list_pages", false);
+ $config->set_default_int(TagListConfig::LENGTH, 15);
+ $config->set_default_int(TagListConfig::POPULAR_TAG_LIST_LENGTH, 15);
+ $config->set_default_int(TagListConfig::TAGS_MIN, 3);
+ $config->set_default_string(TagListConfig::INFO_LINK, 'http://en.wikipedia.org/wiki/$tag');
+ $config->set_default_string(TagListConfig::OMIT_TAGS, 'tagme*');
+ $config->set_default_string(TagListConfig::IMAGE_TYPE, TagListConfig::TYPE_RELATED);
+ $config->set_default_string(TagListConfig::RELATED_SORT, TagListConfig::SORT_ALPHABETICAL);
+ $config->set_default_string(TagListConfig::POPULAR_SORT, TagListConfig::SORT_TAG_COUNT);
+ $config->set_default_bool(TagListConfig::PAGES, false);
}
public function onPageRequest(PageRequestEvent $event)
@@ -78,7 +81,7 @@ class TagList extends Extension
public function onPostListBuilding(PostListBuildingEvent $event)
{
global $config, $page;
- if ($config->get_int('tag_list_length') > 0) {
+ if ($config->get_int(TagListConfig::LENGTH) > 0) {
if (!empty($event->search_terms)) {
$this->add_refine_block($page, $event->search_terms);
} else {
@@ -105,8 +108,8 @@ class TagList extends Extension
public function onDisplayingImage(DisplayingImageEvent $event)
{
global $config, $page;
- if ($config->get_int('tag_list_length') > 0) {
- if ($config->get_string('tag_list_image_type') == 'related') {
+ if ($config->get_int(TagListConfig::LENGTH) > 0) {
+ if ($config->get_string(TagListConfig::IMAGE_TYPE) == TagListConfig::TYPE_RELATED) {
$this->add_related_block($page, $event->image);
} else {
if (class_exists("TagCategories") and $config->get_bool(TagCategoriesConfig::SPLIT_ON_VIEW)) {
@@ -121,30 +124,30 @@ class TagList extends Extension
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Tag Map Options");
- $sb->add_int_option("tags_min", "Only show tags used at least ");
+ $sb->add_int_option(TagListConfig::TAGS_MIN, "Only show tags used at least ");
$sb->add_label(" times");
- $sb->add_bool_option("tag_list_pages", "
Paged tag lists: ");
+ $sb->add_bool_option(TagListConfig::PAGES, "
Paged tag lists: ");
$event->panel->add_block($sb);
$sb = new SetupBlock("Popular / Related Tag List");
- $sb->add_int_option("tag_list_length", "Show top ");
+ $sb->add_int_option(TagListConfig::LENGTH, "Show top ");
$sb->add_label(" related tags");
- $sb->add_int_option("popular_tag_list_length", "
Show top ");
+ $sb->add_int_option(TagListConfig::POPULAR_TAG_LIST_LENGTH, "
Show top ");
$sb->add_label(" popular tags");
- $sb->add_text_option("info_link", "
Tag info link: ");
- $sb->add_choice_option("tag_list_image_type", [
- "Image's tags only" => "tags",
- "Show related" => "related"
- ], "
Image tag list: ");
- $sb->add_choice_option("tag_list_related_sort", [
- "Tag Count" => "tagcount",
- "Alphabetical" => "alphabetical"
- ], "
Sort related list by: ");
- $sb->add_choice_option("tag_list_popular_sort", [
- "Tag Count" => "tagcount",
- "Alphabetical" => "alphabetical"
- ], "
Sort popular list by: ");
- $sb->add_bool_option("tag_list_numbers", "
Show tag counts: ");
+ $sb->start_table();
+ $sb->add_text_option(TagListConfig::INFO_LINK, "Tag info link", true);
+ $sb->add_text_option(TagListConfig::OMIT_TAGS, "Omit tags", true);
+ $sb->add_choice_option(TagListConfig::IMAGE_TYPE,
+ TagListConfig::TYPE_CHOICES,
+ "Image tag list", true);
+ $sb->add_choice_option(TagListConfig::RELATED_SORT,
+ TagListConfig::SORT_CHOICES,
+ "Sort related list by", true);
+ $sb->add_choice_option(TagListConfig::POPULAR_SORT,
+ TagListConfig::SORT_CHOICES,
+ "Sort popular list by", true);
+ $sb->add_bool_option("tag_list_numbers", "Show tag counts", true);
+ $sb->end_table();
$event->panel->add_block($sb);
}
// }}}
@@ -165,17 +168,56 @@ class TagList extends Extension
return int_escape($_GET['mincount']);
} else {
global $config;
- return $config->get_int('tags_min'); // get the default.
+ return $config->get_int(TagListConfig::TAGS_MIN); // get the default.
}
}
+ private static function get_omitted_tags(): array
+ {
+ global $config, $database;
+ $tags_config = $config->get_string(TagListConfig::OMIT_TAGS);
+
+ $results = $database->cache->get("tag_list_omitted_tags:".$tags_config);
+
+ if($results==null) {
+ $results = [];
+ $tags = explode(" ", $tags_config);
+
+ if (empty($tags)) {
+ return [];
+ }
+
+ $where = [];
+ $args = [];
+ $i = 0;
+ foreach ($tags as $tag) {
+ $i++;
+ $arg = "tag$i";
+ $args[$arg] = Tag::sqlify($tag);
+ if (strpos($tag, '*') === false
+ && strpos($tag, '?') === false) {
+ $where[] = " tag = :$arg ";
+ } else {
+ $where[] = " tag LIKE :$arg ";
+ }
+
+ }
+
+ $results = $database->get_col("SELECT id FROM tags WHERE " . implode(" OR ", $where), $args);
+
+ $database->cache->set("tag_list_omitted_tags:" . $tags_config, $results, 600);
+
+ }
+ return $results;
+ }
+
private function get_starts_with(): string
{
global $config;
if (isset($_GET['starts_with'])) {
return $_GET['starts_with'] . "%";
} else {
- if ($config->get_bool("tag_list_pages")) {
+ if ($config->get_bool(TagListConfig::PAGES)) {
return "a%";
} else {
return "%";
@@ -244,7 +286,7 @@ class TagList extends Extension
"), ["tags_min"=>$tags_min, "tags_min2"=>$tags_min, "starts_with"=>$starts_with]);
$html = "";
- if ($config->get_bool("tag_list_pages")) {
+ if ($config->get_bool(TagListConfig::PAGES)) {
$html .= $this->build_az();
}
foreach ($tag_data as $row) {
@@ -287,7 +329,7 @@ class TagList extends Extension
"), ["tags_min"=>$tags_min, "starts_with"=>$starts_with]);
$html = "";
- if ($config->get_bool("tag_list_pages")) {
+ if ($config->get_bool(TagListConfig::PAGES)) {
$html .= $this->build_az();
}
@@ -407,27 +449,34 @@ class TagList extends Extension
}
// }}}
// blocks {{{
- private function add_related_block(Page $page, Image $image)
+ private function add_related_block(Page $page, Image $image): void
{
global $database, $config;
- $query = "
- SELECT t3.tag AS tag, t3.count AS calc_count, it3.tag_id
- FROM image_tags AS it1 -- Starting image's tags
- INNER JOIN tags AS t1 ON t1.id = it1.tag_id AND t1.tag NOT LIKE 'tagme%'
- -- Get images with the same tags as the starting image
- INNER JOIN image_tags AS it2 ON it1.tag_id=it2.tag_id
- -- Get the tags from those other images except the same as the starting tags
- INNER JOIN image_tags AS it3 ON it2.image_id=it3.image_id
- LEFT JOIN image_tags it4 ON it4.image_id = it1.image_id AND it4.tag_id = it3.tag_id
- INNER JOIN tags AS t3 ON t3.id = it3.tag_id AND t3.tag NOT LIKE 'tagme%'
- WHERE
- it1.image_id=:image_id
- GROUP BY it3.tag_id, t3.tag, t3.count
- ORDER BY calc_count DESC
+ $omitted_tags = self::get_omitted_tags();
+ $starting_tags = $database->get_col("SELECT tag_id FROM image_tags WHERE image_id = :image_id",["image_id" => $image->id]);
+
+ $starting_tags = array_diff($starting_tags,$omitted_tags);
+
+ if(count($starting_tags) === 0) {
+ // No valid starting tags, so can't look anything up
+ return;
+ }
+
+ $query = "SELECT tags.* FROM tags INNER JOIN (
+ SELECT it2.tag_id
+ FROM image_tags AS it1
+ INNER JOIN image_tags AS it2 ON it1.image_id=it2.image_id
+ AND it2.tag_id NOT IN (".implode(",",array_merge($omitted_tags,$starting_tags)).")
+ WHERE
+ it1.tag_id IN (".implode(",",$starting_tags).")
+ GROUP BY it2.tag_id
+ ) A ON A.tag_id = tags.id
+ ORDER BY count DESC
LIMIT :tag_list_length
";
- $args = ["image_id" => $image->id, "tag_list_length" => $config->get_int('tag_list_length')];
+
+ $args = ["tag_list_length" => $config->get_int(TagListConfig::LENGTH)];
$tags = $database->get_all($query, $args);
if (count($tags) > 0) {
@@ -440,11 +489,11 @@ class TagList extends Extension
global $database;
$query = "
- SELECT tags.tag, tags.count as calc_count
+ SELECT tags.tag, tags.count
FROM tags, image_tags
WHERE tags.id = image_tags.tag_id
AND image_tags.image_id = :image_id
- ORDER BY calc_count DESC
+ ORDER BY tags.count DESC
";
$args = ["image_id"=>$image->id];
@@ -459,11 +508,11 @@ class TagList extends Extension
global $database;
$query = "
- SELECT tags.tag, tags.count as calc_count
+ SELECT tags.tag, tags.count
FROM tags, image_tags
WHERE tags.id = image_tags.tag_id
AND image_tags.image_id = :image_id
- ORDER BY calc_count DESC
+ ORDER BY tags.count DESC
";
$args = ["image_id"=>$image->id];
@@ -479,16 +528,32 @@ class TagList extends Extension
$tags = $database->cache->get("popular_tags");
if (empty($tags)) {
- $query = "
- SELECT tag, count as calc_count
- FROM tags
- WHERE count > 0
- ORDER BY count DESC
- LIMIT :popular_tag_list_length
- ";
- $args = ["popular_tag_list_length"=>$config->get_int('popular_tag_list_length')];
+ $omitted_tags = self::get_omitted_tags();
+
+ if(empty($omitted_tags)) {
+ $query = "
+ SELECT tag, count
+ FROM tags
+ WHERE count > 0
+ ORDER BY count DESC
+ LIMIT :popular_tag_list_length
+ ";
+
+ } else {
+ $query = "
+ SELECT tag, count
+ FROM tags
+ WHERE count > 0
+ AND id NOT IN (".(implode(",", $omitted_tags)).")
+ ORDER BY count DESC
+ LIMIT :popular_tag_list_length
+ ";
+ }
+
+ $args = ["popular_tag_list_length"=>$config->get_int(TagListConfig::POPULAR_TAG_LIST_LENGTH)];
$tags = $database->get_all($query, $args);
+
$database->cache->set("popular_tags", $tags, 600);
}
if (count($tags) > 0) {
@@ -507,6 +572,20 @@ class TagList extends Extension
return;
}
+ $wild_tags = $search;
+
+ $related_tags = self::get_related_tags($search, $config->get_int(TagListConfig::LENGTH));
+
+ if (!empty($related_tags)) {
+ $this->theme->display_refine_block($page, $related_tags, $wild_tags);
+ }
+ }
+
+ public static function get_related_tags(array $search, int $limit): array
+ {
+ global $config, $database;
+
+
$wild_tags = $search;
$str_search = Tag::implode($search);
$related_tags = $database->cache->get("related_tags:$str_search");
@@ -514,53 +593,66 @@ class TagList extends Extension
if (empty($related_tags)) {
// $search_tags = array();
- $tag_id_array = [];
+ $starting_tags = [];
$tags_ok = true;
foreach ($wild_tags as $tag) {
- if ($tag[0] == "-" || strpos($tag, "tagme")===0) {
+ if ($tag[0] == "-") {
continue;
}
- $tag = str_replace("*", "%", $tag);
- $tag = str_replace("?", "_", $tag);
+
+ $tag = Tag::sqlify($tag);
+
$tag_ids = $database->get_col("SELECT id FROM tags WHERE tag LIKE :tag AND count < 25000", ["tag" => $tag]);
// $search_tags = array_merge($search_tags,
// $database->get_col("SELECT tag FROM tags WHERE tag LIKE :tag", array("tag"=>$tag)));
- $tag_id_array = array_merge($tag_id_array, $tag_ids);
+ $starting_tags = array_merge($starting_tags, $tag_ids);
$tags_ok = count($tag_ids) > 0;
if (!$tags_ok) {
break;
}
}
- $tag_id_list = join(', ', $tag_id_array);
- if (count($tag_id_array) > 5 || count($tag_id_array) == 0) {
- return;
+ if (count($starting_tags) > 5 || count($starting_tags) === 0) {
+ return [];
+ }
+
+ $omitted_tags = self::get_omitted_tags();
+
+ $starting_tags = array_diff($starting_tags,$omitted_tags);
+
+ if(count($starting_tags) === 0) {
+ // No valid starting tags, so can't look anything up
+ return [];
}
if ($tags_ok) {
- $query = "
- SELECT t2.tag AS tag, COUNT(it2.image_id) AS calc_count
+ $query = "SELECT t.tag, A.calc_count AS count FROM tags t INNER JOIN (
+ SELECT it2.tag_id, COUNT(it2.image_id) AS calc_count
FROM image_tags AS it1 -- Got other images with the same tags
- -- Get the tags from those images, except those the same as the starting tags
- INNER JOIN image_tags AS it2 ON it1.image_id=it2.image_id AND it2.tag_id NOT IN($tag_id_list)
- -- And filter out anything starting with tagme
- INNER JOIN tags AS t2 ON it2.tag_id = t2.id AND t2.tag NOT LIKE 'tagme%'
+ INNER JOIN image_tags AS it2 ON it1.image_id=it2.image_id
+ -- And filter out unwanted tags
+ AND it2.tag_id NOT IN (".implode(",",array_merge($omitted_tags,$starting_tags)).")
WHERE
- it1.tag_id IN($tag_id_list)
- GROUP BY t2.tag
- ORDER BY calc_count
+ it1.tag_id IN (".implode(",",$starting_tags).")
+ GROUP BY it2.tag_id) A ON A.tag_id = t.id
+ ORDER BY A.calc_count
DESC LIMIT :limit
";
- $args = ["limit"=>$config->get_int('tag_list_length')];
+ $args = ["limit" => $limit];
$related_tags = $database->get_all($query, $args);
- $database->cache->set("related_tags:$str_search", $related_tags, 60*60);
+ $database->cache->set("related_tags:$str_search", $related_tags, 60 * 60);
}
+
+ }
+ if ($related_tags === false) {
+ return [];
+ } else {
+ return $related_tags;
}
- if (!empty($related_tags)) {
- $this->theme->display_refine_block($page, $related_tags, $wild_tags);
- }
}
+
+
// }}}
}
diff --git a/ext/tag_list/theme.php b/ext/tag_list/theme.php
index 73163a96..e01b0994 100644
--- a/ext/tag_list/theme.php
+++ b/ext/tag_list/theme.php
@@ -38,7 +38,7 @@ class TagListTheme extends Themelet
{
global $config;
- $tag_info_link_is_visible = !is_null($config->get_string('info_link'));
+ $tag_info_link_is_visible = !is_null($config->get_string(TagListConfig::INFO_LINK));
$tag_count_is_visible = $config->get_bool("tag_list_numbers");
return '
@@ -68,7 +68,7 @@ class TagListTheme extends Themelet
{
global $config;
- if ($config->get_string('tag_list_related_sort') == 'alphabetical') {
+ if ($config->get_string(TagListConfig::RELATED_SORT) == TagListConfig::SORT_ALPHABETICAL) {
asort($tag_infos);
}
@@ -117,12 +117,12 @@ class TagListTheme extends Themelet
$page->add_block(new Block($category_display_name, $tag_categories_html[$category], "left", 9));
}
- if ($config->get_string('tag_list_image_type')=="tags") {
- if ($main_html != null) {
- $page->add_block(new Block("Tags", $main_html, "left", 10));
- }
- } else {
- $page->add_block(new Block("Related Tags", $main_html, "left", 10));
+ if ($main_html != null) {
+ if ($config->get_string(TagListConfig::IMAGE_TYPE)==TagListConfig::TYPE_TAGS) {
+ $page->add_block(new Block("Tags", $main_html, "left", 10));
+ } else {
+ $page->add_block(new Block("Related Tags", $main_html, "left", 10));
+ }
}
}
@@ -134,7 +134,7 @@ class TagListTheme extends Themelet
*/
private function get_tag_list_html($tag_infos, $sort)
{
- if ($sort == 'alphabetical') {
+ if ($sort == TagListConfig::SORT_ALPHABETICAL) {
asort($tag_infos);
}
@@ -170,10 +170,10 @@ class TagListTheme extends Themelet
$main_html = $this->get_tag_list_html(
$tag_infos,
- $config->get_string('tag_list_related_sort')
+ $config->get_string(TagListConfig::RELATED_SORT)
);
- if ($config->get_string('tag_list_image_type')=="tags") {
+ if ($config->get_string(TagListConfig::IMAGE_TYPE)==TagListConfig::TYPE_TAGS) {
$page->add_block(new Block("Tags", $main_html, "left", 10));
} else {
$page->add_block(new Block("Related Tags", $main_html, "left", 10));
@@ -193,7 +193,7 @@ class TagListTheme extends Themelet
$main_html = $this->get_tag_list_html(
$tag_infos,
- $config->get_string('tag_list_popular_sort')
+ $config->get_string(TagListConfig::POPULAR_SORT)
);
$main_html .= "
Full List\n";
@@ -213,7 +213,7 @@ class TagListTheme extends Themelet
$main_html = $this->get_tag_list_html(
$tag_infos,
- $config->get_string('tag_list_popular_sort')
+ $config->get_string(TagListConfig::POPULAR_SORT)
);
$main_html .= "
Full List\n";
@@ -242,10 +242,10 @@ class TagListTheme extends Themelet
}
$h_tag_no_underscores = str_replace("_", " ", $h_tag);
- $count = $row['calc_count'];
+ $count = $row['count'];
// if($n++) $display_html .= "\n
";
- if (!is_null($config->get_string('info_link'))) {
- $link = html_escape(str_replace('$tag', url_escape($tag), $config->get_string('info_link')));
+ if (!is_null($config->get_string(TagListConfig::INFO_LINK))) {
+ $link = html_escape(str_replace('$tag', url_escape($tag), $config->get_string(TagListConfig::INFO_LINK)));
$display_html .= '