520 lines
17 KiB
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="❤">';
|
||
|
}
|
||
|
|
||
|
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="❤">';
|
||
|
}
|
||
|
|
||
|
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";
|
||
|
}
|
||
|
}
|