This repository has been archived on 2024-09-05. You can view files and clone it, but cannot push or open issues or pull requests.
shimmie2/ext/pm/main.php

307 lines
10 KiB
PHP
Raw Normal View History

2021-12-14 18:32:47 +00:00
<?php
declare(strict_types=1);
namespace Shimmie2;
2023-02-04 13:27:27 +00:00
use GQLA\Type;
use GQLA\Field;
use GQLA\Query;
2023-02-04 14:47:26 +00:00
use GQLA\Mutation;
2023-02-01 12:50:00 +00:00
2023-07-03 15:03:34 +00:00
use function MicroHTML\{emptyHTML, SPAN};
class SendPMEvent extends Event
{
public PM $pm;
public function __construct(PM $pm)
{
2020-01-26 13:19:35 +00:00
parent::__construct();
$this->pm = $pm;
}
}
2023-02-04 14:47:26 +00:00
#[Type(name: "PrivateMessage")]
class PM
{
2023-02-04 14:47:26 +00:00
public int $id = -1;
public int $from_id;
public string $from_ip;
public int $to_id;
2023-02-04 13:27:27 +00:00
public mixed $sent_date;
#[Field]
public string $subject;
2023-02-04 13:27:27 +00:00
#[Field]
public string $message;
2023-02-04 13:27:27 +00:00
#[Field]
public bool $is_read;
2023-02-04 14:47:26 +00:00
#[Field]
public function from(): User
{
return User::by_id($this->from_id);
}
2023-02-13 22:28:50 +00:00
#[Field(name: "pm_id")]
public function graphql_oid(): int
{
return $this->id;
}
#[Field(name: "id")]
public function graphql_guid(): string
{
return "pm:{$this->id}";
}
2023-02-04 14:47:26 +00:00
public function __construct(
int $from_id,
string $from_ip,
int $to_id,
string $subject,
string $message,
bool $is_read = false
) {
$this->from_id = $from_id;
$this->from_ip = $from_ip;
$this->to_id = $to_id;
$this->subject = $subject;
$this->message = $message;
$this->is_read = $is_read;
}
/**
* @param array<string, mixed> $row
*/
2023-02-04 14:47:26 +00:00
public static function from_row(array $row): PM
{
$pm = new PM(
(int)$row["from_id"],
$row["from_ip"],
(int)$row["to_id"],
$row["subject"],
$row["message"],
bool_escape($row["is_read"]),
);
$pm->id = (int)$row["id"];
$pm->sent_date = $row["sent_date"];
return $pm;
}
/**
* @return PM[]|null
*/
2023-02-04 14:47:26 +00:00
#[Field(extends: "User", name: "private_messages", type: "[PrivateMessage!]")]
public static function get_pms(User $duser): ?array
{
2023-02-04 14:47:26 +00:00
global $database, $user;
if (!$user->can(Permissions::READ_PM)) {
return null;
}
if (($duser->id != $user->id) && !$user->can(Permissions::VIEW_OTHER_PMS)) {
return null;
}
2023-02-04 14:47:26 +00:00
$pms = [];
$arr = $database->get_all(
"SELECT * FROM private_message WHERE to_id = :to_id ORDER BY sent_date DESC",
["to_id" => $duser->id]
);
foreach ($arr as $pm) {
$pms[] = PM::from_row($pm);
}
return $pms;
}
2023-02-07 13:27:20 +00:00
#[Field(extends: "User", name: "private_message_unread_count")]
public static function count_unread_pms(User $duser): ?int
{
global $database, $user;
if (!$user->can(Permissions::READ_PM)) {
return null;
}
if (($duser->id != $user->id) && !$user->can(Permissions::VIEW_OTHER_PMS)) {
return null;
}
return (int)$database->get_one(
"SELECT COUNT(*) FROM private_message WHERE to_id = :to_id AND is_read = :is_read",
["is_read" => false, "to_id" => $duser->id]
);
}
2023-02-04 18:24:34 +00:00
#[Mutation(name: "create_private_message")]
2023-02-13 22:28:50 +00:00
public static function send_pm(int $to_user_id, string $subject, string $message): bool
2023-02-04 14:47:26 +00:00
{
global $user;
if (!$user->can(Permissions::SEND_PM)) {
return false;
}
2023-02-13 22:28:50 +00:00
send_event(new SendPMEvent(new PM($user->id, get_real_ip(), $to_user_id, $subject, $message)));
2023-02-04 14:47:26 +00:00
return true;
}
2009-10-08 16:44:25 +00:00
}
class PrivMsg extends Extension
{
2020-01-26 13:19:35 +00:00
/** @var PrivMsgTheme */
2023-06-27 14:56:49 +00:00
protected Themelet $theme;
2020-01-26 13:19:35 +00:00
public function onDatabaseUpgrade(DatabaseUpgradeEvent $event): void
{
global $database;
// shortcut to latest
if ($this->get_version("pm_version") < 1) {
$database->create_table("private_message", "
2009-05-11 21:09:24 +00:00
id SCORE_AIPK,
from_id INTEGER NOT NULL,
from_ip SCORE_INET NOT NULL,
to_id INTEGER NOT NULL,
2019-11-03 19:25:51 +00:00
sent_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
2009-05-11 21:09:24 +00:00
subject VARCHAR(64) NOT NULL,
message TEXT NOT NULL,
is_read BOOLEAN NOT NULL DEFAULT FALSE,
FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE
2009-05-11 21:09:24 +00:00
");
$database->execute("CREATE INDEX private_message__to_id ON private_message(to_id)");
$this->set_version("pm_version", 3);
}
if ($this->get_version("pm_version") < 2) {
log_info("pm", "Adding foreign keys to private messages");
2020-10-25 21:34:52 +00:00
$database->execute("delete from private_message where to_id not in (select id from users);");
$database->execute("delete from private_message where from_id not in (select id from users);");
$database->execute("ALTER TABLE private_message
ADD FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE,
ADD FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE;");
$this->set_version("pm_version", 2);
2020-10-26 17:13:41 +00:00
}
if ($this->get_version("pm_version") < 3) {
2020-10-26 17:28:21 +00:00
$database->standardise_boolean("private_message", "is_read", true);
$this->set_version("pm_version", 3);
}
}
public function onPageSubNavBuilding(PageSubNavBuildingEvent $event): void
{
global $user;
2023-11-11 21:49:12 +00:00
if ($event->parent === "user") {
if ($user->can(Permissions::READ_PM)) {
$count = $this->count_pms($user);
2023-11-11 21:49:12 +00:00
$h_count = $count > 0 ? SPAN(["class" => 'unread'], "($count)") : "";
2023-07-03 15:03:34 +00:00
$event->add_nav_link("pm", new Link('user#private-messages'), emptyHTML("Private Messages", $h_count));
}
}
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event): void
{
global $user;
if ($user->can(Permissions::READ_PM)) {
$count = $this->count_pms($user);
2023-11-11 21:49:12 +00:00
$h_count = $count > 0 ? SPAN(["class" => 'unread'], "($count)") : "";
2023-07-03 15:03:34 +00:00
$event->add_link(emptyHTML("Private Messages", $h_count), make_link("user", null, "private-messages"));
}
}
public function onUserPageBuilding(UserPageBuildingEvent $event): void
{
global $page, $user;
$duser = $event->display_user;
if (!$user->is_anonymous() && !$duser->is_anonymous()) {
2023-02-04 14:47:26 +00:00
$pms = PM::get_pms($duser);
if (!is_null($pms)) {
$this->theme->display_pms($page, $pms);
}
if ($user->can(Permissions::SEND_PM) && $user->id != $duser->id) {
$this->theme->display_composer($page, $user, $duser);
}
}
}
public function onPageRequest(PageRequestEvent $event): void
{
global $cache, $database, $page, $user;
2024-02-11 11:34:09 +00:00
if ($event->page_matches("pm/read/{pm_id}", permission: Permissions::READ_PM)) {
$pm_id = $event->get_iarg('pm_id');
$pm = $database->get_row("SELECT * FROM private_message WHERE id = :id", ["id" => $pm_id]);
if (is_null($pm)) {
$this->theme->display_error(404, "No such PM", "There is no PM #$pm_id");
} elseif (($pm["to_id"] == $user->id) || $user->can(Permissions::VIEW_OTHER_PMS)) {
$from_user = User::by_id((int)$pm["from_id"]);
if ($pm["to_id"] == $user->id) {
$database->execute("UPDATE private_message SET is_read=true WHERE id = :id", ["id" => $pm_id]);
$cache->delete("pm-count-{$user->id}");
}
$pmo = PM::from_row($pm);
$this->theme->display_message($page, $from_user, $user, $pmo);
2024-08-31 16:05:18 +00:00
if ($user->can(Permissions::SEND_PM)) {
$this->theme->display_composer($page, $user, $from_user, "Re: ".$pmo->subject);
}
} else {
2024-02-11 15:47:40 +00:00
throw new PermissionDenied("You do not have permission to view this PM");
}
}
if ($event->page_matches("pm/delete", method: "POST", permission: Permissions::READ_PM)) {
$pm_id = int_escape($event->req_POST("pm_id"));
$pm = $database->get_row("SELECT * FROM private_message WHERE id = :id", ["id" => $pm_id]);
if (is_null($pm)) {
$this->theme->display_error(404, "No such PM", "There is no PM #$pm_id");
} elseif (($pm["to_id"] == $user->id) || $user->can(Permissions::VIEW_OTHER_PMS)) {
$database->execute("DELETE FROM private_message WHERE id = :id", ["id" => $pm_id]);
$cache->delete("pm-count-{$user->id}");
log_info("pm", "Deleted PM #$pm_id", "PM deleted");
$page->set_mode(PageMode::REDIRECT);
$page->set_redirect(referer_or(make_link()));
}
}
if ($event->page_matches("pm/send", method: "POST", permission: Permissions::SEND_PM)) {
$to_id = int_escape($event->req_POST("to_id"));
$from_id = $user->id;
$subject = $event->req_POST("subject");
$message = $event->req_POST("message");
send_event(new SendPMEvent(new PM($from_id, get_real_ip(), $to_id, $subject, $message)));
$page->flash("PM sent");
$page->set_mode(PageMode::REDIRECT);
$page->set_redirect(referer_or(make_link()));
}
}
public function onSendPM(SendPMEvent $event): void
{
global $cache, $database;
$database->execute(
"
2009-05-11 21:09:24 +00:00
INSERT INTO private_message(
from_id, from_ip, to_id,
sent_date, subject, message)
2011-02-22 22:29:31 +00:00
VALUES(:fromid, :fromip, :toid, now(), :subject, :message)",
["fromid" => $event->pm->from_id, "fromip" => $event->pm->from_ip,
"toid" => $event->pm->to_id, "subject" => $event->pm->subject, "message" => $event->pm->message]
);
$cache->delete("pm-count-{$event->pm->to_id}");
log_info("pm", "Sent PM to User #{$event->pm->to_id}");
}
private function count_pms(User $user): int
{
2023-12-14 17:06:54 +00:00
global $database;
2023-12-14 17:06:54 +00:00
return cache_get_or_set(
"pm-count-{$user->id}",
2023-12-14 17:06:54 +00:00
fn () => $database->get_one("
SELECT count(*)
FROM private_message
WHERE to_id = :to_id
AND is_read = :is_read
", ["to_id" => $user->id, "is_read" => false]),
600
);
}
}