2024-06-20 14:10:42 +00:00
|
|
|
<?php
|
|
|
|
require_once "model".DIR."Category.php";
|
|
|
|
|
|
|
|
class Categorize extends Modules {
|
|
|
|
# Array: $caches
|
|
|
|
# Query caches for methods.
|
|
|
|
private $caches = array();
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public static function __install(
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
Category::install();
|
|
|
|
|
|
|
|
Group::add_permission("manage_categorize", "Manage Categories");
|
|
|
|
Route::current()->add("category/(name)/", "category");
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public static function __uninstall(
|
|
|
|
$confirm
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if ($confirm)
|
|
|
|
Category::uninstall();
|
|
|
|
|
|
|
|
Group::remove_permission("manage_categorize");
|
|
|
|
Route::current()->remove("category/(name)/");
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function list_permissions(
|
|
|
|
$names = array()
|
|
|
|
): array {
|
2024-06-20 14:10:42 +00:00
|
|
|
$names["manage_categorize"] = __("Manage Categories", "categorize");
|
|
|
|
return $names;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function feed_item(
|
|
|
|
$post,
|
|
|
|
$feed
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!empty($post->category))
|
|
|
|
$feed->category(
|
|
|
|
$post->category->clean,
|
|
|
|
url("category", MainController::current()),
|
|
|
|
$post->category->name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function related_posts(
|
|
|
|
$ids,
|
|
|
|
$post,
|
|
|
|
$limit
|
|
|
|
): array {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (empty($post->category_id))
|
|
|
|
return $ids;
|
|
|
|
|
|
|
|
$results = SQL::current()->select(
|
|
|
|
tables:"post_attributes",
|
|
|
|
fields:array("post_id"),
|
|
|
|
conds:array(
|
|
|
|
"name" => "category_id",
|
|
|
|
"value" => $post->category_id,
|
|
|
|
"post_id !=" => $post->id
|
|
|
|
),
|
|
|
|
order:array("post_id DESC"),
|
|
|
|
limit:$limit
|
|
|
|
)->fetchAll();
|
|
|
|
|
|
|
|
foreach ($results as $result)
|
|
|
|
$ids[] = $result["post_id"];
|
|
|
|
|
|
|
|
return $ids;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function parse_urls(
|
|
|
|
$urls
|
|
|
|
): array {
|
2024-06-20 14:10:42 +00:00
|
|
|
$urls['|/category/([^/]+)/|'] = '/?action=category&name=$1';
|
|
|
|
return $urls;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function manage_posts_column_header(
|
|
|
|
): string {
|
2024-06-20 14:10:42 +00:00
|
|
|
return '<th class="post_category value">'.
|
|
|
|
__("Category", "categorize").'</th>';
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function manage_posts_column(
|
|
|
|
$post
|
|
|
|
): string {
|
2024-06-20 14:10:42 +00:00
|
|
|
$td = '<td class="post_category value">';
|
|
|
|
|
|
|
|
if (isset($post->category->name))
|
|
|
|
$td.= '<a href="'.
|
|
|
|
url("manage_category/query/".urlencode("id:".$post->category->id)).
|
|
|
|
'">'.
|
|
|
|
$post->category->name.
|
|
|
|
'</a>';
|
|
|
|
|
|
|
|
$td.= '</td>';
|
|
|
|
|
|
|
|
return $td;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function post_options(
|
|
|
|
$fields,
|
|
|
|
$post = null
|
|
|
|
): array {
|
2024-06-20 14:10:42 +00:00
|
|
|
$options[0]["value"] = "0";
|
|
|
|
$options[0]["name"] = __("[None]", "categorize");
|
|
|
|
$options[0]["selected"] = empty($post->category_id);
|
|
|
|
|
|
|
|
foreach (Category::find() as $category) {
|
|
|
|
$name = oneof($category->name, __("[Untitled]"));
|
|
|
|
$selected = (isset($post) and ($post->category_id == $category->id));
|
|
|
|
|
|
|
|
$options[$category->id]["value"] = $category->id;
|
|
|
|
$options[$category->id]["name"] = $name;
|
|
|
|
$options[$category->id]["selected"] = $selected;
|
|
|
|
}
|
|
|
|
|
|
|
|
$fields[] = array(
|
|
|
|
"attr" => "option[category_id]",
|
|
|
|
"label" => __("Category", "categorize"),
|
|
|
|
"help" => "categorizing_posts",
|
|
|
|
"type" => "select",
|
|
|
|
"options" => $options
|
|
|
|
);
|
|
|
|
|
|
|
|
return $fields;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function post(
|
|
|
|
$post
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!empty($post->category_id)) {
|
|
|
|
$category = new Category($post->category_id);
|
|
|
|
|
|
|
|
if (!$category->no_results)
|
|
|
|
$post->category = $category;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
private function get_category_post_count(
|
|
|
|
$category_id
|
|
|
|
): int {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!isset($this->caches["category_post_counts"])) {
|
|
|
|
$counts = SQL::current()->select(
|
|
|
|
tables:"post_attributes",
|
|
|
|
fields:array("COUNT(value) AS total", "value AS category_id"),
|
|
|
|
conds:array("name" => "category_id"),
|
|
|
|
group:"value"
|
|
|
|
)->fetchAll();
|
|
|
|
|
|
|
|
$this->caches["category_post_counts"] = array();
|
|
|
|
|
|
|
|
foreach ($counts as $count) {
|
|
|
|
$id = $count["category_id"];
|
|
|
|
$total = (int) $count["total"];
|
|
|
|
$this->caches["category_post_counts"][$id] = $total;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fallback($this->caches["category_post_counts"][$category_id], 0);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function category_post_count_attr(
|
|
|
|
$attr,
|
|
|
|
$category
|
|
|
|
): int {
|
2024-06-20 14:10:42 +00:00
|
|
|
if ($category->no_results)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return $this->get_category_post_count($category->id);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function twig_context_main(
|
|
|
|
$context
|
|
|
|
): array {
|
2024-06-20 14:10:42 +00:00
|
|
|
$context["categorize"] = array();
|
|
|
|
|
|
|
|
foreach (Category::find() as $category) {
|
|
|
|
if ($category->show_on_home)
|
|
|
|
$context["categorize"][] = $category;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $context;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function main_category(
|
|
|
|
$main
|
|
|
|
): void {
|
2024-09-05 17:51:48 +00:00
|
|
|
if (!isset($_GET['name']))
|
|
|
|
Flash::warning(
|
|
|
|
__("You did not specify a category.", "categorize"),
|
|
|
|
"/"
|
2024-06-20 14:10:42 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$category = new Category(
|
|
|
|
array("clean" => $_GET['name'])
|
|
|
|
);
|
|
|
|
|
2024-09-05 17:51:48 +00:00
|
|
|
if ($category->no_results)
|
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("The category you specified was not found.", "categorize")
|
2024-06-20 14:10:42 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$results = SQL::current()->select(
|
|
|
|
tables:"post_attributes",
|
|
|
|
fields:array("post_id"),
|
|
|
|
conds:array(
|
|
|
|
"name" => "category_id",
|
|
|
|
"value" => $category->id
|
|
|
|
)
|
|
|
|
)->fetchAll();
|
|
|
|
|
|
|
|
$ids = array();
|
|
|
|
|
|
|
|
foreach ($results as $result)
|
|
|
|
$ids[] = $result["post_id"];
|
|
|
|
|
2024-09-05 17:51:48 +00:00
|
|
|
if (empty($ids))
|
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("There are no posts in the category you specified.", "categorize")
|
2024-06-20 14:10:42 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$posts = new Paginator(
|
|
|
|
Post::find(
|
|
|
|
array(
|
|
|
|
"placeholders" => true,
|
|
|
|
"where" => array("id" => $ids)
|
|
|
|
)
|
|
|
|
),
|
|
|
|
$main->post_limit
|
|
|
|
);
|
|
|
|
|
2024-09-05 17:51:48 +00:00
|
|
|
if (!$posts->total)
|
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("There are no posts in the category you specified.", "categorize")
|
|
|
|
);
|
2024-06-20 14:10:42 +00:00
|
|
|
|
|
|
|
$main->display(
|
|
|
|
array("pages".DIR."category", "pages".DIR."index"),
|
|
|
|
array(
|
|
|
|
"posts" => $posts,
|
|
|
|
"category" => $category->name
|
|
|
|
),
|
|
|
|
_f("Posts in category “%s”", fix($category->name), "categorize")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function manage_nav(
|
|
|
|
$navs
|
|
|
|
): array {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (Visitor::current()->group->can("manage_categorize"))
|
|
|
|
$navs["manage_category"] = array(
|
|
|
|
"title" => __("Categories", "categorize"),
|
|
|
|
"selected" => array(
|
|
|
|
"new_category",
|
|
|
|
"delete_category",
|
|
|
|
"edit_category"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
return $navs;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_determine_action(
|
|
|
|
$action
|
|
|
|
): ?string {
|
2024-06-20 14:10:42 +00:00
|
|
|
$visitor = Visitor::current();
|
|
|
|
|
|
|
|
if ($action == "manage" and $visitor->group->can("manage_categorize"))
|
|
|
|
return "manage_category";
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_manage_category(
|
|
|
|
$admin
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!Visitor::current()->group->can("manage_categorize"))
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to manage categories.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
# Redirect searches to a clean URL or dirty GET depending on configuration.
|
|
|
|
if (isset($_POST['query']))
|
|
|
|
redirect(
|
|
|
|
"manage_category/query/".
|
2024-09-05 17:51:48 +00:00
|
|
|
str_ireplace(
|
|
|
|
array("%2F", "%5C"),
|
|
|
|
"%5F",
|
|
|
|
urlencode($_POST['query'])
|
|
|
|
).
|
2024-06-20 14:10:42 +00:00
|
|
|
"/"
|
|
|
|
);
|
|
|
|
|
|
|
|
fallback($_GET['query'], "");
|
|
|
|
list($where, $params, $order) = keywords(
|
|
|
|
$_GET['query'],
|
|
|
|
"name LIKE :query",
|
|
|
|
"categorize"
|
|
|
|
);
|
|
|
|
|
|
|
|
$categorize = Category::find(
|
|
|
|
array(
|
|
|
|
"where" => $where,
|
|
|
|
"params" => $params,
|
|
|
|
"order" => $order
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
$admin->display(
|
|
|
|
"pages".DIR."manage_category",
|
|
|
|
array("categorize" => $categorize)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_new_category(
|
|
|
|
$admin
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!Visitor::current()->group->can("manage_categorize"))
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to add categories.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
$admin->display("pages".DIR."new_category");
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_add_category(
|
|
|
|
$admin
|
|
|
|
): never {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!Visitor::current()->group->can("manage_categorize"))
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to add categories.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!isset($_POST['hash']) or !Session::check_token($_POST['hash']))
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("Invalid authentication token.")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (empty($_POST['name']))
|
|
|
|
error(
|
|
|
|
__("No Name Specified", "categorize"),
|
|
|
|
__("A name is required to add a category.", "categorize"),
|
|
|
|
code:400
|
|
|
|
);
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
fallback($_POST['clean'], $_POST['name']);
|
2024-06-20 14:10:42 +00:00
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
$clean = Category::check_clean(
|
|
|
|
oneof(
|
|
|
|
sanitize($_POST['clean'], true, SLUG_STRICT, 128),
|
|
|
|
md5($_POST['name'])
|
|
|
|
)
|
|
|
|
);
|
2024-06-20 14:10:42 +00:00
|
|
|
|
|
|
|
Category::add(
|
|
|
|
name:$_POST['name'],
|
|
|
|
clean:$clean,
|
|
|
|
show_on_home:!empty($_POST['show_on_home'])
|
|
|
|
);
|
|
|
|
|
|
|
|
Flash::notice(
|
|
|
|
__("Category added.", "categorize"),
|
|
|
|
"manage_category"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_edit_category(
|
|
|
|
$admin
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (empty($_GET['id']) or !is_numeric($_GET['id']))
|
|
|
|
error(
|
|
|
|
__("No ID Specified"),
|
|
|
|
__("An ID is required to edit a category.", "categorize"),
|
|
|
|
code:400
|
|
|
|
);
|
|
|
|
|
|
|
|
$category = new Category($_GET['id']);
|
|
|
|
|
|
|
|
if ($category->no_results)
|
2024-09-05 17:51:48 +00:00
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("Category not found.", "categorize")
|
2024-06-20 14:10:42 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!$category->editable())
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to edit this category.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
$admin->display(
|
|
|
|
"pages".DIR."edit_category",
|
|
|
|
array("category" => $category)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_update_category(
|
|
|
|
$admin
|
|
|
|
): never {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!isset($_POST['hash']) or !Session::check_token($_POST['hash']))
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("Invalid authentication token.")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (empty($_POST['id']) or !is_numeric($_POST['id']))
|
|
|
|
error(
|
|
|
|
__("No ID Specified"),
|
|
|
|
__("An ID is required to update a category.", "categorize"),
|
|
|
|
code:400
|
|
|
|
);
|
|
|
|
|
|
|
|
if (empty($_POST['name']))
|
|
|
|
error(
|
|
|
|
__("No Name Specified", "categorize"),
|
|
|
|
__("A name is required to update a category.", "categorize"),
|
|
|
|
code:400
|
|
|
|
);
|
|
|
|
|
|
|
|
$category = new Category($_POST['id']);
|
|
|
|
|
|
|
|
if ($category->no_results)
|
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("Category not found.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!$category->editable())
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to edit this category.", "categorize")
|
|
|
|
);
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
fallback($_POST['clean'], $_POST['name']);
|
2024-06-20 14:10:42 +00:00
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
$clean = ($_POST['clean'] != $category->clean) ?
|
|
|
|
Category::check_clean(
|
|
|
|
oneof(
|
|
|
|
sanitize($_POST['clean'], true, SLUG_STRICT, 128),
|
|
|
|
md5($_POST['name'])
|
|
|
|
)
|
|
|
|
)
|
|
|
|
:
|
|
|
|
$category->clean
|
|
|
|
;
|
2024-06-20 14:10:42 +00:00
|
|
|
|
|
|
|
$category = $category->update(
|
|
|
|
name:$_POST['name'],
|
|
|
|
clean:$clean,
|
|
|
|
show_on_home:!empty($_POST['show_on_home'])
|
|
|
|
);
|
|
|
|
|
|
|
|
Flash::notice(
|
|
|
|
__("Category updated.", "categorize"),
|
|
|
|
"manage_category"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_delete_category(
|
|
|
|
$admin
|
|
|
|
): void {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (empty($_GET['id']) or !is_numeric($_GET['id']))
|
|
|
|
error(
|
|
|
|
__("No ID Specified"),
|
|
|
|
__("An ID is required to delete a category.", "categorize"),
|
|
|
|
code:400
|
|
|
|
);
|
|
|
|
|
|
|
|
$category = new Category($_GET['id']);
|
|
|
|
|
|
|
|
if ($category->no_results)
|
2024-09-05 17:51:48 +00:00
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("Category not found.", "categorize")
|
2024-06-20 14:10:42 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!$category->deletable())
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to delete this category.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
$admin->display(
|
|
|
|
"pages".DIR."delete_category",
|
|
|
|
array("category" => $category)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-01-13 09:56:01 +00:00
|
|
|
public function admin_destroy_category(
|
|
|
|
): never {
|
2024-06-20 14:10:42 +00:00
|
|
|
if (!isset($_POST['hash']) or !Session::check_token($_POST['hash']))
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("Invalid authentication token.")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (empty($_POST['id']) or !is_numeric($_POST['id']))
|
|
|
|
error(
|
|
|
|
__("No ID Specified"),
|
|
|
|
__("An ID is required to delete a category.", "categorize"),
|
|
|
|
code:400
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!isset($_POST['destroy']) or $_POST['destroy'] != "indubitably")
|
|
|
|
redirect("manage_category");
|
|
|
|
|
|
|
|
$category = new Category($_POST['id']);
|
|
|
|
|
|
|
|
if ($category->no_results)
|
|
|
|
show_404(
|
|
|
|
__("Not Found"),
|
|
|
|
__("Category not found.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!$category->deletable())
|
|
|
|
show_403(
|
|
|
|
__("Access Denied"),
|
|
|
|
__("You do not have sufficient privileges to delete this category.", "categorize")
|
|
|
|
);
|
|
|
|
|
|
|
|
Category::delete($category->id);
|
|
|
|
Flash::notice(
|
|
|
|
__("Category deleted.", "categorize"),
|
|
|
|
"manage_category"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|