leilukin-tumbleblog/modules/likes/likes.php

520 lines
17 KiB
PHP

<?php
require_once "model".DIR."Like.php";
class Likes extends Modules {
# Array: $caches
# Query caches for methods.
private $caches = array();
public static function __install(): void {
$config = Config::current();
Like::install();
Group::add_permission("like_post", "Like Posts");
Group::add_permission("unlike_post", "Unlike Posts");
$config->set(
"module_likes",
array(
"show_on_index" => true,
"like_with_text" => false,
"like_image" => "pink.svg"
)
);
}
public static function __uninstall($confirm): void {
if ($confirm)
Like::uninstall();
Group::remove_permission("like_post");
Group::remove_permission("unlike_post");
Config::current()->remove("module_likes");
}
public function user_logged_in($user): void {
$_SESSION['likes'] = array();
}
public function user($user): void {
$user->has_many[] = "likes";
}
public function post($post): void {
$post->has_many[] = "likes";
}
public function list_permissions($names = array()): array {
$names["like_post"] = __("Like Posts", "likes");
$names["unlike_post"] = __("Unlike Posts", "likes");
return $names;
}
public function admin_like_settings($admin): void {
$config = Config::current();
if (!Visitor::current()->group->can("change_settings"))
show_403(
__("Access Denied"),
__("You do not have sufficient privileges to change settings.")
);
if (empty($_POST)) {
$admin->display(
"pages".DIR."like_settings",
array("like_images" => $this->like_images())
);
return;
}
if (!isset($_POST['hash']) or !Session::check_token($_POST['hash']))
show_403(
__("Access Denied"),
__("Invalid authentication token.")
);
fallback($_POST['like_image'], $config->chyrp_url."/modules/likes/images/pink.svg");
$config->set(
"module_likes",
array(
"show_on_index" => isset($_POST['show_on_index']),
"like_with_text" => isset($_POST['like_with_text']),
"like_image" => $_POST['like_image']
)
);
Flash::notice(
__("Settings updated."),
"like_settings"
);
}
public function settings_nav($navs): array {
if (Visitor::current()->group->can("change_settings"))
$navs["like_settings"] = array(
"title" => __("Likes", "likes")
);
return $navs;
}
public function main_most_likes($main): void {
$posts = Post::find(array("placeholders" => true));
usort($posts[0], function ($a, $b) {
$count_a = $this->get_post_like_count($a["id"]);
$count_b = $this->get_post_like_count($b["id"]);
if ($count_a == $count_b)
return 0;
return ($count_a > $count_b) ? -1 : 1 ;
});
$main->display(
array("pages".DIR."most_likes", "pages".DIR."index"),
array("posts" => new Paginator($posts, $main->post_limit)),
__("Most liked posts", "likes")
);
}
public function main_like()/*: never */{
if (empty($_GET['post_id']) or !is_numeric($_GET['post_id']))
error(
__("Error"),
__("An ID is required to like a post.", "likes"),
code:400
);
if (BOT_UA or !Visitor::current()->group->can("like_post"))
show_403(
__("Access Denied"),
__("You do not have sufficient privileges to like posts.", "likes")
);
$post = new Post($_GET['post_id']);
if ($post->no_results)
show_404(
__("Not Found"),
__("Post not found.")
);
Like::create($post->id);
Flash::notice(
__("Post liked.", "likes"),
$post->url()."#likes_".$post->id
);
}
public function main_unlike()/*: never */{
if (empty($_GET['post_id']) or !is_numeric($_GET['post_id']))
error(
__("Error"),
__("An ID is required to unlike a post.", "likes"),
code:400
);
if (BOT_UA or !Visitor::current()->group->can("unlike_post"))
show_403(
__("Access Denied"),
__("You do not have sufficient privileges to unlike posts.", "likes")
);
$post = new Post($_GET['post_id']);
if ($post->no_results)
show_404(
__("Not Found"),
__("Post not found.")
);
Like::remove($post->id);
Flash::notice(
__("Post unliked.", "likes"),
$post->url()."#likes_".$post->id
);
}
public function ajax_like(): void {
if (empty($_POST['post_id']) or !is_numeric($_POST['post_id']))
error(
__("Error"),
__("An ID is required to like a post.", "likes"),
code:400
);
# JavaScript can't know if this is allowed, so don't throw an error here.
if (BOT_UA or !Visitor::current()->group->can("like_post")) {
json_response(
__("You do not have sufficient privileges to like posts.", "likes"),
false
);
return;
}
$post = new Post($_POST['post_id']);
if ($post->no_results)
show_404(
__("Not Found"),
__("Post not found.")
);
$count = $post->like_count;
Like::create($post->id);
if ($count <= 0) {
$text = __("You like this.", "likes");
} else {
$p = _p("You and %d person like this.", "You and %d people like this.", $count, "likes");
$text = sprintf($p, $count);
}
json_response($text, true);
}
public function ajax_unlike(): void {
if (empty($_POST['post_id']) or !is_numeric($_POST['post_id']))
error(
__("Error"),
__("An ID is required to unlike a post.", "likes"),
code:400
);
# JavaScript can't know if this is allowed, so don't throw an error here.
if (BOT_UA or !Visitor::current()->group->can("unlike_post")) {
json_response(
__("You do not have sufficient privileges to unlike posts.", "likes"),
false
);
return;
}
$post = new Post($_POST['post_id']);
if ($post->no_results)
show_404(__("Not Found"), __("Post not found."));
$count = $post->like_count - 1;
Like::remove($post->id);
if ($count <= 0) {
$text = __("No likes yet.", "likes");
} else {
$p = _p("%d person likes this.", "%d people like this.", $count, "likes");
$text = sprintf($p, $count);
}
json_response($text, true);
}
public function delete_post($post): void {
SQL::current()->delete(
table:"likes",
conds:array("post_id" => $post->id)
);
}
public function delete_user($user): void {
SQL::current()->update(
table:"likes",
conds:array("user_id" => $user->id),
data:array("user_id" => 0)
);
}
private function get_post_like_count($post_id): int {
if (!isset($this->caches["post_like_counts"])) {
$counts = SQL::current()->select(
tables:"likes",
fields:array("COUNT(post_id) AS total", "post_id AS post_id"),
group:"post_id"
)->fetchAll();
$this->caches["post_like_counts"] = array();
foreach ($counts as $count) {
$id = $count["post_id"];
$total = (int) $count["total"];
$this->caches["post_like_counts"][$id] = $total;
}
}
return fallback($this->caches["post_like_counts"][$post_id], 0);
}
public function post_like_count_attr($attr, $post): int {
if ($post->no_results)
return 0;
return $this->get_post_like_count($post->id);
}
public function get_user_like_count($user_id): int {
if (!isset($this->caches["user_like_counts"])) {
$counts = SQL::current()->select(
tables:"likes",
fields:array("COUNT(user_id) AS total", "user_id AS user_id"),
group:"user_id"
)->fetchAll();
$this->caches["user_like_counts"] = array();
foreach ($counts as $count) {
$id = $count["user_id"];
$total = (int) $count["total"];
$this->caches["user_like_counts"][$id] = $total;
}
}
return fallback($this->caches["user_like_counts"][$user_id], 0);
}
public function user_like_count_attr($attr, $user): int {
if ($user->no_results)
return 0;
return $this->get_user_like_count($user->id);
}
public function visitor_like_count_attr($attr, $visitor): int {
return ($visitor->id == 0) ?
count(fallback($_SESSION['likes'], array())) :
$this->user_like_count_attr($attr, $visitor) ;
}
public function post_like_link_attr($attr, $post): ?string {
$config = Config::current();
$route = Route::current();
$main = MainController::current();
$visitor = Visitor::current();
$settings = $config->module_likes;
if ($post->no_results or !isset($route))
return null;
if ($settings["show_on_index"] == false and $route->action == "index")
return null;
$html = '<div class="likes" id="likes_'.$post->id.'">';
if (!Like::exists($post->id)) {
if ($visitor->group->can("like_post")) {
$html.= '<a class="likes like" href="'.
url("/?action=like&post_id=".
$post->id, $main).
'" data-post_id="'.
$post->id.
'">';
if (!empty($settings["like_image"])) {
$path = $config->chyrp_url.
"/modules/likes/images/".
urlencode($settings["like_image"]);
$html.= '<img src="'.
$path.
'" alt="&#x2764;">';
}
if ($settings["like_with_text"]) {
$html.= ' <span class="like">'.
__("Like!", "likes").
'</span>';
$html.= ' <span class="unlike">'.
__("Unlike!", "likes").
'</span>';
}
$html.= '</a>';
}
$html.= ' <span class="like_text">';
$count = $post->like_count;
if ($count <= 0) {
$html.= __("No likes yet.", "likes");
} else {
$p = _p("%d person likes this.", "%d people like this.", $count, "likes");
$html.= sprintf($p, $count);
}
$html.= '</span>';
} else {
if ($visitor->group->can("unlike_post")) {
$html.= '<a class="likes liked" href="'.
url("/?action=unlike&post_id=".
$post->id, $main).
'" data-post_id="'.
$post->id.
'">';
if (!empty($settings["like_image"])) {
$path = $config->chyrp_url.
"/modules/likes/images/".
urlencode($settings["like_image"]);
$html.= '<img src="'.
$path.
'" alt="&#x2764;">';
}
if ($settings["like_with_text"]) {
$html.= ' <span class="like">'.
__("Like!", "likes").
'</span>';
$html.= ' <span class="unlike">'.
__("Unlike!", "likes").
'</span>';
}
$html.= '</a>';
}
$html.= ' <span class="like_text">';
$count = $post->like_count - 1;
if ($count <= 0) {
$html.= __("You like this.", "likes");
} else {
$p = _p("You and %d person like this.", "You and %d people like this.", $count, "likes");
$html.= sprintf($p, $count);
}
$html.= '</span>';
}
$html.= '</div>';
return $html;
}
private function like_images(): array {
$images = array();
$dir = new DirectoryIterator(MODULES_DIR.DIR."likes".DIR."images");
foreach ($dir as $item) {
if ($item->isFile()) {
$filename = $item->getFilename();
if (preg_match("/.+\.(jpg|jpeg|png|gif|svg)$/i", $filename))
$images[] = $filename;
}
}
return $images;
}
public function manage_posts_column_header(): string {
return '<th class="post_likes value">'.__("Likes", "tags").'</th>';
}
public function manage_posts_column($post): string {
return '<td class="post_likes value">'.$post->like_count.'</td>';
}
public function import_chyrp_post($entry, $post): void {
$chyrp = $entry->children("http://chyrp.net/export/1.0/");
if (!isset($chyrp->like))
return;
foreach ($chyrp->like as $like) {
$timestamp = $like->children(
"http://www.w3.org/2005/Atom"
)->published;
$login = $like->children(
"http://chyrp.net/export/1.0/"
)->login;
$user = new User(array("login" => unfix((string) $login)));
Like::add(
post_id:$post->id,
user_id:(!$user->no_results) ? $user->id : 0,
timestamp:datetime((string) $timestamp),
session_hash:uniqid("imported_", true)
);
}
}
public function posts_export($atom, $post): string {
$likes = SQL::current()->select(
tables:"likes",
conds:array("post_id" => $post->id)
)->fetchAll();
foreach ($likes as $like) {
$user = new User($like["user_id"]);
$login = (!$user->no_results) ? $user->login : "" ;
$atom.= '<chyrp:like>'."\n".
'<chyrp:login>'.
fix($login, false, true).
'</chyrp:login>'."\n".
'<published>'.
when("c", $like["timestamp"]).
'</published>'."\n".
'</chyrp:like>'."\n";
}
return $atom;
}
public function stylesheets($styles): array {
$styles[] = Config::current()->chyrp_url.
"/modules/likes/likes.css";
return $styles;
}
public function javascript(): void {
include MODULES_DIR.DIR."likes".DIR."javascript.php";
}
}