143 lines
6.2 KiB
PHP
143 lines
6.2 KiB
PHP
<?php
|
|
|
|
namespace com\fwdekker\deathnotifier\tracking;
|
|
|
|
use com\fwdekker\deathnotifier\Action;
|
|
use com\fwdekker\deathnotifier\IllegalStateError;
|
|
use com\fwdekker\deathnotifier\UnexpectedException;
|
|
use com\fwdekker\deathnotifier\validation\HasStringLengthRule;
|
|
use com\fwdekker\deathnotifier\validation\InvalidTypeException;
|
|
use com\fwdekker\deathnotifier\validation\InvalidValueException;
|
|
use com\fwdekker\deathnotifier\validation\IsNotBlankRule;
|
|
use com\fwdekker\deathnotifier\validation\IsValidCsrfTokenRule;
|
|
use com\fwdekker\deathnotifier\validation\RuleSet;
|
|
use com\fwdekker\deathnotifier\validation\LoginValidator;
|
|
use com\fwdekker\deathnotifier\wikipedia\ArticleType;
|
|
use com\fwdekker\deathnotifier\wikipedia\PersonStatus;
|
|
use com\fwdekker\deathnotifier\wikipedia\Wikipedia;
|
|
use com\fwdekker\deathnotifier\wikipedia\WikipediaException;
|
|
|
|
|
|
/**
|
|
* Adds a tracking.
|
|
*/
|
|
class AddTrackingAction extends Action
|
|
{
|
|
/**
|
|
* @var TrackingList the list to add the tracking to
|
|
*/
|
|
private readonly TrackingList $tracking_list;
|
|
/**
|
|
* @var Wikipedia the API of Wikipedia
|
|
*/
|
|
private readonly Wikipedia $wikipedia;
|
|
|
|
|
|
/**
|
|
* Constructs a new `AddTrackingAction`.
|
|
*
|
|
* @param TrackingList $tracking_list the list to add the tracking to
|
|
* @param Wikipedia $wikipedia the API of Wikipedia
|
|
*/
|
|
public function __construct(TrackingList $tracking_list, Wikipedia $wikipedia)
|
|
{
|
|
$this->tracking_list = $tracking_list;
|
|
$this->wikipedia = $wikipedia;
|
|
}
|
|
|
|
|
|
/**
|
|
* Adds a tracking by the current user of the specified person.
|
|
*
|
|
* Requires that the user is logged in and that a valid CSRF token is present.
|
|
*
|
|
* @param array<int|string, mixed> $inputs `"token": string`: a valid CSRF token, `"person_name": string`: the name
|
|
* of the person to track
|
|
* @return array{"input_name": string, "normalized_name": string} the person's name as given by the user, and the
|
|
* normalized version of that name
|
|
* @throws InvalidTypeException if any of the inputs has the incorrect type
|
|
* @throws InvalidValueException if the user is not logged in, if no valid CSRF token is present, if the article
|
|
* title is a blank string, if the article title is too short or too long, if the specified article does not exist,
|
|
* if the specified article is not about a person, or if the user is already tracking this article
|
|
* @throws UnexpectedException if Wikipedia could not be reached
|
|
*/
|
|
public function handle(array $inputs): array
|
|
{
|
|
(new LoginValidator(validate_logged_in: true))->check($_SESSION);
|
|
(new RuleSet([
|
|
"token" => [new IsValidCsrfTokenRule()],
|
|
"person_name" => [
|
|
new IsNotBlankRule(),
|
|
new HasStringLengthRule(TrackingList::MIN_TITLE_LENGTH, TrackingList::MAX_TITLE_LENGTH)
|
|
],
|
|
]))->check($inputs);
|
|
|
|
[$normalized_name, $status] = $this->get_and_validate_page_info(strval($inputs["person_name"]));
|
|
$this->tracking_list->transaction(function () use ($normalized_name, $status) {
|
|
if ($this->tracking_list->has_tracking($_SESSION["uuid"], $normalized_name))
|
|
throw new InvalidValueException("You are already tracking <b>$normalized_name</b>.");
|
|
|
|
$this->tracking_list->add_tracking($_SESSION["uuid"], $normalized_name, $status);
|
|
});
|
|
|
|
return ["input_name" => $inputs["person_name"], "normalized_name" => $normalized_name];
|
|
}
|
|
|
|
/**
|
|
* Validates that {@see $person_name} is an article about a person, and returns information about that article.
|
|
*
|
|
* @param string $person_name the title of the article about a person to return the information of
|
|
* @return array{string, PersonStatus} the normalized name and `PersonStatus` of the specified article
|
|
* @throws InvalidValueException if the article about {@see $person_name} does not exist or is not about a person
|
|
* @throws UnexpectedException if Wikipedia could not be reached
|
|
*/
|
|
private function get_and_validate_page_info(string $person_name): array
|
|
{
|
|
try {
|
|
$info = $this->wikipedia->query_person_info([$person_name]);
|
|
|
|
$normalized_name = $info->redirects[$person_name];
|
|
$type = $info->results[$normalized_name]["type"];
|
|
$status = $info->results[$normalized_name]["status"];
|
|
} catch (WikipediaException $exception) {
|
|
throw new UnexpectedException(
|
|
"Could not reach Wikipedia. Maybe the website is down?",
|
|
previous: $exception
|
|
);
|
|
}
|
|
|
|
if (in_array($normalized_name, $info->missing)) {
|
|
throw new InvalidValueException(
|
|
$this->override_message ??
|
|
"Wikipedia does not have an article about " .
|
|
"<b><a href='https://en.wikipedia.org/wiki/Special:Search?search=" .
|
|
rawurlencode($normalized_name) . "'>" . htmlentities($normalized_name) . "</a></b>. " .
|
|
"Maybe you need to capitalise the surname?",
|
|
"person_name"
|
|
);
|
|
} else if ($type === ArticleType::Disambiguation) {
|
|
throw new InvalidValueException(
|
|
$this->override_message ??
|
|
"<b><a href='https://en.wikipedia.org/wiki/" . rawurlencode($normalized_name) . "'>" .
|
|
htmlentities($normalized_name) . "</a></b> refers to multiple articles. " .
|
|
"<a href='https://en.wikipedia.org/wiki/" . rawurlencode($normalized_name) . "'>Check Wikipedia</a> " .
|
|
"to see if your article is listed.",
|
|
"person_name"
|
|
);
|
|
} else if ($type === ArticleType::Other) {
|
|
throw new InvalidValueException(
|
|
$this->override_message ??
|
|
"The Wikipedia article about " .
|
|
"<b><a href='https://en.wikipedia.org/wiki/" . rawurlencode($normalized_name) . "'>" .
|
|
htmlentities($normalized_name) . "</a></b> is not about a real-world person.",
|
|
"person_name"
|
|
);
|
|
}
|
|
|
|
if ($status === null)
|
|
throw new IllegalStateError("Person page does not have a status.");
|
|
|
|
return [$normalized_name, $status];
|
|
}
|
|
}
|