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/core/event.php

349 lines
8.9 KiB
PHP
Raw Normal View History

2021-12-14 18:32:47 +00:00
<?php
declare(strict_types=1);
namespace Shimmie2;
2009-07-19 07:38:13 +00:00
/**
* Generic parent class for all events.
*
* An event is anything that can be passed around via send_event($blah)
*/
abstract class Event
{
public bool $stop_processing = false;
public function __construct()
{
}
2019-12-07 22:49:02 +00:00
public function __toString(): string
2019-12-07 22:49:02 +00:00
{
return var_export($this, true);
}
}
2009-07-19 07:38:13 +00:00
/**
2009-07-21 03:18:40 +00:00
* A wake-up call for extensions. Upon recieving an InitExtEvent an extension
* should check that it's database tables are there and install them if not,
* and set any defaults with Config::set_default_int() and such.
*
* This event is sent before $user is set to anything
*/
class InitExtEvent extends Event
{
}
2009-07-19 07:38:13 +00:00
/**
* A signal that a page has been requested.
*
* User requests /view/42 -> an event is generated with $args = array("view",
2024-02-11 11:34:09 +00:00
* "42"); when an event handler asks $event->page_matches("view/{id}"), it returns
* true and sets $event->get_arg('id') = "42"
*/
class PageRequestEvent extends Event
{
2024-02-11 11:34:09 +00:00
private string $method;
2024-02-09 12:43:53 +00:00
public string $path;
2024-02-09 16:36:57 +00:00
/** @var array<string, string|string[]> */
public array $GET;
/** @var array<string, string|string[]> */
public array $POST;
/**
* @var string[]
*/
2024-02-09 15:10:36 +00:00
public array $args;
2024-02-11 11:34:09 +00:00
/**
* @var array<string, string>
*/
private array $named_args = [];
public int $page_num;
private bool $is_authed;
2024-02-09 16:36:57 +00:00
/**
* @param string $method The HTTP method used to make the request
* @param string $path The path of the request
* @param array<string, string|string[]> $get The GET parameters
* @param array<string, string|string[]> $post The POST parameters
*/
public function __construct(string $method, string $path, array $get, array $post)
{
global $user;
2020-01-26 13:19:35 +00:00
parent::__construct();
global $config;
$this->method = $method;
2024-02-09 12:43:53 +00:00
// if we're looking at the root of the install,
// use the default front page
if ($path == "") {
2019-08-02 19:40:03 +00:00
$path = $config->get_string(SetupConfig::FRONT_PAGE);
}
2024-02-09 12:43:53 +00:00
$this->path = $path;
2024-02-09 16:36:57 +00:00
$this->GET = $get;
$this->POST = $post;
2024-02-11 11:34:09 +00:00
$this->is_authed = (
defined("UNITTEST")
|| (isset($_POST["auth_token"]) && $_POST["auth_token"] == $user->get_auth_token())
);
// break the path into parts
2024-02-11 23:28:48 +00:00
$this->args = explode('/', $path);
}
2024-02-09 16:36:57 +00:00
public function get_GET(string $key): ?string
{
if(array_key_exists($key, $this->GET)) {
if(is_array($this->GET[$key])) {
2024-02-11 15:47:40 +00:00
throw new UserError("GET parameter {$key} is an array, expected single value");
2024-02-09 16:36:57 +00:00
}
return $this->GET[$key];
} else {
return null;
}
}
2024-02-10 00:08:55 +00:00
public function req_GET(string $key): string
{
$value = $this->get_GET($key);
if($value === null) {
2024-02-11 15:47:40 +00:00
throw new UserError("Missing GET parameter {$key}");
2024-02-10 00:08:55 +00:00
}
return $value;
}
2024-02-09 16:36:57 +00:00
public function get_POST(string $key): ?string
{
if(array_key_exists($key, $this->POST)) {
if(is_array($this->POST[$key])) {
2024-02-11 15:47:40 +00:00
throw new UserError("POST parameter {$key} is an array, expected single value");
2024-02-09 16:36:57 +00:00
}
return $this->POST[$key];
} else {
return null;
}
}
2024-02-10 00:08:55 +00:00
public function req_POST(string $key): string
{
$value = $this->get_POST($key);
if($value === null) {
2024-02-11 15:47:40 +00:00
throw new UserError("Missing POST parameter {$key}");
2024-02-10 00:08:55 +00:00
}
return $value;
}
/**
2024-02-10 00:46:35 +00:00
* @return string[]|null
*/
public function get_POST_array(string $key): ?array
{
if(array_key_exists($key, $this->POST)) {
if(!is_array($this->POST[$key])) {
2024-02-11 15:47:40 +00:00
throw new UserError("POST parameter {$key} is a single value, expected array");
2024-02-10 00:46:35 +00:00
}
return $this->POST[$key];
} else {
return null;
}
}
/**
* @return string[]
*/
public function req_POST_array(string $key): array
{
$value = $this->get_POST_array($key);
if($value === null) {
2024-02-11 15:47:40 +00:00
throw new UserError("Missing POST parameter {$key}");
2024-02-10 00:46:35 +00:00
}
return $value;
}
2024-02-11 23:28:48 +00:00
public function page_starts_with(string $name): bool
{
return (count($this->args) >= 1) && ($this->args[0] == $name);
}
2024-02-10 00:46:35 +00:00
/**
* Test if the requested path matches a given pattern.
*
* If it matches, store the remaining path elements in $args
*/
2024-02-11 11:34:09 +00:00
public function page_matches(
string $name,
?string $method = null,
?bool $authed = null,
?string $permission = null,
bool $paged = false,
): bool {
global $user;
2024-02-11 11:34:09 +00:00
if($paged) {
if($this->page_matches("$name/{page_num}", $method, $authed, $permission, false)) {
$pn = $this->get_arg("page_num");
if(is_numberish($pn)) {
return true;
}
}
}
assert($method === null || in_array($method, ["GET", "POST", "OPTIONS"]));
$authed = $authed ?? $method == "POST";
// method check is fast so do that first
if($method !== null && $this->method !== $method) {
return false;
}
// check if the path matches
$parts = explode("/", $name);
2024-02-11 11:34:09 +00:00
$part_count = count($parts);
if ($part_count != count($this->args)) {
return false;
}
2024-02-11 11:34:09 +00:00
$this->named_args = [];
for ($i = 0; $i < $part_count; $i++) {
if (str_starts_with($parts[$i], "{")) {
$this->named_args[substr($parts[$i], 1, -1)] = $this->args[$i];
} elseif ($parts[$i] != $this->args[$i]) {
return false;
}
}
// if we matched the method and the path, but the page requires
// authentication and the user is not authenticated, then complain
if($authed && $this->is_authed === false) {
2024-02-11 15:47:40 +00:00
throw new PermissionDenied("Permission Denied");
}
if($permission !== null && !$user->can($permission)) {
2024-02-11 15:47:40 +00:00
throw new PermissionDenied("Permission Denied");
}
return true;
2024-02-10 18:35:55 +00:00
}
/**
* Get the n th argument of the page request (if it exists.)
*/
2024-02-11 11:34:09 +00:00
public function get_arg(string $n, ?string $default = null): string
{
2024-02-11 11:34:09 +00:00
if(array_key_exists($n, $this->named_args)) {
return rawurldecode($this->named_args[$n]);
} elseif($default !== null) {
return $default;
} else {
2024-02-11 15:47:40 +00:00
throw new UserError("Page argument {$n} is missing");
}
}
2024-02-11 11:34:09 +00:00
public function get_iarg(string $n, ?int $default = null): int
2019-11-04 00:42:06 +00:00
{
2024-02-11 11:34:09 +00:00
if(array_key_exists($n, $this->named_args)) {
if(is_numberish($this->named_args[$n]) === false) {
2024-02-11 15:47:40 +00:00
throw new UserError("Page argument {$n} exists but is not numeric");
}
2024-02-11 11:34:09 +00:00
return int_escape($this->named_args[$n]);
} elseif($default !== null) {
return $default;
2019-11-04 00:42:06 +00:00
} else {
2024-02-11 15:47:40 +00:00
throw new UserError("Page argument {$n} is missing");
}
}
}
class CliGenEvent extends Event
{
public function __construct(
public \Symfony\Component\Console\Application $app
) {
2020-01-26 13:19:35 +00:00
parent::__construct();
}
}
2009-07-19 07:38:13 +00:00
/**
* A signal that some text needs formatting, the event carries
* both the text and the result
*/
class TextFormattingEvent extends Event
{
/**
* For reference
*/
public string $original;
/**
* with formatting applied
*/
public string $formatted;
/**
* with formatting removed
*/
public string $stripped;
public function __construct(string $text)
{
2020-01-26 13:19:35 +00:00
parent::__construct();
2020-02-01 20:01:25 +00:00
// We need to escape before formatting, instead of at display time,
// because formatters will add their own HTML tags into the mix and
// we don't want to escape those.
$h_text = html_escape(trim($text));
$this->original = $h_text;
$this->formatted = $h_text;
$this->stripped = $h_text;
}
}
2009-05-08 10:52:29 +00:00
2009-07-19 07:38:13 +00:00
/**
* A signal that something needs logging
2009-05-08 10:52:29 +00:00
*/
class LogEvent extends Event
{
/**
* a category, normally the extension name
*/
public string $section;
/**
* See python...
*/
public int $priority = 0;
/**
* Free text to be logged
*/
public string $message;
/**
* The time that the event was created
*/
public int $time;
/**
* Extra data to be held separate
*
* @var string[]
*/
public array $args;
public function __construct(string $section, int $priority, string $message)
{
2020-01-26 13:19:35 +00:00
parent::__construct();
$this->section = $section;
$this->priority = $priority;
$this->message = $message;
$this->time = time();
}
2009-05-08 10:52:29 +00:00
}
2019-11-03 18:28:38 +00:00
class DatabaseUpgradeEvent extends Event
{
}