Completely type hint template functions and switch to named arguments

This commit is contained in:
Alex Cabal 2025-03-04 16:08:55 -06:00
parent 6108b5e53d
commit 124e8343fc
125 changed files with 542 additions and 450 deletions

View file

@ -1,14 +1,9 @@
<?
/**
* @var ?Artwork $artwork
* @var Artwork $artwork
*/
if($artwork === null){
$artwork = new Artwork();
$artwork->Artist = new Artist();
}
$isEditForm = $isEditForm ?? false;
$isEditForm ??= false;
?>
<fieldset>
<legend>Artist details</legend>

View file

@ -4,33 +4,33 @@
*/
?>
<ol class="artwork-list">
<? foreach($artworks as $artwork){ ?>
<?
$class = '';
<? foreach($artworks as $artwork){ ?>
<?
$class = '';
if($artwork->EbookUrl !== null){
$class .= ' in-use';
}
if($artwork->EbookUrl !== null){
$class .= ' in-use';
}
switch($artwork->Status){
case Enums\ArtworkStatusType::Unverified:
$class .= ' unverified';
break;
switch($artwork->Status){
case Enums\ArtworkStatusType::Unverified:
$class .= ' unverified';
break;
case Enums\ArtworkStatusType::Declined:
$class .= ' declined';
break;
}
case Enums\ArtworkStatusType::Declined:
$class .= ' declined';
break;
}
$class = trim($class);
?>
<li<? if($class != ''){ ?> class="<?= $class ?>"<? } ?>>
<a href="<?= $artwork->Url ?>">
<picture>
<source srcset="<?= $artwork->Thumb2xUrl ?> 2x, <?= $artwork->ThumbUrl ?> 1x" type="image/jpg"/>
<img src="<?= $artwork->ThumbUrl ?>" alt="" property="schema:image"/>
</picture>
</a>
</li>
<? } ?>
$class = trim($class);
?>
<li<? if($class != ''){ ?> class="<?= $class ?>"<? } ?>>
<a href="<?= $artwork->Url ?>">
<picture>
<source srcset="<?= $artwork->Thumb2xUrl ?> 2x, <?= $artwork->ThumbUrl ?> 1x" type="image/jpg"/>
<img src="<?= $artwork->ThumbUrl ?>" alt="" property="schema:image"/>
</picture>
</a>
</li>
<? } ?>
</ol>

View file

@ -1,16 +0,0 @@
<?
/**
* @var Artwork $artwork
*/
?>
<?= ucfirst($artwork->Status->value) ?>
<? if($artwork->EbookUrl !== null){ ?>
in use by
<? if($artwork->Ebook !== null && $artwork->Ebook->Url !== null){ ?>
<i>
<a href="<?= $artwork->Ebook->Url ?>"><?= Formatter::EscapeHtml($artwork->Ebook->Title) ?></a>
</i><? if($artwork->Ebook->IsPlaceholder()){ ?>(unreleased)<? } ?>
<? }else{ ?>
<code><?= Formatter::EscapeHtml($artwork->EbookUrl) ?></code> (unreleased)
<? } ?>
<? } ?>

View file

@ -8,7 +8,7 @@
* @var array<Ebook> $entries
*/
$subtitle = $subtitle ?? null;
$subtitle ??= null;
// Note that the XSL stylesheet gets stripped during `se clean` when we generate the feed.
// `se clean` will also start adding empty namespaces everywhere if we include the stylesheet declaration first.
@ -28,6 +28,6 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
</author>
<link href="<?= SITE_URL ?>/ebooks/opensearch" rel="search" type="application/opensearchdescription+xml" />
<? foreach($entries as $entry){ ?>
<?= Template::AtomFeedEntry(['entry' => $entry]) ?>
<?= Template::AtomFeedEntry(entry: $entry) ?>
<? } ?>
</feed>

View file

@ -1,12 +1,17 @@
<?
use function Safe\preg_replace;
$collectionMembership = $collectionMembership ?? null;
/**
* @var ?CollectionMembership $collectionMembership
*/
$collection = $collectionMembership?->Collection;
$sequenceNumber = $collectionMembership?->SequenceNumber;
?>
<? if($sequenceNumber !== null){ ?>№ <?= number_format($sequenceNumber) ?> in the<? }else{ ?>Part of the<? } ?> <a href="<?= $collection->Url ?>" property="schema:isPartOf"><?= Formatter::EscapeHtml(preg_replace('/^The /ius', '', (string)$collection->Name)) ?></a>
<? if($collection->Type !== null){ ?>
<? if($collection !== null){ ?>
<? if($sequenceNumber !== null){ ?>№ <?= number_format($sequenceNumber) ?> in the<? }else{ ?>Part of the<? } ?> <a href="<?= $collection->Url ?>" property="schema:isPartOf"><?= Formatter::EscapeHtml(preg_replace('/^The /ius', '', $collection->Name)) ?></a>
<? } ?>
<? if($collection?->Type !== null){ ?>
<? if(substr_compare(mb_strtolower($collection->Name), mb_strtolower($collection->Type->value), -strlen(mb_strtolower($collection->Type->value))) !== 0){ ?><?= $collection->Type->value ?><? } ?>
<? }else{ ?>
collection

View file

@ -4,8 +4,8 @@ if(!DONATION_DRIVE_COUNTER_ENABLED || ($autoHide ?? (HttpInput::Bool(COOKIE, 'hi
return;
}
$autoHide = $autoHide ?? true;
$showDonateButton = $showDonateButton ?? true;
$autoHide ??= true;
$showDonateButton ??= true;
$current = 0;
if(NOW < DONATION_DRIVE_COUNTER_START || NOW > DONATION_DRIVE_COUNTER_END){

View file

@ -13,8 +13,8 @@ if(
return;
}
$autoHide = $autoHide ?? true;
$showDonateButton = $showDonateButton ?? true;
$autoHide ??= true;
$showDonateButton ??= true;
$deadline = $donationDrive->End->format('F j');
$timeLeft = NOW->diff($donationDrive->End);

View file

@ -3,7 +3,7 @@
* @var array<Ebook> $carousel
*/
$isMultiSize = $isMultiSize ?? false;
$isMultiSize ??= false;
?>
<? if(sizeof($carousel) > 0){ ?>
<ul class="ebook-carousel<? if($isMultiSize){ ?> multi-size<? } ?>">

View file

@ -1,11 +1,11 @@
<?
/**
* @var ?Collection $collection
* @var array<Ebook> $ebooks
* @var ?Collection $collection
*/
$view = $view ?? Enums\ViewType::Grid;
$collection = $collection ?? null;
$view ??= Enums\ViewType::Grid;
$collection ??= null;
?>
<ol class="ebooks-list<? if($view == Enums\ViewType::List){ ?> list<? }else{ ?> grid<? } ?>"<? if($collection !== null){ ?> typeof="schema:BookSeries" about="<?= $collection->Url ?>"<? } ?>>
<? if($collection !== null){ ?>

View file

@ -3,7 +3,7 @@
* @var Ebook $ebook
*/
$showPlaceholderMetadata = $showPlaceholderMetadata ?? false;
$showPlaceholderMetadata ??= false;
?>
<section id="metadata">
<h2>Metadata</h2>

View file

@ -1,7 +1,10 @@
<?
$ebook = $ebook ?? new Ebook();
$isEditForm = $isEditForm ?? false;
$showProjectForm = $showProjectForm ?? true;
/**
* @var Ebook $ebook
*/
$isEditForm ??= false;
$showProjectForm ??= true;
?>
<fieldset>
<legend>Contributors</legend>
@ -204,7 +207,7 @@ $showProjectForm = $showProjectForm ?? true;
/>
</label>
<fieldset class="project-form">
<?= Template::ProjectForm(['project' => $ebook->ProjectInProgress, 'areFieldsRequired' => false]) ?>
<?= Template::ProjectForm(project: $ebook->ProjectInProgress ?? new Project(), areFieldsRequired: false) ?>
</fieldset>
</fieldset>
<? } ?>

View file

@ -1,5 +1,5 @@
<?
$includeLinks = $includeLinks ?? true;
$includeLinks ??= true;
?>
<div class="footer<? if(!$includeLinks){ ?> no-links<? } ?>">
<? if($includeLinks){ ?>

View file

@ -1,7 +1,11 @@
<?
$preheader = $preheader ?? null;
$letterhead = $letterhead ?? false;
$hasAdminTable = $hasAdminTable ?? false;
/**
* @var ?string $preheader
*/
$preheader ??= null;
$hasLetterhead ??= false;
$hasAdminTable ??= false;
?><!DOCTYPE html>
<html lang="en">
<head>
@ -38,7 +42,7 @@ $hasAdminTable = $hasAdminTable ?? false;
-webkit-text-size-adjust: none;
}
<? if($letterhead){ ?>
<? if($hasLetterhead){ ?>
div.body.letterhead{
background-image: url("https://standardebooks.org/images/logo-email.png");
background-position: top 2em center;
@ -222,7 +226,7 @@ $hasAdminTable = $hasAdminTable ?? false;
color: #4f9d85;
}
<? if($letterhead){ ?>
<? if($hasLetterhead){ ?>
div.body.letterhead{
background-image: url("https://standardebooks.org/images/logo-email-dark.png");
}
@ -235,7 +239,7 @@ $hasAdminTable = $hasAdminTable ?? false;
</style>
</head>
<body>
<div class="body<? if($letterhead){ ?> letterhead<? } ?>">
<div class="body<? if($hasLetterhead){ ?> letterhead<? } ?>">
<? if($preheader){ ?>
<p class="preheader"><?= Formatter::EscapeHtml($preheader) ?><? for($i = 0; $i < 150 - strlen($preheader); $i++){ ?>&zwnj;&nbsp;<? } ?></p>
<? } ?>

View file

@ -4,9 +4,9 @@
* @var string $role
* @var User $user
*/
?><?= Template::EmailHeader(['hasAdminTable' => true, 'letterhead' => true]) ?>
?><?= Template::EmailHeader(hasAdminTable: true, hasLetterhead: true) ?>
<p>Youve been assigned a new ebook project to <strong><?= $role ?></strong>:</p>
<?= Template::ProjectDetailsTable(['project' => $project, 'useFullyQualifiedUrls' => true, 'showArtworkStatus' => false]) ?>
<?= Template::ProjectDetailsTable(project: $project, useFullyQualifiedUrls: true, showArtworkStatus: false) ?>
<p>If youre unable to <?= $role ?> this ebook project, <a href="mailto:<?= EDITOR_IN_CHIEF_EMAIL_ADDRESS ?>">email the Editor-in-Chief</a> and well reassign it.</p>
<ul>
<li>
@ -20,4 +20,4 @@
</p>
</li>
</ul>
<?= Template::EmailFooter(['includeLinks' => false]) ?>
<?= Template::EmailFooter(includeLinks: false) ?>

View file

@ -2,7 +2,7 @@
/**
* @var int $ebooksThisYear
*/
?><?= Template::EmailHeader(['letterhead' => true]) ?>
?><?= Template::EmailHeader(hasLetterhead: true) ?>
<p>Hello,</p>
<p>Last year, your generous donation to <a href="<?= SITE_URL ?>">Standard Ebooks</a> made it possible for us to continue producing beautiful ebook editions for free distribution.</p>
<p>It also allowed me to add you to our <a href="<?= SITE_URL ?>/about#patrons-circle">Patrons Circle</a>, a group of donors who are honored on our masthead, and who have a direct voice in the future of our <a href="<?= SITE_URL ?>/ebooks">ebook catalog</a>, for one year.</p>

View file

@ -1,4 +1,4 @@
<?= Template::EmailHeader(['letterhead' => true]) ?>
<?= Template::EmailHeader(hasLetterhead: true) ?>
<p>Hello,</p>
<p>I couldnt help but notice that your monthly donation to Standard Ebooks has recently ended. Your generous donation allowed me to add you to our <a href="<?= SITE_URL ?>/about#patrons-circle">Patrons Circle</a>, a group of donors who are honored on our masthead, and who have a direct voice in the future of our <a href="<?= SITE_URL ?>/ebooks">ebook catalog</a>.</p>
<p>Oftentimes credit cards will expire, or recurring charges will get accidentally canceled for any number of nebulous administrative reasons. If you didnt mean to cancel your recurring donation—and thus your Patrons Circle membership—nows a great time to <a href="<?= SITE_URL ?>/donate#patrons-circle">renew it</a>.</p>

View file

@ -2,18 +2,32 @@
use Safe\DateTimeImmutable;
use function Safe\filemtime;
$title = $title ?? '';
$highlight = $highlight ?? '';
$description = $description ?? '';
$manual = $manual ?? false;
/**
* @var ?string $title
* @var ?string $highlight
* @var ?string $description
* @var ?string $feedUrl
* @var ?string $feedTitle
* @var ?string $downloadUrl
* @var ?string $canonicalUrl
* @var ?string $coverUrl
*/
$title ??= null;
$highlight ??= null;
$description ??= null;
$feedUrl ??= null;
$feedTitle ??= null;
$downloadUrl ??= null;
$canonicalUrl ??= null;
$coverUrl ??= null;
$css ??= [];
$isManual ??= false;
$isXslt ??= false;
$isErrorPage ??= false;
$ogType ??= 'website';
$colorScheme = Enums\ColorSchemeType::tryFrom(HttpInput::Str(COOKIE, 'color-scheme') ?? Enums\ColorSchemeType::Auto->value);
$isXslt = $isXslt ?? false;
$feedUrl = $feedUrl ?? null;
$feedTitle = $feedTitle ?? '';
$isErrorPage = $isErrorPage ?? false;
$downloadUrl = $downloadUrl ?? null;
$canonicalUrl = $canonicalUrl ?? null;
$css = $css ?? [];
$showPublicDomainDayBanner = PD_NOW > new DateTimeImmutable('January 1, 8:00 AM', SITE_TZ) && PD_NOW < new DateTimeImmutable('January 14', LATEST_CONTINENTAL_US_TZ) && !(HttpInput::Bool(COOKIE, 'hide-public-domain-day-banner') ?? false);
// As of Sep. 2022, all versions of Safari have a bug where if the page is served as XHTML, then `<picture>` elements download all `<source>`s instead of the first supported match.
@ -41,8 +55,8 @@ if(!$isXslt){
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head prefix="twitter: https://twitter.com/ schema: http://schema.org/"><? /* The `og` RDFa prefix is part of the RDFa spec */ ?>
<meta charset="utf-8"/>
<title><? if($title != ''){ ?><?= Formatter::EscapeHtml($title) ?> - <? } ?>Standard Ebooks: Free and liberated ebooks, carefully produced for the true book lover</title>
<? if($description != ''){ ?>
<title><? if($title !== null){ ?><?= Formatter::EscapeHtml($title) ?> - <? } ?>Standard Ebooks: Free and liberated ebooks, carefully produced for the true book lover</title>
<? if($description !== null){ ?>
<meta content="<?= Formatter::EscapeHtml($description) ?>" name="description"/>
<? } ?>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
@ -59,7 +73,7 @@ if(!$isXslt){
<link href="/css/dark.css?version=<?= filemtime(WEB_ROOT . '/css/dark.css') ?>" media="screen<? if($colorScheme == Enums\ColorSchemeType::Auto){ ?> and (prefers-color-scheme: dark)<? } ?>" rel="stylesheet" type="text/css"/>
<? } ?>
<? } ?>
<? if($manual){ ?>
<? if($isManual){ ?>
<link href="/css/manual.css?version=<?= filemtime(WEB_ROOT . '/css/manual.css') ?>" media="screen" rel="stylesheet" type="text/css"/>
<? if($colorScheme == Enums\ColorSchemeType::Auto || $colorScheme == Enums\ColorSchemeType::Dark){ ?>
<link href="/css/manual-dark.css?version=<?= filemtime(WEB_ROOT . '/css/manual-dark.css') ?>" media="screen<? if($colorScheme == Enums\ColorSchemeType::Auto){ ?> and (prefers-color-scheme: dark)<? } ?>" rel="stylesheet" type="text/css"/>
@ -68,7 +82,7 @@ if(!$isXslt){
<? foreach($css as $url){ ?>
<link href="<?= Formatter::EscapeHtml($url) ?>?version=<?= filemtime(WEB_ROOT . $url) ?>" media="screen" rel="stylesheet" type="text/css"/>
<? } ?>
<? if($canonicalUrl){ ?>
<? if($canonicalUrl !== null){ ?>
<link rel="canonical" href="<?= Formatter::EscapeHtml($canonicalUrl) ?>" />
<? } ?>
<link href="/apple-touch-icon-120x120.png" rel="apple-touch-icon" sizes="120x120"/>
@ -90,8 +104,8 @@ if(!$isXslt){
<link rel="search" href="/ebooks/opensearch" type="application/opensearchdescription+xml; charset=utf-8"/>
<? if(!$isErrorPage){ ?>
<meta content="#394451" name="theme-color"/>
<meta content="<? if($title != ''){ ?><?= Formatter::EscapeHtml($title) ?><? }else{ ?>Standard Ebooks<? } ?>" property="og:title"/>
<meta content="<?= $ogType ?? 'website' ?>" property="og:type"/>
<meta content="<? if($title !== null){ ?><?= Formatter::EscapeHtml($title) ?><? }else{ ?>Standard Ebooks<? } ?>" property="og:title"/>
<meta content="<?= $ogType ?>" property="og:type"/>
<meta content="<?= $pageUrl ?>" property="og:url"/>
<meta content="<?= SITE_URL . ($coverUrl ?? '/images/logo.png') ?>" property="og:image"/>
<meta content="summary_large_image" name="twitter:card"/>

View file

@ -3,7 +3,7 @@
* Notes:
*
* - *All* OPDS feeds must contain a `rel="http://opds-spec.org/crawlable"` link pointing to the `/feeds/opds/all` feed.
* - The `<fh:complete/>` element is required to note this as a "Complete Acquisition Feeds"; see <https://specs.opds.io/opds-1.2#25-complete-acquisition-feeds>.
* - The `<fh:complete/>` element is required to note this as a "Complete Acquisition Feed"; see <https://specs.opds.io/opds-1.2#25-complete-acquisition-feeds>.
*/
/**
@ -16,8 +16,8 @@
* @var array<Ebook> $entries
*/
$isCrawlable = $isCrawlable ?? false;
$subtitle = $subtitle ?? null;
$isCrawlable ??= false;
$subtitle ??= null;
// Note that the XSL stylesheet gets stripped during `se clean` when we generate the feed.
// `se clean` will also start adding empty namespaces everywhere if we include the stylesheet declaration first.
@ -45,6 +45,6 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
<uri><?= SITE_URL ?></uri>
</author>
<? foreach($entries as $entry){ ?>
<?= Template::OpdsAcquisitionEntry(['entry' => $entry]) ?>
<?= Template::OpdsAcquisitionEntry(entry: $entry) ?>
<? } ?>
</feed>

View file

@ -9,7 +9,7 @@
* @var array<OpdsNavigationEntry> $entries
*/
$subtitle = $subtitle ?? null;
$subtitle ??= null;
// Note that the XSL stylesheet gets stripped during `se clean` when we generate the feed.
// `se clean` will also start adding empty namespaces everywhere if we include the stylesheet declaration first.

View file

@ -1,13 +1,13 @@
<?
use Enums\HttpMethod;
/**
* @var Project $project
*/
use Enums\HttpMethod;
$useFullyQualifiedUrls = $useFullyQualifiedUrls ?? false;
$showTitle = $showTitle ?? true;
$showArtworkStatus = $showArtworkStatus ?? true;
$useFullyQualifiedUrls ??= false;
$showTitle ??= true;
$showArtworkStatus ??= true;
?>
<table class="admin-table">
<tbody>

View file

@ -1,10 +1,14 @@
<?
$project = $project ?? new Project();
/**
* @var Project $project
*/
$areFieldsRequired ??= true;
$isEditForm ??= false;
$managers = User::GetAllByCanManageProjects();
$reviewers = User::GetAllByCanReviewProjects();
$pastProducers = User::GetNamesByHasProducedProject();
$areFieldsRequired = $areFieldsRequired ?? true;
$isEditForm = $isEditForm ?? false;
?>
<? if(!$isEditForm){ ?>
<input type="hidden" name="project-ebook-id" value="<?= $project->EbookId ?? '' ?>" />

View file

@ -3,9 +3,9 @@
* @var array<Project> $projects
*/
$includeTitle = $includeTitle ?? true;
$includeStatus = $includeStatus ?? true;
$showEditButton = $showEditButton ?? false;
$includeTitle ??= true;
$includeStatus ??= true;
$showEditButton ??= false;
?>
<table class="data-table">
<caption aria-hidden="true">Scroll right </caption>

View file

@ -31,7 +31,7 @@ print("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
<width>144</width>
</image>
<? foreach($entries as $entry){ ?>
<?= Template::RssEntry(['entry' => $entry]) ?>
<?= Template::RssEntry(entry: $entry) ?>
<? } ?>
</channel>
</rss>

View file

@ -1,5 +1,6 @@
<?
/**
* @var string $query
* @var array<string> $tags
* @var Enums\EbookSortType $sort
* @var Enums\ViewType $view
@ -18,13 +19,13 @@ $isAllSelected = sizeof($tags) == 0 || in_array('all', $tags);
</select>
</label>
<label>Keywords
<input type="search" name="query" value="<?= Formatter::EscapeHtml($query ?? '') ?>"/>
<input type="search" name="query" value="<?= Formatter::EscapeHtml($query) ?>"/>
</label>
<label class="sort">
<span>Sort</span>
<span>
<select name="sort">
<? if(isset($query) && $query != ''){ ?>
<? if($query != ''){ ?>
<option value="<?= Enums\EbookSortType::Relevance->value ?>"<? if($sort == Enums\EbookSortType::Relevance){ ?> selected="selected"<? } ?>>Relevance</option>
<option value="<?= Enums\EbookSortType::Newest->value ?>"<? if($sort == Enums\EbookSortType::Newest){ ?> selected="selected"<? } ?>>S.E. release date (new &#x2192; old)</option>
<? }else{ ?>

View file

@ -1,8 +1,11 @@
<?
$user = $user ?? new User();
$isEditForm = $isEditForm ?? false;
$generateNewUuid = $generateNewUuid ?? false;
$passwordAction = $passwordAction ?? Enums\PasswordActionType::None;
/**
* @var User $user
* @var Enums\PasswordActionType $passwordAction;
* @var bool $generateNewUuid
*/
$isEditForm ??= false;
?>
<label class="email">

View file

@ -1,9 +1,8 @@
<?
/**
* @var array<Ebook> $ebooks
* @var bool $showPlaceholderMetadata
*/
$showPlaceholderMetadata = $showPlaceholderMetadata ?? false;
?>
<ul class="wanted-list">
<? foreach($ebooks as $ebook){ ?>
@ -15,7 +14,7 @@ $showPlaceholderMetadata = $showPlaceholderMetadata ?? false;
by <?= Formatter::EscapeHtml($ebook->AuthorsString) ?>. <?= $ebook->ContributorsHtml ?>
<? foreach($ebook->CollectionMemberships as $index => $collectionMembership){ ?>
<? if($index == 0){ ?><?= Template::CollectionDescriptor(['collectionMembership' => $collectionMembership]) ?><? }else{ ?><?= lcfirst(Template::CollectionDescriptor(['collectionMembership' => $collectionMembership])) ?><? } ?><? if($index < sizeof($ebook->CollectionMemberships) - 1){ ?>, <? } ?><? if($index == sizeof($ebook->CollectionMemberships) - 1){ ?>.<? } ?>
<? if($index == 0){ ?><?= Template::CollectionDescriptor(collectionMembership: $collectionMembership) ?><? }else{ ?><?= lcfirst(Template::CollectionDescriptor(collectionMembership: $collectionMembership)) ?><? } ?><? if($index < sizeof($ebook->CollectionMemberships) - 1){ ?>, <? } ?><? if($index == sizeof($ebook->CollectionMemberships) - 1){ ?>.<? } ?>
<? } ?>
<? if(isset($ebook->EbookPlaceholder->Notes)){ ?>